Які є конвенції в REST API та для чого їх дотримуватись

REST API — один з найпопулярніших підходів для проєктування API ваших сервісів. Ця технологія здається простою, але, на жаль, дуже часто розробники неправильно розуміють або застосовують її принципи. Тим більше, що REST (на відміну від того ж HTTP) не є стандартом. А там, де немає стандарту, не може бути однієї правильної відповіді/рішення на технічне питання/завдання. Але можуть бути конвенції та правила, які відображають сталi best practices.

Тому я вирішив написати статтю про конвенції, які зараз поширені, і про те, які наслідки можуть бути, якщо їх не дотримуватись. Конвенції цікаві тим, що можуть відрізнятися від компанії до компанії, тому ця стаття заснована на моєму практичному досвіді, включаючи деякі наші тренінги. Також я помістив цікаві приклади з API популярних соцмереж. Але мені було б також цікаво, якби читачі поділилися і своїм практичним досвідом.

З чого усе почалося

Отже, 2000 року відбулася подія, яка начебто не повинна була істотно вплинути на майбутнє ІТ і Web 2.0. Американський інженер Рой Філдінг захистив докторську дисертацію в університеті в Ірвіні, штат Каліфорнія. Саме ця дисертація під назвою Architectural Styles and the Design of Network-based Software Architectures привела до домінування REST API, який зараз масово використовують по всьому світу. Щоправда, це сталося не відразу, тільки в 2010-х роках, після десятиліття гегемонії SOAP, RMI і XML-RPC.

Чому Філдінг вирішив взяти саме цю тему? Річ у тому, що Філдінг був не простим інженером, а ще й комп’ютерним вченим і дослідником, одним з авторів протоколу HTTP і Apache HTTP вебсервера. Що ж таке REST? Це не протокол, не формат даних і не фреймворк, це архітектурний стиль, який вводить 5 обмежень або принципів (constraints) для комунікації в розподіленої архітектури.

  • Наявність клієнта і сервера.
  • Принцип stateless.
  • Єдині інтерфейси (Uniform interface) для ресурсів.
  • Layered-архітектура.
  • Можливості кешування відповіді сервера.

Відповідно, якщо ви створюєте сервіси в своєму застосунку і вони відповідають цим обмеженням, то ви маєте усі підстави назвати свої сервіси RESTful. Ви можете використовувати будь-який протокол, формат даних або фреймворк для розробки своїх сервісів. Але традиційно саме протокол HTTP і формат JSON найбільш часто використовуються завдяки своїй простоті та популярності в ІТ. Тоді ваші сервіси матимуть назву RESTful вебсервіси. Я навів ці обмеження, оскільки вони не раз знадобляться нам пізніше.

Операції з ресурсами

REST — це дуже велика тема, а в цій статті мені б хотілося торкнутися конвенції, пов’язаної з ресурсами і uniform interface для RESTful вебсервісів, і навести кілька практичних прикладів. Отже, будь-яка операція, ініційована клієнтом, пов’язана з деякою дією над тим ресурсом, який міститься на сервері. Якщо ваш проєкт — це інтернет-магазин, а ваші ресурси — книги, то це може бути отримання, зміна або видалення книг з магазину. Для того щоб уніфікувати операції з ресурсами, обмеження uniform interface вводить 4 умови:

  • У кожного ресурсу повинен бути унікальний ідентифікатор (URI), який клієнт вкаже в своєму запиті.
  • Сервер зберігає у себе інформацію про ресури, а повертає їх клієнту у вигляді представлень (representations). Причому внутрішній спосіб зберігання (СУБД, файлові системи) повинен бути незалежним від формату представлення (XML, JSON, HTML). По суті, тут REST API приховує (інкапсулює) внутрішнє представлення ресурсу. Представлення може містити не тільки інформацію про ресурс, а й додаткові метадані.
  • Запити від клієнта повинні бути самопояснювальними і містити всю необхідну інформацію, щоб сервер міг обробити запит.
  • Сервер повинен повертати клієнту не тільки запитану інформацію (про ресурс), а й всі доступні операції, пов’язані з нею, а також посилання на пов’язані ресурси. Цей принцип відомий як HATEOAS (Hypermedia as the engine of application state).

Uniform interface — це важливий момент в REST, оскільки він за визначенням є stateless, і сервер обробляє запит, тільки з огляду на його вміст (включаючи і URI). Як же правильно скласти URI для ваших запитів? Уявімо, що у вас є найпростіший інтернет-магазин, в якому є 4 ресурси:

  • Книги (books).
  • Замовлення (orders).
  • Автори (authors).
  • Користувачі (users).

Хочу ще раз нагадати, що REST є не стандартом, а архітектурним стилем, і розробники вільні реалізувати його як вважають за потрібне. Але RESTful вебсервіси використовують HTTP, а цей протокол вже є стандартом зі своєю специфікацією. Тому загальновживані конвенції потрібні для того, щоб правильно використовувати HTTP і щоб розробники щоразу не вигадували велосипед.

У програмуванні ми вже використовуємо кілька конвенцій, пов’язаних з найменуванням:

  • Назва класiв — іменники.
  • Назва інтерфейсiв — іменники або прикметники.
  • Назва методiв (функцiй) — дієслова.
  • Назва змінних — іменники або прикметники.

Ці конвенції випливають з тієї ролі, яку відіграють наші абстракції і моделі. Так метод виконує деяку дію, тому його назва буде дієсловом. А клас є узагальненням тих об’єктів, які ми використовуємо в проєкті і тому буде іменником.

В RESTful вебсервісах прийнята така конвенція:

  • Назва ресурсу — це іменник у множині
  • Дія над ресурсом — це HTTP-метод, який вказує клієнт (споживач нашого API).
  • GET — отримати ресурс.
  • POST — створити ресурс.
  • PUT — змінити ресурс повністю (оновити).
  • DELETE — видалити ресурс.
  • PATCH — частково змінити ресурс.

Цим REST API відрізняється від SOAP, де і операція, і вхідні аргументи (метадані) зберігаються в самому XML envelope body. Таким чином, для будь-якого REST-сервісу потрібно вибрати і правильний URI, і HTTP-метод. І саме метод визначає, яку операцію над ресурсом ми будемо виконувати. Здавалося б, усе просто, але розберімо кілька прикладів. Ті варіанти, які відповідають сформованим конвенціям, відзначені жирним шрифтом, невідповідні, спірні та альтернативні — курсивом.

Практика

1. Отже, вам потрібно отримати всі книги з магазину. У такому випадку клієнт повинен відправити запит на сервер:

GET /books

Я бачив і URI /allbooks, і /getAllBooks, і навіть суперечливий URI від GitHub виду /search/books. На мою думку, вони є некоректними, тому що або містять надлишкову інформацію, або вже містили дію (get, search), тоді як дію має вказувати клієнт, обираючи той чи інший HTTP-метод.

2. Тепер вам потрібно отримати книги англійською мовою. Чи можемо ми вибрати для нового сервісу URI /englishbooks або /books/english?

В принципі ніхто нам це не може заборонити, але з часом виникнуть нові запити і нові endpoints /availablebooks, /soldoutbooks тощо. У цьому разі English, available, sold out — це атрибути стану ресурсу (книги), і ми в нашому сервісі хочемо фільтрувати книги з тих чи інших атрибутів.

У REST API прийнята така конвенція. Для сортування, фільтрації та обмеження ресурсів використовуйте параметри запиту (query parameters). Тоді наші URI будуть виглядати так:

/books?type=available або /books?language=english

Такий підхід допоможе нам одним REST API виконати будь-яку операцію з пошуку ресурсів, змінюючи тільки параметри запитів.

3. Тепер вам потрібно повернути книгу з ідентифікатором 100. У минулому прикладі ми використовували параметри запиту для фільтрації. Чи можемо ми використовувати їх і в цьому випадку?

GET /books?id=100

Ні в REST, ні в HTTP ця ситуація прямо не описана і не стандартизована. Але тут потрібно згадати, що одна книга — це теж ресурс і частина більш глобального ресурсу книги. У REST API прийнято використовувати символ «/» для відображення ієрархічної залежності між ресурсами. Тому нам варто вибрати альтернативний варіант — параметри шляху (path variables). І в такому варіанті URI буде /books /100. У другого варіанта є й інші переваги:

  • Параметр шляху завжди є обов’язковим, тоді як параметр запиту — опціональним.
  • Якщо ви вирішите вказувати ідентифікатори через параметри запиту, то у вас буде багато способів так зробити, наприклад, /books?Id=100 або /books?book_id=100, а це може призвести до відсутності єдиного стандарту навіть в межах одного проєкту.
  • Згідно з HTTP-конвенцією, якщо ви вказуєте URI з параметрами запиту, за яким нічого не знайдено, то ви повинні просто повернути порожню відповідь з кодом 200. А ось якщо ви використовуєте параметри шляху і нічого не знайдено, тоді код 404.

Ви можете навести карколомне заперечення. А яка взагалі різниця, який варіант використовувати, якщо у нас є HATEOAS? При запиті всіх ресурсів (GET / books) сервіс поверне посилання для отримання інформації про індивідуальні ресурси:

[{

«id»: «123»,

«title»: «REST API»,

«year»: 2021,

«_links»: {

«self»: {

«href»: «shop.com/api/books/123»

}

}]

Це все правильно, HATEOAS і був придуманий, щоб REST-клієнт не залежав від внутрішньої логіки і реалізації сервера. Ба більше, якщо ми захочемо поміняти URI сервісу, не буде потрібно змін на клієнті. Але тут можуть виникнути дві складності:

  • У нас може не бути HATEOAS.
  • Нам може знадобитися окрема сторінка на операцію з ресурсом для стороннього сервісу або клієнта, де workflow HATEOAS не застосовують.

У будь-якому випадку в межах одного проєкту (компанії) повинен бути вироблений один стандарт (або набір конвенцій), щоб уніфікувати розробку і використання всіх сервісів. Тут краще використовувати наявні конвенції, які пропонує ІТ-індустрія.

4. Ідентифікатори не завжди передаються як параметри шляху. У разі BATCH-запиту, коли ми хочемо отримати відразу кілька ресурсів за їх ідентифікаторами, ми вже використовуємо параметри запиту:

GET /books?ids=1,2,3

5. Можливий варіант, коли у ресурсу є не тільки первинний ідентифікатор у вашому сервісі (цифровий, UUID), але і глобальний бізнес-ідентифікатор, наприклад ISBN у книг. І може бути так, що сторонні сервіси не знають первинний ідентифікатор книги, але знають її ISBN і хочуть отримувати інформацію про книги по ньому. Якою буде URI в цьому випадку?

Ми можемо передавати ISBN як параметр запиту, і багато хто так і робить: /books?isbn= 978-1-56619-909-4. Але ISBN — унікальний ідентифікатор ресурсу, тому логічніше буде передавати його як параметр шляху. Але ми не можемо використовувати URI /books/978-1-56619-909-4, оскільки його вже зарезервовано. Тому є ось такий витончений варіант:

GET /books/isbn/978-1-56619-909-4

6. У нашому інтернет-магазині потрібен REST-сервіс для отримання інформації про поточного (залогіненого) клієнта. REST або HTTP ніяк не роз’яснюють цю ситуацію. За аналогією з попередніми прикладами ви можете використовувати URI:

GET /users/user_login

Такий варіант існує, наприклад, у BitBucket. Але є більш популярний підхід, який використовують GitHub, StackExchange, Facebook і багато інших. Вони ввели спеціальний ресурс — авторизований користувач, і URI виглядає відповідно: /me, /self або навіть /users/me або /users/current. Це не суперечить REST, оскільки ви самі вибираєте назву для ваших ресурсів. А ідентифікатор користувача передається в заголовку запиту Authorization (або якимось іншим способом). До того ж це приватний API, який ніколи буде використаний стороннім сервісом або клієнтом.

Іноді трапляється варіант /user, і він мені здається неправильним, тому що назва ресурсу повинна бути у множині. А /users теж не підходить, тому що GET /users повинен повернути всіх користувачів.

Але тут є непомітний підступ. Одна з вимог до REST API — це можливість кешування результатів роботи сервісу (якщо сервіс ідемпотентний). Чи можемо ми зробити endpoint /me кешованим? У дефолтному варіанті як ключ кешу використовується URI і параметри запиту. Якщо у вас поточний користувач передається через заголовки запиту, то ви можете закешувати перше значення і помилково повертати його для всіх користувачів. Тому тут дуже важливо налаштувати конфігурацію кеша і включити заголовки запиту.

7. Тепер потрібно повернути замовлення користувача за його ідентифікатором. Якщо ви впевнені, що URI повинен бути /orders/REF9423402343, то тут потрібно згадати про те, що ресурси бувають трьох видів:

  • Незалежні — можуть існувати без прив’язки до інших ресурсів.
  • Залежні — існують тільки в контексті інших ресурсiв (як inner classes в Java).
  • Асоціативні — незалежні ресурси, на які можна посилатися з інших ресурсів.

Замовлення не можна створити само собою, для цього потрібні ресурси-контейнери (книга і користувач), тому це залежний ресурс. Для залежних ресурсів в URI прийнято вказувати всіх його «батьків». Якщо ми хочемо отримати всі замовлення по книзі з ідентифікатором 100, то URI буде не /orders?bookId=100, а /books/100/orders. Такий підхід зручний тим, що можна відразу отримати URI «батька» — /books/100. Іноді трапляється варіант виду /books/orders/100, але він неправильний.

Тут виникає цікаве питання. А який може бути максимальний рівень вкладеності такого посилання? Практично я не бачив більше трьох. Наприклад, якщо ми хочемо отримати замовлення за книгами певного автора, то URI буде виду /authors/2/books/100/orders.

8. Яким буде URI для додавання нової книги? Для створення ресурсу згідно з HTTP-специфікацією використовується HTTP-метод POST, і тоді запит буде мати такий вигляд:

POST /books

Іноді трапляються неправильні підходи:

  • PUT /books — PUT використовується тільки для зміни наявного ресурсу.
  • POST /create-book — дію зазначено в URI.
  • GET /create-book — неправильний HTTP-метод.

Як бути з операцією купівлі книги? Часом бувають неправильні варіанти типу POST /books/100/pay. Тут знову ж дію вказують в самому URI. Щоб вибрати правильний варіант, потрібно відповісти на питання. Купівля книжки — це операція з яким ресурсом? Зрозуміло, із замовленням (його створення). Тому правильний варіант буде:

POST /books/100/orders

Дивно, що іноді неправильні варіанти є у відомих проєктах. Наприклад, в Twitter API v 1.1 (поточної) для створення лайку використовують такий запит:

POST favorites/create

І тільки у версії 2.0 вони його привели до тями:

POST /2/users/{id}/likes

Некоректний URI і в API вiд Bitbucket для додавання користувача в групу:

/rest/api/1.0/admin/groups/add-user

9. Для зміни ресурсу згідно з HTTP-специфікацією використовують HTTP-метод PUT, причому ми обов’язково вказуємо ідентифікатор ресурсу (навіть якщо він є в тілі запиту):

PUT /books/100

Але бувають і більш складні приклади. Наприклад, у книги є прапорець activated — чи доступна вона для продажу. Відповідно нам потрібно мати сервіс(и) для установки і скидання цього прапорця. Тут можна використовувати і PUT, але тоді нам доведеться отримати книгу з сервера, змінити activated, пройти шлях серіалізації → відправки по мережі → десеріалізацію і потім зміну всього вмісту в базі. Це дуже неощадливий варіант.

Іноді є варіанти, які я б назвав workarounds:

  • POST /books/100/activate.
  • POST /books/100?changeStatus=activated.
  • POST /books/100?action=activate.

Це, по суті, милиці, тому що в HTTP-специфікації є спеціальний HTTP-метод — PATCH, який використовується для часткової зміни ресурсу. Є навіть специфікація JSON Patch (RFC 6902), яка вказує формат тіла запиту:

[

{ «op»: «replace», «path»: «/activated», «value»: true}

]

У параметрі op вказують операцію над тим полем, яке міститься в параметрі path. Це може бути додавання, видалення, зміна, копіювання, переміщення або тестування. А сам запит буде виглядати так:

PATCH /books/100

І все ж є проєкти, де люблять вибирати свій шлях самурая. Наприклад, в LinkedIn API для часткової зміни потрібно вказати в заголовку запиту X-Restli-Method значення PARTIAL_UPDATE, а сам запит буде виглядати як POST /books/100.

10. Що, як потрібно виконати операцію, яка начебто не пов’язана ні з яким ресурсом? Наприклад, користувач забув свій пароль на сайті, він вводить email у формі та очікує отримати на пошту лист з новим паролем (або з посиланням на скидання пароля). Чи можна використовувати запит виду:

POST /users/sendReminderEmail?email= [email protected]?

Тут потрібно знову поставити питання — до якого ресурсу належить цей запит? І відповідь дуже проста: лист-нагадування паролю і є цей ресурс, який створюється нашим запитом. Тут ми не знаємо ідентифікатор користувача, тому ним слугує його email. І правильний запит має такий вигляд:

POST /users/my_email@shop.com/reminder-email

11. Яким буде URI для копіювання (клонування) книги? Мало хто знає, що в специфікації HTTP є метод COPY. І в найбільш RESTful варіанті ми саме його і використовуємо:

COPY /books/100

Але на практиці більше застосовують іншу конвенцію з більш популярним методом POST-виду POST /books/100 або POST /books?source=100

Такий варіант існує в в SendGrid API.

Висновок

Ми з вами розібрали найбільш популярні операції з REST API. Як ви могли бачити з прикладів, дуже часто для кожної операції є кілька альтернативних варіантів реалізації. Але я закликаю всіх використовувати сформовані конвенції, які випливають з REST constraints, HTTP-специфікації та сформованих best practices.

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

👍ПодобаєтьсяСподобалось59
До обраногоВ обраному51
LinkedIn

Найкращі коментарі пропустити

Щодо REST є три стадії:
— вивчення технології і загальноприйнятих стандартів
— хейт стандартів з юнацьким запалом
— розуміння необхідності дотримання принципів стандартизації

Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Сьогодні на інтервʼю попросили задизайнити RESTFUL API та основні сутності трансферу грошей з одного акаунту на інший.
Такі задачки для мене є дуже абстрактними, тому що в реальності функціонал завжди змінюється і доповнюється в майбутньому і писати API і імплементацію під ці абстрактні критерії не дивлячись у майбутнє системи доволі тупо.

Чи «оверсинкати» дизайн наперед в умовах інтервʼю це філософське питання :). Я проти підтримувати кашу з скажімо JSON обʼєктів в request body, яка навіть версійності не буде мати. Але це і був підхід інтерв’ювера. Його підхід це суто операції над /transfers і він вважає, що це RESTFUL, хоч може мені і справді слід перечитати принципи RESTful API . 

Коли мова йде за трансфер, це робота-операція як мінімум з двома ресурсами. Якщо розглядати операцію створення скажімо користувачів в RESTFUL API,- це правильно мати один ендпойнт /users для управління конкретним ресурсом засобами CRUD операціями за допомогою HTTP.
Я ж вважав і поки вважаю, що в його випадку використовувати POST /transfers для перерахунку з одного акаунти на інший це дія — ACTION, а не робота з ресурсами, а так не повинно бути в RESTFUL. В даному випадку це буде схоже на SOAP чи GraphQL, де REST maturity level 0 або 1

Тому я бачив даний дизайн наступним чином: /api/v1
/transfers/alias-sender-account/alias-recipient-account
Де — alias-sender-account та alias-recipient-account це синтетичні ID створені для безпеки, які мапляться на бекенді в реальні ID.


і це вже насамперед робота з ресурсами, 
GET /transfers — поверне всі трансфери всіх акаунтів для поточного користувача 
GET /transfers/alias-sender-account — поверне всі трансфери для конкретного акаунту на інші акаунти для поточного користувача
GET /transfers/alias-sender-account/alias-recipient-account — поверне всі трансфери для конкретного акаунту на інший конкретний акаунт для поточного користувача

Під «для поточного користувача» може бути любий користувач системи, якщо операцію виконує admin account.

Ще думав навіть над таким API: /api/v1
/transfers/moneys/alias-sender-account/alias-recipient-account/coins


Я бачу так, а як бачите RESTFUL API для transfers ви?

GET /transfers — поверне всі трансфери всіх акаунтів для поточного користувача

Нема ж поточного користовача, rest є стейтлес, треба весь контекст надсилати (хоча токен авторизації то вже і буде контекст, але імхо все одно краще в урлі, там же люблять казати про http семантику для кешу і інші плюшки).

Взагалі якийсь культ з цим ідеологічно правильним rest’ом, більше часу на суперечки йде. Тому мені різні rpc більш подобаються.

/api/v1/transfer , а все інше в post-параметрах щоб зробити переказ мені норм

Задача стояла саме про restful api дизайн. Мені особисто підхід rpc і всі параметри в тілі реквесту не подобається, — ускладнює трейсінг, дебаг, підтримку, читаємість.
Щодо відсутності токена авторизації і скоупу юзера погоджуюся.
Але без IdP (Customer Identity and Access) системи не уявляю розробки в наш час. А вона вже форсить те, як будуть скопи юзерів передаватися і використовуватися

Тільки на фронтенді це легше, через них ми і почали запихати все підряд в body, бо фронтенщики ліниві :). На бекенді тільки замапити приходиться path, query і інші параметри

Як на мене операції з замовленнями краще всетаки робити /orders
До прикладу, у вас вказано купівля книги:

POST /books/100/orders

А якщо ти купуєш декілька книг?
Я б робив вже краще POST /orders/books
і в тілі запиту вже передавав айді книг із замовлення

Посони усе покуй, якщо ваша фіча\тіма не приносить грошей то лей офф і досвідос...усі ваші срачі про «крисиві» архитектури та коди http до дупи. Привіт із 24-го і велком ту зе реаліті.

усі ваші срачі про «крисиві» архитектури та коди http до дупи. Привіт із 24-го і велком ту зе реаліті.

проходжу саме якраз просто цікавенний кейз «за перехід на стандарт сі++11» (так саме так 11-го року як 13 років тому після якого вже є сі++14 та сі++17 та сі++20 навіть є) тож там якраз «за крисиві архитектури» вже буквально з якого я дивуюся але мовчу «ми будемо застосуємо фічу (мови) бо це крута фіча» яка «фіча» майже буквально саме функціональна на стільки що я її вже застосував «у одному місці» )) тільки вирішивши це «натівними імплементаціями платформ» замість «стандарту мови»

бо мені саме треба було от буквально треба саме як чиста функція

ЗЫ: до речі як на мене з точки зору саме чистої функції rest уже відходить бо (як на мене) його основна цінність була у прямій базі на «чистий» http який є майже скрізь (чи буквально скрізь) але при цьому з інших компонентів може бути ну там хіба що json

... але зараз коли комунікаційні протоколи апішного рівня вже існують певна кількість і то «майже стандарт» і то вже з наявними імплементаціями під більшість популярних платформ...

POST /users/[email protected]/reminder-email

Як на мене
POST /users/reminder-email
{
«email»: «[email protected]»
}
може бути більш кращим варіантом, через:
1. Дуже часто URL можуть бути видні на марштуризаторах (якщо не HTTPS), тому краще не включати приватну інформацію в них
2. Дуже часто URL можуть логуватися і бути видними в системах агрегації логів на стороні shop.com
3. В більшості випадків треба ставити капчу на таку сторінку, а значить в реквест додається ще поля
4. URL деколи треба енкодити і вони мають обмеження по довжині

Абсолютно згіден. Якийсь дуже поганий приклад від автора. Якщо вже передавати якісь приватні дані в url, то хоча б шифруйте якось, інакше.

нема сенсу знову ж таки просто юзайте https «всьо уже украдєно до нас» (к) (тм)

Тю, скрін зробили і все)

Дуже часто URL можуть бути видні на марштуризаторах (якщо не HTTPS)

з одного боку це справді доречне зауваження з іншого боку вже сьогодні а точніше вже навіть учора вся комунікація яка так чи інакше «іде назовні» вже ведеться через https бо саме _має_ вестися виключно через https

... тож фактино на сьогодні це питання має малий сенс

ЗЫ: до того ж не захищений протокол означає відкритість усієї комунікації взагалі а там у вас в середині вже самих даних можуть бути і частіше є вже на стільки sensitive data що за умови «не довіреної мережі» це питання вже не має сенсу

URL можуть бути видні на марштуризаторах

в принципі я мабуть ще уточню що я тоді певно не зовсім зрозумів це бо якщо можна побачити URL як то GET то тоді точно можна побачити все бо воно ж там же ж у тому самому текстовому блоці і йде просто відкритим текстом і так само просто відкритим текстом і проходить через будь який «маршрутизатор»

В більшості випадків треба ставити капчу на таку сторінку

а ви точно про API? ))

Якщо не HTTPs то дійсно видно все, там вже нічного не допоможе проти програм які спеціально перехоплюють трафік. Все-таки комунікація між сервісами мікросервісної архітектури, дуже часто не будується на основі HTTPs.
Я все одно вважаю, що імовірність того що URL буде засвічений навіть в інфраструктурі shop.com велика, і це буде показано розробникам та команді підтримки коли це не потрібно.

Можливо щось змінилося, але в капчі від гугла десь 2-3 версії, після її проходження надається id результатів(довга рандомна стрінга). Коли ці дані потрапляють на бекенд, то через server-to-server коммунікацію перевіряється, що вона була пройдено успішно. Тому так, капча впливає на виклик бекенду

Є ще питання про локалізацію =)
Припустимо є статті. І я хочу, щоб у кожної статті була своя локалізація. Для цього в базі даних є два ключових поля: Id та Language.
Як це буде мовою rest?
GET api/articles?language="uk" — отримати статті з фільтром за мовою.
GET api/articles/{id} — отримати усі локалізації якоїсь статті.
GET api/articles/{id}/{language} — отримати локалізовану статтю.
POST api/articles — додати статтю. У тілі передаємо потрібні поля та мову статті.
POST api/articles/{id} — додати локалізацію для існуючої статті. У тілі передаємо потрібні поля та мову.
PUT api/articles/{id}/{language} — редагувати локалізовану статтю.
DELETE api/articles/{id} — видалити статтю з усіма локалізаціями.
DELETE api/articles/{id}/{language} — видалити локалізацію для статті.
Чи передавати мову таким чином не прийнято, бо у нас є заголовок Accept-Language і треба користутись ним? Як взагалі правильно?
Також буду вдячний за поради з локалізації бази даних. Мені здалось, що row localization це оптимальний варіант, бо не хочеться робити надлишкову нормалізацію, додаючи ще три таблиці.

Раджу використовувати ті можливості, які закладені в протокол HTTP: заголовки Accept-Language та Content-Language.

GET api/articles/{id}/{language} — отримати локалізовану статтю.

Такий підхід суперечить концепції REST API, тому що стаття від перекладу не змінюється. Це та сама стаття, просто іншою мовою. Тобто переклади статей — це не ресурси, це уявлення.
Але в браузері, в адресному рядку можна використовувати таку URL-адресу, тому що іноді користувачеві потрібно отримати переклад певною мовою незалежно від налаштувань і локалізації його браузера.

Також буду вдячний за поради з локалізації бази даних. Мені здалось, що row localization це оптимальний варіант, бо не хочеться робити надлишкову нормалізацію, додаючи ще три таблиці.

Найпростіший варіант — для кожної мови додати стовпець у таблиці перекладів.
Якщо NoSQL — то зберігати переклади як масив.

Цей варіант мені не дуже сподобався, бо при додаванні нової мови доведеться робити міграцію. Також доведеться модифікувати запити до бази даних.

Тоді можна створити окрему таблицю Translations, в якій будуть стовпці id статті, id мови (локаль) і текст перекладу.

Саме так роблю я, зазвичай

Language field виглядає як фільтр, можна його в query params передавати. Так само і з delete.

Добрий день, Сергію.
У мене знову питання =)
Ми використовуємо soft delete. Тобто, не видаляємо запис із бази даних, а лише ставимо йому IsDeleted=true.
Тобто, для «видалення» у нас є запит DELETE books/{id}, який не видалить запис повністю, а лише увімкне параметр IsDeleted.
І тут питання. Якщо хтось зробить виклик GET books/{id} для видаленого запису, який код повертати? 400, 404 чи 410? Зараз я повертаю 404 з текстом, що запис було видалено. Тут можна сказати, що 404 не підходить, бо запис насправді знайдено, але він видалений. 410 для цього взагалі найкраще підходить, але я не можу його використовувати, бо у нас внутрішні сервіси використовують grpc, а grpc не має статусу-аналога 410. Коротше кажучи, для використання 410 потрібно буде гратись з middleware та interceptors. Також 410 здається використовується, якщо видалили перманентно і тоді це не дуже підходить для soft delete. 400 мені не подобається, бо в даному випадку з запитом усе гаразд.
Що ви можете порадити?

Так рест апі і то що в базі то різні речі.
Для користувача апі ресурс просто не існує, тому 404.

У чому відмінність для користувача, якщо книга видалена чи відсутня?
Якщо вона видалена (і я так розумію, назавжди), то він отримає 404 і сервер буде правий, тому що такої книги немає.

Ще питання.
От є у нас метод, що повертає перелік книжок.
GET books.
Я зазвичай повертаю перелік об’єктів. Хоча бачив, як хтось створює клас з переліком.
Тобто, [item1, item2] vs {Items: [item1, item2]}. Просто масив з елементами чи масив з елементами у класі?

У REST API те, що сервер повертає клієнту називається уявленням (representation).
Структура і формат даних в ньому є внутрішньою справою вашого додатку і тут немає якихось обмежень.

Удобнее всего любые данные заворачивать в объект, даже если это просто массив, или единичное булево значение.

А в каких случаях это удобнее? Мне, например, не очень удобно создавать лишний класс ради одного свойства, значение которого я могу передать напрямую.

Если есть увязка с gRPC. Дело в том, что API для других сервисов лучше предоставлять не по REST, а по gRPC, хотябы потому что меньше оверхеда. REST нужен разве что для фронтенда. И так, если есть gRPC, там же в proto-файле можно объявить REST-API. В этом случае, gRPC-хендлеры хотят структуру с входными данными (формируется за счёт URL и body), и структуру с результатом. В http-response будет соответственно писаться структура. Ещё по proto-файлам можно генерировать swagger-файл с описанием, и таким образом, всё становится унифицировано.

З технічної точки зору обидва підходи будуть працювати.
Але, коли повертаєш список об’єктів завжди треба думати наперед про пагінацію (розбиття відповіді на сторінки)
Другий варіант дозволяє додати пагінацію у backward-compatible підхід. Наприклад тут є приклад відповіді з пагінацією від Spring Boot www.wimdeblauwe.com/...​ation-with-spring-boot-2
В теорії backward-compatible підхід можна зробити і з [item1, item2] виглядом, якщо будувати логіку на додаткових хедерах, але це ускладнить міграцію.

Також другий варіант в теорії дозволяє додавати нові поля у відповідь, але важко придумати коли це може бути необхідним. Наприклад

GET /myself/assets
{
«creditcards»: { .. }
}

А в наступних версія можуть додатися і banks. Але тут вже з пагінацією проблеми можуть виникнути

Є питання.
Наприклад, є користувачі. У користувачів є права.
Тобто, логічно припустити, що отримання та додавання прав для користувача потрібно зробити так:
GET users/{userId}/rights
POST users/{userId}/rights
А для зміни та видалення робити так?
PUT users/{userId}/rights/{id}
DETETE users/{userId}/rights/{id}
Якщо так, то зручно це буде? Бо нам потрібно передавати ідентифікатор користувача і ідентифікатор права. А для зміни та видалення достатньо ідентифікатора права. Чи зробити інший контроллер? Щоб було так:
PUT rights/{id}
DETETE rights/{id}
Але тоді мені не подобається, що для прав функціонал буде в різних контролерах. Як правильно?

PUT rights/{id}

У принципі, можна зробити і так. Код все стерпить.
А тепер уявіть, що ви хочете отримати всі права певного користувача.
І тоді ви згадаєте, що права — це залежна (від користувача) сутність, тому в цьому випадку запит має бути:
GET /users/{userId}/rights
І тут у вас почнеться плутанина — для різних операцій із правами різні конвенції та шляхи. Бажаєте підтримувати такий зоопарк?

Зрозуміло. Дякую. Є ще питання. Припустимо, я обрав перший шлях. А потім виникає потреба отримати всі права для всіх користувачів, тобто, тупо з таблиці всі дані дістати незалежно від користувача. Як це зробити? Все одно доведеться робити GET rights?

А який use-case? Я розумію — отримати всіх користувачів, а навіщо всі права всіх користувачів?
У будь-якому випадку можна спробувати щось на кшталт GET /users/rights

Права користувачів я навів у якості прикладу. Насправді я працюю над іншою темою, але структура така сама. Фронт-енд та бізнес-аналітики захотіли це =)
Я теж замислювався над GET users/rights, мабуть так і зроблю. Сподіваюсь, інші розробники не будуть проти.
А то я якось в попередній компанії зробив усе за стандартами, а мені інший розробник каже:
«Що це ти наробив? Де звичні GET users/GetUser?id={id} та POST users/CreateUser?». І сказав, що мабуть переробить =)
А коли він побачив PUT та DELETE, то взагалі був здивований. У нас в бібліотеці для клієнтських додатків навіть не було методів для PUT та DELETE, мені довелось їх додати. =)

Для видалення одного запису ми робимо так?
DELETE books/{id}
І якщо ми хочемо видалити декілька записів одразу, то як правильно зробити такий метод?
А ще питання, що зазвичай повертають від вервера при видаленні (кількість видалених записів, або сам запис)?

І якщо ми хочемо видалити декілька записів одразу, то як правильно зробити такий метод?

Тут немає однозначної відповіді. Наприклад, так DELETE /books?ids=1,2,3

А ще питання, що зазвичай повертають від вервера при видаленні (кількість видалених записів, або сам запис)?

Зазвичай повертають результат:
204 — no content
404 — дані не знайдені

Краще 204.
Коди 400-499 призначені для відповідей що передбачають відмову у обслуговуванні згідно внутрішньої логіки, 500-599 про помилку сервера при обробці запиту.
Коди 200-299 які передбачають сигналізування про успішне виконання операції.

Універсальних гайдлайнів не може існувати і REST наче не претендує на такі лаври.
Чи я помиляюся?

Абсолютно вірно. REST API — це архітектурний стиль, а навіть не патерн. Але в цій статті розглядалися конвенції для RESTful веб-сервісів, що базуються на HTTP. А HTTP — це вже протокол.

Як краще намалювати сервіс з отримання кількості книг?
GET books/count — так?

Цiкаве питання. Цей момент не відображений ні в REST API, ні в специфікації HTTP. Найчастіше кількість ресурсів потрібна в тому випадку, якщо ви використовуєте посторінкове виведення (server-side pagi-nation). І це значення надсилається клієнту як заголовок відповіді, наприклад X-TOTAL-COUNT. Щоправда, такі заголовки користувача з префіксом X- є deprecated(www.rfc-editor.org/rfc/rfc6648). Тому можна використовувати стандартний заголовок Content-Range. Як має виглядати URI? Найпростіше оголосити кількість книг під-ресурсом:
GET /books/count
Щоправда, це не зовсім коректно, оскільки кількість — скалярна величина і не може бути ресурсом. Тому найкраще використовувати інші варіанти. Наприклад, ввести параметр, який керуватиме тим, чи повертаються самі ресурси чи ні:
GET /books?includeItems=false
Або використовувати стандартний заголовок запиту Accept:
Accept: application/json;type=count

Дякую за відповідь.
Поки що залишу у вигляді GET /books/count, це хоч і не дуже гарно, але просто як дуб =)
З приводу сторінок.
Як я думав зробити.
Наприклад, маємо GET /books/count (або у будь-якому іншому вигляді), що повертає загальну кількість книг.
Також маємо GET /books?skip=0&take=100 (бачив часто limit/offset, але мені на слух більш звично skip/take; алеможна зауважити, що параметри краще мати іменники, а не дієслова; також для мене звично спочатку пропустити, а потім взяти (offset/limit vs limit/offset); чи це взагалі не те, за що потрібно перейматись?).
І клієнт викликає перший метод, щоб дізнатись повну кількість і сформувати сторінки, а потім другий метод, де передає параметри сторінки, і отримує перелік.
Це стандартний підхід?
Чи повинен я окрім переліку книг також повернути параметри сторінки чи в цьому немає сенсу, тому що клієнт і так знає, які параметри він передав? (Насправді мені просто лінь робити ще один клас-обгортку =) ).

У такому разі навіщо вам спочатку дізнаватися загальну кількість книг?
Це ж у чистому вигляді server-side pagination. Ви надсилаєте запит на отримання першої сторінки:
GET /books?page=0&count=20
і вам, крім самих книг, повертається і загальна кількість (як заголовок відповіді).
А це дозволяє вашому клієнтському додатку визначити, скільки всього буде сторінок і так далі.

зазвичай, на практиці, просто розширють, усякими аргументами
mode, include, exclude, fields, only, format, ..., ....

та й сама відповідь складається з двох секцій
meta та дата
у meta й запихується все що не виглядає типовими обїектом або колекцієй обїектів системи

REST що надається в таких статтях для «новачків» — непрактичний. та ще й частенько призводить до купи зайвих запитів.

класика
є міста, є магазини, є люди

яким буде запит
видати магазини того міста де живе людина?

в одному проекті був би таким:
GET /stores?filter=user_id:[nnn]&by=cities

іншими словами, на практиці мова запитів базується на тих ідеях, на яких виник GraphQL
Але й зараз, GraphQL буває забагато, і простіше просто розширювати запити аргументами.
Які вийдуть — спеціалізованними до домену проекту, тобто — простими, «наочними»

нижче була порада

як заголовок відповіді, наприклад X-TOTAL-COUNT

вважаю шкідливою, тому що
1. клієнтом може бути не браузер
2. HTTP — це транспортний рівень, а не домену

Сказочка на ночь. Жил был Дима Б. И купил он себе жигули. Нравилось ему его жигули модели рест 2101. Поскольку много там всяких разных деталек под капотом было. Всяких железячек, вспомогательных агрегатов, вообщем есть чем занятся. Всегда руки в масле, всегда есть что подкрутить, поменять маслицо каждые 5 тысяч, слить водичку с радиатора, подкрутить трамблер, подпалить сцепление. Однажды в то село пожаловал некто на электромобиле. И сказал, что же ты Дима Б. ездишь на жигули, вот посмотри, в электро мотор встроен в колесо, всего две педали и разгон до сотни за 4 секунды. Нет говорит Дима Б. Разве знаешь ты как заваривать выхлопную трубу ? Разве знаешь как завести рест 2101 с толкача ? Где же у тебя коробка передач еще одна педаль сцепление ? Это ненастоящая машина. Уходи, говорит.
"надутощекий и не слегка туповатый псевдоэксперт толкающей дичь, бредовые фантазии и просто вранье"©. И поехал Дима Б. дальше на своем пердящем Жигули, ведь не знал Дима Б. что электромобили появились еще раньше жигулей, но обстоятельства так сложились, что жигулей по дорогам ездит намного больше чем тесл.

То что PATCH ооооочень нравится Frontend devs это понятно, а хоть кто то реализовывал PATCH в реальных проектах?

(Возьмем JAVA)
Вот во многих статьях забывают что используя объекты мы можем иметь по сути 2 состояния.
— Значение (или дефолт значение)
— Null

Но вот в чем проблема. Как нам понять что человек передал нам null или просто не передал нам поля и он не ожидает что поле очистится.
{  "id": 1,  "address": null  }
==
{  "id": 1 }

Т. Е. у нас на выходе и так и так будет объект с address null и потом мы никак не определим что именно мы получили.
Лучшее что находил это вот эту статью (во многих других как будто все игнорируют эту проблему...)
nullbeans.com/...​ing-a-rest-api-in-spring

(Приходила идея получить заветное 3 состояние с помощью Optional, но вроде наркомания немного)

Это кстате к вопросу GraphQL, где ты можешь указать какие именно поля ты апдейтишь в запросе.
Но в Рест скорей всего придется лепить еще пару методов, которые будут отвечать только за апдейт адресса.

ПС: У нас тут есть специалист-внедренец-консультант Дима Бугай который внедряет рест фул апи от эмбеддед до самолетов пароходов грузовиков, поинтересуйтся у него как сделать метод патч.

А можно его как то тегнуть ? или как я его могу найти ?

Но вот в чем проблема.

Во-первых, да, такая проблема существует.
Но при ее формулировке важно понимать, почему она существует.

Ее причина не в ресте как таковом, который просто сумма рекмоендаций по тому, что приложение должно предоставлять интерфейс к своему состоянию.

Ее причина — в том, как исторически сложился общепринятый стандарт реализации реста в джаве. То есть, еще раз, это проблема не реста — это проблема джавы.

Возьмем твой же пример.

{ «id»: 1, «address»: null }
==
{ «id»: 1 }

Очевидно, что это два разных джсона. И некто, кто не в курсе, как это реализуется внутри джавы, не увидит проблемы, ведь пользователь явно передает «address»: null как новую версию состояния.

Проблема в том, что данный жсон мы привыкли обрабатывать связкой Jackson+POJO, которая не может корректно обработать эту проблемную разницу в жсонах. Тут надо упомянуть, что POJO в принципе достаточно хреновый стандарт.

Теоретические альтернативы есть. Например, мы можем не использовать POJO-модели с предопределенной структурой, и это тут же уберет проблему дефолтного нулла в поле класса, использовать что-то типа гугловского простенького JSONObject, тогда в нем вполне явно будет присутствовать «address»: null, т.к. класс POJO отсутсвует во взаимодействии в принципе. Проблема тут в том, что большинством, привыкших к чему-то типа

@GetMapping
.....(@Body MyDto myDto) {
этот подход будет воспринят враждебно, а проблему еще надо им поднести и продемонстрировать. А также в том, что фреймворки могут иметь проблемы с такой штукой. Когда-то я такое делал, внутри джексона или внутри спринга есть какой-то класс сырого жсона, который можно принять вместо POJO в контроллере.

Есть еще одна альтернатива. Рест не требует приема состояния в таком же формате, в каком оно хранится, или отдается. Т.е. ты на самом деле свободен в выборе формата передачи данных о редактировании фрагмента состояния. Никто не мешает тебе сделать что-то типа:

PATCH /app/....../{id}
{
    "fieldA": "newValue",
    "fieldB": "otherValue",
    "empty": [
          "fieldC",
          "fieldD"
    ]
}

И представить это классом, наследующим класс модели, с добавлением поля List empty:

class MyObjectPatch extends MyObjectModel implements Json {
     List<String> empty;
}
и далее
class MyObjectModel {
     ....

     public void apply(MyObjectPatch patch) {
           for (String field : patch.empty) {
                this.setNull(field); 
           }
     }
Я не говорю, что два подхода идеальны, просто они возможны, и ведут к достаточно осознанному, контролируемому коду без магии, который легко читать, менять и оптимизировать.
Вот во многих статьях забывают

"Авторами многих статьей" выступают профессиональные «написатели статей» без практического опыта. Они либо не владеют глубокой проблематикой, либо не знают путей решения, и поэтому боятся затрагивать острые темы, чтобы не вызвать срача или насмешек.

Вотето я понимаю подход. Чтобы проапдейтить поле в джисоне плюс два фреймворка, иерархия наследования и новый сериализатор в проекте.
Диссертацию не пробовал написать на эту тему ?

плюс два фреймворка, иерархия наследования и новый сериализатор

Иди в раша сми, озолотишься. С таким уровнем отрыва от реальность и скиллом нагло и безостановочно нести чушь о том, чего нет, составишь конкуренцию киселеву и скабеевой.

Эх Дима Дима. Внимай гласс мудрости.
Простые вещи должны делаться просто, а сложные чуть чуть сложнее ©
В данном случае мы имеем кривой инструмент умноженный на кривые руки джависта.

Неа )
в данном случае мы имеем беспричинно надутощекого и не слегка туповатого псевдоэксперта в лице тебя, в каждом посте толкающего дичь, бредовые фантазии и просто вранье.

слегка туповатого

"Я всегда буду искать ленивого человека — он найдёт лёгкий путь решить
задачу"© Билл Гейтс

Проблема в том Дмитрий, что ты не ленивый говнокодер. Готов засрать весь код проекта хитроумными решениями и рест апи копипастой.

Поэтому я бы предпочел чтобы ты был более туповат и банально ленился нагребать эти горы муссора в свой проект.

Божечки... хорошо, что я не сижу на Джаве :)

Данная проблема описана в этом фильетоне.
dou.ua/...​rums/topic/34550/#2223073

К сожалению мы уже ничего не можем поделать с этим поколением. Нет более преданного покупателя автоваза, чем рабочий в робе, который научился крутить болты под своим корытом в поле.

Все что мы можем сделать, это обучить новое поколение. А автовазоацев оставить поддерживать ржавое легаси

что я не сижу на Джаве
JavaScript / NodeJS Developer

Нашел чему радоваться.

Как у вас решили данную проблему ?

Как у вас решили данную проблему ?

Ну так у нас есть, как минимум, undefined чтобы точно понять передавали ли поле, или нет.
PATCH только для изменения значения полей, но не удаления самих полей. В качестве экспериментов- поля операторы аля `fieldName$operator`: optionalValue. Так что удалить поле нечто вроде { «address$drop»: null } парсер чего на JS пишется за 5 минут. Главное, не увлекаться чтобы монгу не получить.
Вот только на практике удалять поля как бы и незачем. Зачем нам удалять address? Есть ресурс, есть его схема, зачем удалять от туда что то, вместо сброса в null? Очень ли важно различать отсутствие поля в модели от его наличия с null?
Да и если что в DELETE как бы не запрещено body передавать.

Да и если что в DELETE как бы не запрещено body передавать.

Рассматривался вопрос когда одним запросом нужно передать новую версию боди, которая изменяет одни данные и удаляет другие.

на практике удалять поля как бы и незачем

Обычно да, но вопрос был задан именно в таком варианте.

Ну, как я там уже сказал, если очень хочется одним запросом — добавляем в ключ поля мета информацию/оператор/экшн что с ним нужно сделать готово. Какие тут варианты еще тут могут быть? Передавать

«empty»: [
«fieldC»,
«fieldD»
]

более стремное решение. А так и структура объекта сохраняется, гибко и все вполне читаемо.
И если нет сложных операций с вложенными объектами типа добавления элемента в вложенный массив, то приняв простое соглашение, что null равнозначен отсутствию поля, проблема решается сама собой. Передали null значит бэк может удалить это поле в модели, а может и не удалять, только занулить, без разницы, ибо все клиенты трактовали бы это в обеих случаях как отсутствие поля. Зачем клиенту отличать эти два состояния с практической стороны вопроса?

PATCH только для изменения значения полей,

А чем выставления 1 поля в модели (ресурсе) как null не изменение ?

Зачем нам удалять address? Есть ресурс, есть его схема, зачем удалять от туда что то, вместо сброса в null? Очень ли важно различать отсутствие поля в модели от его наличия с null?

Ну опять же. Как я понимаю, если у нас есть 9999^9999 инпутов то придут Frontend devs и попросят Patch сделать. А сохранять в бд отсутствие параметров как пустые строки или 0 кажется неправильным подходом.

Да и если что в DELETE как бы не запрещено body передавать.

Хм. В REST мы же работает с ресурсами. И если мы вызываем DELETE /card/1 то (я думаю) мы ожидаем что этот ресурс удалится и плевать какое body.
Можете объяснить что вы хотели сказать ?

то приняв простое соглашение, что null равнозначен отсутствию поля, проблема решается сама собой.

Ну так смысл вопроса в том что при работе с моделями бэк и не может понять нам передали null или нет. (без костылей аля препроцессим json руками) И тут нужно идти дальше и говорить что мы теперь не поддерживаем null вообще и пускай используют PUT если юзер что то удалил что напрочь убивает смысл PATCH и его удобство для Frontend devs. (если конечно мы не используем дефолтные параметры везде и бд у нас с null а не с 0 и пустыми строками)

А чем выставления 1 поля в модели (ресурсе) как null не изменение ?

Ну правильно, это изменение, но не удаление же? Зачем нам удалять с модели поле?

А сохранять в бд отсутствие параметров как пустые строки или 0 кажется неправильным подходом.

ну так null же и ставим. Поля с null можно и не отдавать клиенту, считая что их и нет в модели.

И если мы вызываем DELETE /card/1 то (я думаю) мы ожидаем что этот ресурс удалится

ну да. А если передадим в body json схему, то удалим не саму модель, а указанные поля.

Ну так смысл вопроса в том что при работе с моделями бэк и не может понять нам передали null или нет.

Ну видать это грабли конкретной реализации парсера в строго типизированном ЯП. Без понятия что там за такой парсер, что предоставляет API который не различает отсутствие поля и поле с null.

Зачем нам удалять с модели поле?

Потому что PATCH это частичный апдейт где вся идея в том, что в переданном JSON нету некоторых полей.

Поля с null можно и не отдавать клиенту, считая что их и нет в модели.

Вопрос не про запись, а чтение json.

body json схему, то удалим не саму модель, а указанные поля.

т. е. Delete /card/1 {address: null} должно модифицировать 1 поле ? С body в Delete и так хватает проблем, а тут мы еще не удаляем ресурс а модифицируем его. stackoverflow.com/...​or-an-http-delete-request

Кажется неправильным использовать Delete для такого так как он должен удалять весь ресурс.

Ну видать это грабли конкретной реализации парсера в строго типизированном ЯП. Без понятия что там за такой парсер, что предоставляет API который не различает отсутствие поля и поле с null.

Так в этом и смысл что это не парсер, а то в то что он парсит.
Опять же пример.
{}
{ «code»: null ... }
{ «code»: 0 ... }
{ «code»: 999 ... }

В Java будет
public class TestClass { Integer code; }
null, null, 0, 999

в GO будет
type Data struct { Code int `json:"code"}
0, 0, 0, 999

мені здається проблема все ж RTFM, а не java
та всього того що у гілці далі було згадано.

application/xml-patch+xml
RFC-5261: An Extensible Markup Language (XML) Patch Operations Framework Utilizing XML Path Language (XPath) SelectorsRFC-7351: A Media Type for XML Patch Operations
=>
application/json-patch+json jsonpatch.com
RFC-6902: JavaScript Object Notation (JSON) Patch („Utilizing JSON Pointer” вирішили вже не писати)
=> JsonPatch є java|jakarta ee JSR-374: JSON-P(rocessing).

не мені звісно казати, бо PATCH в останньому проєкті зроблений на тій самій сутності,
проте ми хоч розуміємо, що самі собі буратіни, і не на дзеркало треба кивати.

мені здається проблема все ж RTFM, а не java

Вам неправильно здається.
Перечитайте уважно про те, в чому проблема з null.

то може ж наведете що читати?
і якщо не важко,
з прикладами де визначений стандарт на PATCH матиме проблему з null.

то може ж наведете що читати?

Мій коментар, який ви відкоментували, неуважно прочитавши.
Проблема з нулом — побічний єфект прийнятої в джаві як де-факто стандарт технології мапінгу жсону одразу на модель. Якщо у DtoObject.field == null, то неможливо відрізнити це переданий нулл з інпуту користувача, чи відсутні дані.
Ця проблема зникає, якщо приймати результати у іншому вигляді, наприклад як JsonObject.

Не розумію, що ще потрібно тут пояснювати або читати. Це може бути незрозуміло хіба що тому, хто ніколи не бачив джава дто обєктів з анотаціями джексона.

перечитав — проблема першого коментаря обмежена spring GetMapping, і до java не відноситься.
RTFM: XML-PATCH, JSON-PATCH, JSR-374.

для любителів велосипедів з DTO, чим і сам грішний,
jackson-modules-java8 підтримує Optional.

Но вот в чем проблема. Как нам понять что человек передал нам null или просто не передал нам поля и он не ожидает что поле очистится.
{ «id»: 1, «address»: null }
==
{ «id»: 1 }

Это же элементарно. В Go есть метатег «omitempty», и если он НЕ указан напротив поля в структуре, то это поле будет записано в json, если оно пустое/нулевое. Если указан — то не будет. Какой отсюда правильный вывод? Совершенно очевидно, что пора всем оставшимся слезать с джавы и переходить на Go, поскольку в джаве возникают какие-то идиотские траблы на самых элементарных вещах.

У Котлин все ок

Я вижу что там используют зачастую примитивы и у них конечно есть дефолтное значение. Если в Java использовать тот же подход то мы получим тот же объект на выходе с дефолтными 0, 0.0 (а для String можно явно задать дефолт "")

И я не понимаю где

Это же элементарно

. Вы сами говорите что у вас

это поле будет записано в json, если оно пустое/нулевое.

. Именно что пустое оно или нулевое как раз в PATCH имеет значение.

Разница между null и дефолтным 0 же огромная...

Разница между null и дефолтным 0 же огромная...

В Go строгая типизация, примитивные типы могут принимать либо null, либо 0. Числа могут принимать только 0, указатели — null. Если указан метатег типа `json:"field,omitempty"`, то поле пропускается, если это числовой 0, пустой указатель, пустая строка. Если тип поля или структура при чтении не известен, можно читать в тип interface{}, и потом разбираться, что прочитано.

Хм. А можете привести пример где эта проблема явно решена. Именно код модельки с которой можно понять
{ «code»: null ... } - тут нужно выставить code в null (нам важно знать что он именно отсутствует)
{ «code»: 0 ... } - тут нужно выставить code в 0 (валидное число от юзера)
{ «code»: 999 ... } - тут нужно выставить code в 999 (валидное число от юзера).

И как с моделькой дальше можно работать без костылей

Проблемы нет как таковой. Если читается структура из json, то все отсутствующие в json поля принимаются нулевыми (0 для чисел, «" для строк, null для указателей). Лишние поля в json — игнорируются для детерминированной читаемой структуры. Когда структура пишется в json, то нулевые поля с метатегом «omitempty» не записываются.

Например, если поле в структуре определено как Code int `json:"code,omitempty"`, то нулевое значение Code записано не будет в json. Если определено как Code int `json:"code"`, или вообще без метатегов, то будет записано в json как "code": 0, null в этом случае писаться никогда не будет. Аналогично кстати и с yaml Code int `json:"code,omitempty" yaml:"code,omitempty"`

Ну вот вы говорите про запись в json, а я про чтение.
Как я понимаю в Go должно быть что то наподобие *Integer и тогда мы получим ту же проблему что и в Java. Мне не нужно создать json мне нужно его обработать.
(Забыл про главный use case когда {} = ничего не меняем)

Просто вы приводите пример (и судя по коду так заведено в Go) с использованием примитивов которые не могут содержать null, и имеет всегда состояние со значением что ещё больше (по моему мнению) связывает руки. И добавляет вопрос Как понять что у юзера на счету 0 грн или он ещё даже не имеет счета.

Но думаю это разговор уже совсем другого топика, и если

элементарно

сводится к использованию встроенных дефолтных значений то нужно понимать что это не совсем полное решение а больше как ограничение

Да, я немного ошибся, если при чтении какие-то поля не указаны — они пропускаются. Вот пример в плейграунде: play.golang.org/p/CwX1wQqCMRO

Думаю это будет более наглядно.
play.golang.org/p/-8vHycUBflq

Опять же. В Go все свелось к тому что там примитивы и дефолтные значения что (как по мне) только усугубляют эту проблему т. к.

{}
{ «code»: null ... }
{ «code»: 0 ... }
{ «code»: 999 ... }

В Java будет
public class TestClass { Integer code; }
null, null, 0, 999

в GO будет
type Data struct { Code int `json:"code"}
0, 0, 0, 999

Но думаю что это больше философия языка и подход про который нужно больше почитать чтобы понять как оно вообще тогда с БД работает.

Думаю ответ в плане, в GO это решено закрыт.

В Котлин этой проблемы нет если я не ошибаюсь, там можно конкретно указать какое поле сетишь

Так и в java jackson не единственный способ работы с JSON.

Звідки взялось правило шо POST — це тіки створення а PUT — зміна?

Мені здається POST це для методів які при кожному повторному виклику повертають новий, не кешуємий, результат (створення об’єкту сюди підпадає, але ж не тільки)

Чим PUT books/new вам не підходить?

І взагалі я б POST метод у API взагалі не використовував. Без нього якось xss не дуже заходить.

Звідки взялось правило шо POST — це тіки створення а PUT — зміна?

Немає такого правила, але частіше за все так відбувається.

Чим PUT books/new вам не підходить?

Слово «ідемпотентність» вам щось говорить? Якщо ні, то почитайте.

РЕСТ — це не стільки про АПІшечку, скільки про архітектурний стиль побудови розподілених систем, система рекомендацій, що суттєво спрощують побудову таких систем.

www.ics.uci.edu/...​pubs/dissertation/top.htm

Ох уж ця індепотентність. В 99% RESTful приложух окрім GET нічого нормально не кешується, але про неї кожен раз згадують.

Ох уж ця індепотентність. В 99% RESTful приложух окрім GET нічого нормально не кешується

Почитайте таки про ідемпотентність. Спойлер: ідемпотентність не гарантує можливість кешування (для цього є інший термін)

Якшо PUT не кешується то немає сенсу дотримуватись його суцільної індепотентності.
Отже немає сенсу класти усі не індепотентні поведінки у POST.

А яку думку своїм «почтийте» намагатись ви донести хз.

видно единоверец GraphQL, чьи постулаты гласят «REST уже не торт»

Так он никогда не был торт, вечно вот такого рода статьи пилят. Все потому что его выдумали хипстеры в эпоху jquery девелоперов для домохозяек-вайтишников. Хотя уже и openapi сто лет существует, но мыши продолжают грызть кактус.

OpenAPI это язык для разметки именно RESTful api соответственно REST живее всех живых, хоть и имеет недостатки, и OpenAPI был создан для облегчения жизни тех кто делает или использует REST api

вечно вот такого рода статьи пилят

Потому что вечно есть куча «девелоперов» которым не хватает мозга понять простые концепции и выучить пару терминов. Особо вмазанные потом пилят говно типа графкл в надежде заполнить им пустоту в голове.

вмазанные потом пилят говно типа графкл

Ну вот что ты нагнетаешь. Некотрых кастомеров наоборот умиротворяет вот это все. Стильно, модно, молодежно.

Некотрых кастомеров наоборот умиротворяет
вмазанные

:)

Особо вмазанные потом пилят говно типа графкл

GraphQL придумывался фейсбуком для вполне конкретного, и довольно специфичного для них сценария использования. Поэтому, «особо вмазанными» я бы называл, разве что, тех, кто бездумно лепит GraphQL как замену REST без учёта контекста использования.

А так, эта технология решает свои задачи, которые с использованием только REST решаются гораздо менее эффективно. Просто далеко не на каждом проекте такие задачи есть.

которые с использованием только REST решаются гораздо менее эффективно.

Вообще как то странно, что GraphQL сравнивается обычно с голым REST на голом http т.е чисто спецификацией, а не имплементацией. Как будто кто то мешает при желании натянуть на абстрактный CRUD контроллер те же поля, пагинацию, ресолверы, валидаторы, выплевывание доков и т.д. Зарядить то можно что угодно и по сути получить функциональный аналог GQL, но по идеологии множественных ресурсных эндпоинтов.

Просто далеко не на каждом проекте такие задачи есть.

Что значит «далеко не на каждом проекте» ? В основу GraphQL положена ведь простая идея. Верни мне только то, что мне нужно, а не все. Например если мы возьмем эту страницу доу, то пользователь должен видеть комментарии до определенного уровня вложенности, потому что некоторые страницы имеют по 10 тыс комментариев.
Если у нас есть грид где пользователь может кастомизировать колонки которые он видит (неужели это такая редкая функциональность ?) то GraphQL позволяет перечислить колонки которые нам нужно вернуть, а не лепить это все в рест велосипедом типа список колонок, а потом собирать с этих колонок кверю попутно оставляя лазейку для sql injection в рест.

Неужели не понятно, что у рест огромное количество ограничений даже для самых простых проектов с одним гридом или деревом комментариев ?

Я уже молчу что через рест проблема загрузить банальную картинку больших размеров, которые сейчас продуцирует каждый первый телефон в кармане. Наверное это тоже «не на каждом проекте есть».

если мы возьмем эту страницу доу, то пользователь должен видеть комментарии до определенного уровня вложенности, потому что некоторые страницы имеют по 10 тыс комментариев.

Чем REST мешает добиться такого же эффекта, передавая параметр в духе maxDepth в query string?

то GraphQL позволяет перечислить колонки которые нам нужно вернуть, а не лепить это все в рест велосипедом типа список колонок, а потом собирать с этих колонок кверю попутно оставляя лазейку для sql injection в рест.

Во-первых, с каких делов это «велосипед»? Если уж так хочется максимально дистанцироваться от not-invented-here, то для этих целей давно придумали OData.

Во-вторых, это почему же в REST, по вашему мнению, есть лазейки для SQL injection, а в GraphQL всё хорошо и идеально в этом плане? Сама спецификация GraphQL никак от SQL инъекций не защищает, а с учётом любви хипстерствующего молодняка к наколеночным реализациям GraphQL резолверов — я бы больше переживал именно за такой подход в плане безопасности.

Я уже молчу что через рест проблема загрузить банальную картинку больших размеров

А зачем грузить через REST картинки? Он не предназначен для этого.

Чем REST мешает добиться такого же эффекта, передавая параметр в духе maxDepth в query string?

Тем что это костыль. Каждый раз когда нужны будут данные немножко с другого ракурса, нужно будет лепить новый параметр, флажок или еще один метод.

Если уж так хочется максимально дистанцироваться от not-invented-here, то для этих целей давно придумали OData.

О, уже появляются новые аббревиатуры. А зачем мне новые аббревиатуры, если у меня задача пределльно проста. Кастомизировать грид и не гонять всем паровозом трафик ?

Концепция сделать выборку только того, что нужно (на подобии GraphQL), почему-то отвечает на все вопросы. Уменьшает трафик, координально уменьшает количество методов в апи (а с ними всякие прослойки маперов с дто), увеличивает быстродействие, делает интерфейс более гибким, понятным и естественным.

А зачем грузить через REST картинки? Он не предназначен для этого.

Странно это все. То тут рест подходил от эмбеддед до грузовиков самолетов и пароходов, а то тут уже с задачей грузить медиа файлы не справляется.

Вам норм працювати із цим мішком новаторської істини на спині? Не тисне :)?

У топи в таблиці Мєндєлєєва. Але REST тут ні до чого. Маєте свої розробки — буду радий почитати. Дайте, будь ласка, посилання на свої статті. Коммент про «путь», це, нажаль, непереконливо.

Все потому что его выдумали хипстеры в эпоху jquery девелоперов для домохозяек-вайтишников.

Охлол, видимо ты сам вайтишник-домохозяин, потому иначе ты бы знал что REST — это диссер одного очень умного чувака на 400 страниц. Сходи подучи матчасть. И да, создавался он еще тогда когда до jquery было еще 6 лет=)

Так эту диссертацию нужно было писать, после того как увидишь десятки проектов, а не после студенческой скамьи, с целью получить свою докторскую лычку.

В отличии от наших реалий, в западных универах большинство персонала таки имеет опыт работы в реальных проектах, чистые теоретики, как у нас, там редкость.

Опыт работы в реальных проектах это скорей всего стажировка. Если это какой-то инновационный алгоритм, Ок. Можно легко проверить что это работает. Но разрабатывать подходы в проектировании на стажировках, это немного неправильно. Это должни делать дядьки с 20-30 годами опыта работы, которые насмотрелись предостаточно живых трупов.

Мне вот интересно. Когда люди пишут, технология ХХХ самая лучшая. Они действительно думают что ХХХ будет и через 5 лет и через 10 и через 20 и вообще прогресс остановился на месте и лучше уже не придумают ?

REST? в 2021м? :D

HTTP? в 2021м? )
TCP/IP? в 2021м?
RDBMS? в 2021м?

Уровень абсурдности вопросов примерно один и тот же
:D

Нет, не одной и тоже. Об этом даже в статье написано.

HTTP? в 2021м? )
TCP/IP? в 2021м?
RDBMS? в 2021м?

Только если не иметь общего представления.
Например HTTP идет поверх TCP/IP — тебя не заставляют самому данные бинарным буфером отправлять в 2021.
REST идет поверх HTTP — тебя не заставляют формочками постить в HTML
Поверх REST тоже есть технологии, которые не заставляют тебя клипать однотипные методы на каждую энтити

Только если не иметь общего представления

То, что ты не имеешь представления о ресте, мы уже выясняли. А дальше твой пост не интересен.
А вообще это была ирония, которой ты не понял. Что в принципе для тебя неудивительно.

Сделай такой рест от которого ты бы не сокрушался в каждом топике, во что превратились проекты.
Кстате в статье выше, на 4 нечастных энтити я уже насчитал 11 рест методов. А ведь там еще нет гридов, какихто сложных визардов, фильтров для поиска, секурити и тд тд, которые есть в обычном приложении.
Рест хорош примерно как лопата. Посмотрел, о лопата, можно яму копать. Только потом выясняется что копать придется карьер, гранитный.

Сделай такой рест от которого ты бы не сокрушался в каждом топике

Ооо, за меня не волнуйся, я такой рест и делаю. Клиенты, которые работали с моим рестом, бывало писали в телегу благодарности, а девы, работавшие с моим же рестом у клиентов, переходя на другие проекты, писали мне о том, что после моего реста они не могут без глазных капель смотреть на другие апи.

семью позвал все благодарят соседи инвестигейтили — тоже благодарят. даже несколько человек умерло от благодарности. полицейские приехали, пожарные, на районе кипиш все от благодарности помирают, на похоронах все благодарят. сам президент приехал вся страна благодарит. Спасибо, бруже, за твой рест!

Ваше промовисте «Закусывай, чувак» неповне без знання руських мемів, на один з котрих Ви так симптоматично відреагували. Якось погугліть.

писать русские мемы українською мовою

Нет. Мне не нужно результаты своей роботы выдумывать, не меряй всех по себе

Щодо REST є три стадії:
— вивчення технології і загальноприйнятих стандартів
— хейт стандартів з юнацьким запалом
— розуміння необхідності дотримання принципів стандартизації

Потом приходит осознание того, что стандарта нет и каждый лепит свой REST из говна и палок в меру своих фантазий об RFC которого нет.

то стадія 2. Коли розумієш, що твоїм АПІ не хочуть користуватися і йдуть до конкурентів, приходить стадія 3

99% успеха апи это высокая доступность(отклик, аптайм, всплесковые нагрузки), документация и поддержка для ишуёв и фичареквестов. А от того что там урлы не по феншую еще никто к конкурентам не ушел.

Роузуміння, що між «новомодний» та «загальноприйнятий» — різниця у всьому. Модне не стане загальноприйнятим, воно стане старомодним. REST — старомодний.

Хейтити стандарти — глупо. На них треба покладатися. Але REST — стандарт минулої епохи, він гарнюня для того, для чого його створили. А творили його НЕ стандартом, а нативними правилами, тобто еволюційно виявилося, що для керування даними (здебільшого невеликими файлами) з консолі (Карл!!!!!!!!!!!) варто запам′ятати кілька простих команд, а вже потім це стало стандартом.

Сьогодні IT має суттєво ширшу область застосування, оцифровуються найскладніші процеси, які вимагають відповідних протоколів. А на DOU все ще шастають манкі-формошльопи, які драконять на REST. Замість того, щоб застосовувати стандарт там, де він дає вигоду, а де нема — забити на нього і зробити на більш низькому рівні абстранкції.

Візьмемо гіпотетичний приклад, що для якоїсь задачі дуже цікаво стало робити FUCK. Наприклад, процес подорослішав, сталося поглинання компаній, і відповідно потрібно стало транслювати потоки інфи. Що зробить адепт REST? Цілу купу запитів PUT і GET, які звісно ж створять проблеми, бо за логікою мають іти чітко один за одним, а REST (біда! біда!) не має засобів контролю послідовності та синхронизації. А тому, будуть костилити.

Можно нам другого Пєніє, а то этот сломался? :)

Хейтити стандарти — глупо.

a week earlier:

Я так и сказал — JS плохой язык.
следовало бы навсегда уничтожить JavaScript
Я так и сказал — нужно просто убить язык
JS нужно ликвидировать.
нужно немедленно переходить на новый язык

Как прекрасно вырезать из контекста. Свой код ты тоже копипастишь, удаляя все ЕСЛИ?

JS нужно убить только когда будет вменяемая альтернатива. Прежде всего по производительности и компактности хранения данных. Тот монстр что сейчас рождён под небольшие манипуляции. Для сложных задач он дико проблемен, а отладка ЖабаСкрипта, особенно чужого, писанного неизвестно кем, когда и где — это ужос-ужос.

JS — это не стандарт. Это куча стандартов, совместимы между собой чуть менее чем никак. Нет даже общей виртуальной машины, на которую можно ориентироваться, что работает в одном месте, валится с ошибкой в другом.

Саппорт проектов на JS — это геморой, прежде всего финансовый. И хотя десятки лет эволюции инструментов для ЖС не прошли даром, всё равно, добиться решения проблемы, даже чётко указав, где она — задача «со звёздочкой» даже для меня. Чего уж говорить об обычных пользователях.

tc39.es/ecma262 не один язык не проектируется без абстрактной машины, на которой этот язык исполняется. Вы можите взять несколько разных js движокв и запустить спокойно один и тот же код. Он вам выдаст одно и тоже, то что Вы написали чистый бред. Есть нюансы с различными расширениеми языка, дополнениями основного стандарта.
Memory layout, как Вы себе это представляете, в таких родах языках, где нету статической типизации и структура объекта может полностью поменяться. Для layout дрочерства есть developer.mozilla.org/...​e/Global_Objects/DataView
Вы, что пишите распределенные системы для решения science задач на js? Если да, то Вы ебаклак.

Так можно придраться, на ровном месте, к любому языку начиная от C заканчивая венцом человеской эволюции Basic. Ну давайте все дружно на assembler перейдем, что может быть лучше, а да Вы в какую церковь ходите AT&T или intel. Если в последнюю то на костер Вас. Фатализм без понятия, того как работает js и проблемы рынка.

JS нужно убить только когда будет вменяемая альтернатива.

То есть говорить что JS какаха и все в нем не так, это не хэйт, потому что есть сноска, что пока нет альтернативы «приходится» его терпеть? Логично :)

JS — это не стандарт.

Всего лишь единственный и общепринятый ЯП для разработки на фронте, считай стандарт, с которым работает огромное количество разрабов.

Нет даже общей виртуальной машины, на которую можно ориентироваться, что работает в одном месте, валится с ошибкой в другом.

Очень интересно узнавать что то новое. И что в твоем понимании виртуальная машина? Я надеюсь ты не путаешь браузер с виртуальной машиной/движком js. Cколько ж актуальных JS движков есть нынче, если ты о них, и какие там несовместимости по части js?
Даже если бы так было, то откуда о такой специфике знает месье, который на JS ничего не писал? А в мк типа ESP тоже будем пихать настольную версию «виртуальной машины»?
Как раз суть в том, что код JS, написанный в конце 90-х будет ровно так же исполнен на современных движках, при чем куда эффективнее из-за более совершенных его реализаций по части оптимизаций.

Саппорт проектов на JS — это геморой, прежде всего финансовый.

Ммм и откуда тебе знать то? А саппорт кровавого энтерпрайза на джаве не «финансовый геморой»? Как же так получилось, что джаву потеснил JS на бэке, если это геморно, дороже и ущербнее? :)

добиться решения проблемы, даже чётко указав, где она — задача «со звёздочкой» даже для меня

Мне очень нравится акцент «даже для меня»- оказывается не так просто решать задачи на JS, абсолютно его не зная и с ним не работая, даже если ты сам господин Пєніє :)

Чего уж говорить об обычных пользователях.

А «обычные» это кто? Оо

А тому будуть створювати новий мікросервіс для трансляції потоку.
Є задачі для gRPC/websocket, є задачі для REST, тому що генерація/перевірка jwt токена і лайв-чатік різні речі, але і те, і інше не збирається вмирати найближчим часом

Саме так! REST гарнюня. А от намагання все на світі запихнути в REST та заборонити те, що туди не лізе — є мракобіссям.

Нестандартна позиція завжди привертає увагу середньостатичних.
Скажіть, що Ви думаєте про RESTfull JWT Autherntication API in 2021? Чи варто? Чи є альтернативи?

Ты такую техночушь сейчас постишь, писец просто. Какие-то консоли прошлых эпох эволюционируют в компании и плодят непоследовательные ПУТ и ГЕТ чем создают проблемы для непоследовательной логики, прям стыдно читать.

То есть код с этим дерьмом читать тебе не стыдно? Или ты сибаритствуешь и кода не пишешь, а только учишь как надо?

С каким дерьмом?
Не имею привычку читать дерьмо, так что тут ты, видимо, авторитет.
Или ты дерьмом назвал пут и гет методы хттп?

Или ты дерьмом назвал пут и гет методы хттп?

Да, так и есть. Дерьмо по трафику. Дерьмо по архитектурному подходу. По объему необходимого кода, да по много чему.
Это один из самых вредных подходов. И вредность его заключается в том, что простыми принципами пытаются решить сложные задачи.
Запомни. Системы никогда не решали задачи дерганья друг друга методов, системы решали задачи правильной _синхронизации_ и _восстановления_ данных между собой.

Как только это поймёшь, сразу придет осознание, почему в более менее интересных системах рест не видно и не слышно. В тех же стриминговых сервисах, мессенджерах, систем репликации, офлайновых систем вроде Гита и тд тп. Рест это крайне примитивный подход, который годится только для очень примитивных приложений. А для не примитивных он даёт крайне большой оверинжениринг и просто не решает задачи на том уровне на котором они должни быть решены.

ПС Сейчас ты в своем стиле пробузишь «для каждой задачи свой инструмент», но нет. Экскаватор годится чтобы копать траншеи и карьеры, а лопата (рест) только траншеи

В тех же стриминговых сервисах, мессенджерах, систем репликации, офлайновых систем вроде Гита и тд тп.

Мда, ты видно совмем тупой. Я гдето сказал, что рест должен использоваться вообще для всего? Нет, это ты так себе придумал в порыве технобреда и чсв.

О, начал недостаток своего скудного образования компенсировать переходом на личности.
Ну хорошо что хоть пришло понимание, что рест годится только клепать сайтики вида

Книги (books).
Замовлення (orders).
Автори (authors).
Користувачі (users).

И то, пока там не попадается многошаговый визард для заказа товара. Там уже начинаются первые костыли на фронт и бекенд части.

Ну хорошо что хоть пришло понимание, что рест годится только клепать сайтики вида

Рест годится не только для этого. Но у тебя слишком мало опыта и понимания чтобы это понять.

Там уже начинаются первые костыли

хватит уже, то что ты не умеешь в данные и поэтому везде лепишь костыли уже все поняли.

Рест годится не только для этого.

Давай примеры.

Давай примеры.

Любой домен, отвечающий таким условиям:
1) обладает фиксируемым на определенный момент времени(запрос) состоянием
2) состояние может быть представлено в неком формате, доступном для сериализации
3) состояние приложения обладает некоей степенью статичности, которая наделяет сериализуемый фрагмент состояния приложения на определенный момент времени существенной бизнес-значимостью.

Предыдущий вариант получился слишком запросо-центричным.

Любой домен, отвечающий таким условиям:
1) обладает фиксируемым на определенный момент времени[время запроса] состоянием или же это состоние может быть создано
2) состояние приложения (или его фрагмент, который в текущем контексте можно считать полным состоянием) может быть представлено в неком формате, доступном для сериализации для трансфера сериализованных данных в/из приложения
3) состояние приложения (или новый его вариант, в который оно перейдет вследствии передачи данных) обладает некоей степенью статичности, которая наделяет сериализуемый в определенный момент времени фрагмент состояния приложения, который участвует в трансфере в/из приложения, существенной бизнес-значимостью.

Также:
Фрагменты логики приложения, не обладающие сколько-нибудь статичным [значимо хранимым] состоянием, могут быть предоставлены посредством реста, однако использовать какой-нибудь событийный протокол[технологию] передачи данных представляется более удобным и естественным.

Также:
Домен не обладает повышенными mission-critical требованиями, такими, которые делают время оверхеда, связанного с HTTP, нежелательным.

Давай примеры.

Любые.
Сайт-визитка. Управление состоянием любой аппаратуры. Корпоративное приложение любой степени сложности домена. Управление любыми логистическими операциями, складские роботы, корабли, грузовики, самолеты, грузы. Управление складами. Управление персональными данными. Управление платформами по обработке информации. Учетные данные любой специфики.
Степень сложности и/или глубины вложенности доменных моделей не играет при этом никакой роли.

Жесть то какая.
Тебе нужно было назвать четкие критерии, когда ты считаешь что рест подходит и когда рест не подходит. Чтобы можно было взять например мессенджер аля Телеграмм и определить, нужен там рест или нет. Критерии должни были быть четкие, по пунктам, например асинхронность, критичность к быстродействию и трафику, отсудствие нужды в публичном апи, работа с целостным доменом и тд.
Вместо этого ты наплел каких-то сериализаций, кораблей, самолетов, статических состояний, доменов вышитых лобзиком и еще хрен знает чего (все взято из твоего поста буквально).

Слушай дружище с такими зачатками логики, класификации и систематизации данных у тебя должно хорошо получатся кричать «вильна касса». Пробуй свои силы в Макдональдс.

Разговор был про домены, а не про технические критерии, но я уже заметил, что ты пытаешься поменять контекст, когда тебе нечего ответить умного, то есть почти всегда .
И да, я ожидал что ты ничего не поймешь ввиду своей ограниченности, и напишешь чтото пассивно-агрессивное.
Ты надоел, досвидос.

Какой контекст. Тебя спросили когда использовать отвертку. Ведь ты этот инструмент каждый день используешь и шурупувертов не признаешь ? Но ты вместо того чтобы четко и кратко по пунктам объяснить плюсы отвертки, наплел два поста художественной литературы.
Не удивлюсь если в твоём проекте болты отвёрткой закручивают, что ещё взять с специалистов которые не могут внятно объяснить зачем им этот инструмент.

ебя спросили когда использовать отвертку

Так Дмитрий и привёл целый список подходящих сценариев:

Управление состоянием любой аппаратуры. Корпоративное приложение любой степени сложности домена. Управление любыми логистическими операциями, складские роботы, корабли, грузовики, самолеты, грузы. Управление складами. Управление персональными данными. Управление платформами по обработке информации. Учетные данные любой специфики.

Что не так?

Чтобы можно было взять например мессенджер аля Телеграмм и определить, нужен там рест или нет.

На это тоже был ответ, пусть и несколько косвенный:

Фрагменты логики приложения, не обладающие сколько-нибудь статичным [значимо хранимым] состоянием, могут быть предоставлены посредством реста, однако использовать какой-нибудь событийный протокол[технологию] передачи данных представляется более удобным и естественным.

В условном телеграмме значимо хранимым состоянием могут быть настройки, списки контактов и история чатов. Но, так исторически сложилось, что довольно часто за хранение списка контактов и истории чатов отвечает чат-сервер со своими протоколами взаимодействия (тем же пресловутым XMPP). А вот настройки клиента, да — вполне могут отдаваться и обновляться через REST API endpoint.

Так Дмитрий и привёл целый список подходящих сценариев:

Мне не жалко. Давайте пройдем по списку.

Управление состоянием любой аппаратуры.

Что значит любой ? Сколько аппаратуры управляется через rest ?
Почему у моего принтера или роутера и даже мобильного нет рест апи ? У скольких станков с чпу есть рест апи ? Что если состояние аппаратуры нужно отслеживать асинхронно ? Например отправили сигнал на аппаратуру, аппаратура ушла «думать» и через неопределенное время присылает ответ.
Что если на аппаратуру нужно загрузить блок данных например в 100 мегабайт ?

Корпоративное приложение любой степени сложности домена.

Это там где Дмитрий в прошлый раз сел в лужу ? Поскольку при сложном домене, все части домена должни быть согласованы. А значит нельзя дернуть метод А поддомена и метод Б поддомена, поскольку оба могут вернуть разное состояние обьекта.

Управление любыми логистическими операциями, складские роботы, корабли, грузовики, самолеты.

Забыл упопомянуть марсоходы ракетоносцы спутники и даже электросамокаты. Всем не хватает рест фул апи !

грузовики

Ой, вэй. Сбросьте ссылку на сваггер. Возьму браузер попробую у своего новенького сува через постмен открыть багажник.

Сколько аппаратуры управляется через rest ?

Для хабов, управляющих устройствами класса «умный дом», REST интерфейсы вполне применяются.

Для принтеров и роутеров REST интерфейсы не делают, т.к., во-первых, не очень-то и нужно, а, во-вторых, исторически так сложилось, что программные интерфейсы для взаимодействия с этими устройствами были другими.

Поскольку при сложном домене, все части домена должни быть согласованы. А значит нельзя дернуть метод А поддомена и метод Б поддомена, поскольку оба могут вернуть разное состояние обьекта.

Вот только REST к этому совершенно ни при чём. Согласованность обеспечивается логикой внутри домена, а не способом взаимодействия с публичным интерфейсом этого домена, коим и является REST.

Сбросьте ссылку на сваггер. Возьму браузер попробую у своего новенького сува через постмен открыть багажник.

На прошлой работе коллеги участвовали в проекте по connected car. И, если память не подводит, клиентское приложение с бэкендом общалось через REST. Сама электроника автомобиля, скорее всего, использовала другие протоколы для взаимодействия с бэкендом, потому, что REST over HTTP не очень подходит для нестабильного и медленного соединения (4G какбе есть не везде, и даже там, где есть — не факт, что гарантирует широкую пропускную способность канала на уровне Application Layer)

Было

Управление состоянием любой аппаратуры.

Стало

Для принтеров и роутеров REST интерфейсы не делают, т.к., во-первых, не очень-то и нужно

Ясно понятно.

Согласованность обеспечивается логикой внутри домена

Какого домена. Если я например делаю запрос SQL то с консистентностью никаких проблем (через теже транзакции). Но Рест получается эту важную фичу просто ломает. Потому что в идеологии Рест у нас 100500 маленьких методов на каждое действие. С «большим сложным доменом» это так не работает.

На прошлой работе коллеги участвовали в проекте по connected car. И, если память не подводит, клиентское приложение с бэкендом общалось через REST.

Нет там никакого рест. Глупые люди, учите матчасть. Чтобы сделать рест нужен полноценный _веб сервер_. Зачем делать из автомобиля веб сервер ? Кто эти говнокодеры ?

Было
Стало

Когда нет аргументов, начинают докапываться к словам. И принтерами, и роутерами технически можно управлять через REST интерфейс — другое дело, что это не практично в условиях устоявшихся решений и стандартов для этих устройств.

Кстати, я вот совсем не уверен, что встроенные Веб-админки для роутеров не используют REST для получения / записи настроек.
Специально проверил свой — так там вообще что-то в духе GraphQL.

Потому что в идеологии Рест у нас 100500 маленьких методов на каждое действие.

Это неверно, в REST идеологии нет методов, кроме методов HTTP протокола.

То, что RESTом частенько называют то, что, по факту, является JSON-RPC — это уже другой вопрос. В каноничном же REST есть состояние объекта, и никто не запрещает представлять это состояние так, чтобы в него нельзя было вносить несогласованные изменения.

Нет там никакого рест. Глупые люди, учите матчасть. Чтобы сделать рест нужен полноценный _веб сервер_. Зачем делать из автомобиля веб сервер ?

Автомобиль — если мы говорим о connected car — существует не в вакууме. Есть мобильное приложение, или Веб-«морда» для управления им. По какому протоколу, как ты думаешь, это приложение или Веб-интерфейс общаются с бэкендом, который уже перенаправляет нужные команды самому авто?

И принтерами, и роутерами технически можно управлять через REST интерфейс

шта ? Ты через рест будешь отправлять картинку на печать ? Там надо вообщето медиа файлы грузить, которые вроде как ты согласился что их рестом не грузят.

Веб-админки для роутеров не используют REST для получения / записи настроек.

Для каких настроек ? Зачем там рест ? Кто туда будет через тот рест ходить ? Роутер хостит простенький сайт для настроек. Никакого реста там не нужно, это пятое колесо.

В каноничном же REST есть состояние объекта, и никто не запрещает представлять это состояние так, чтобы в него нельзя было вносить несогласованные изменения.

Через костыли бесспорно, можно и микроскопом гвозди закручивать против часовой стрелки. Но зачем ?

Есть мобильное приложение, или Веб-«морда» для управления им

Мобильное приложение != Веб морда.
Нормальное мобильное приложение может стримить через тот же блю туз аудио файлы, видео файлы в авто. Какой тут рест ?
Не неси чушь.

Согласованность обеспечивается логикой внутри домена, а не способом взаимодействия с публичным интерфейсом этого домена, коим и является REST.

Я ему уже это пытался сказать разными словами раз 10. Он не понимает ни аргументов, ни что такое интерфейс и воюет с какими-то ветрянными мельницами в своей голове.

При DDD подходе мы читаем и работаем с полным доменом (агрегатом). Домен может быть достаточно больших размеров и плохо пролазить через рест.

При DDD подходе мы читаем и работаем с полным доменом (агрегатом

Боже какой технобред ты несешь. Пойди почитай, что такое ддд и не пиши ебалы.

ддд

Ты наверно имел ввиду это ?
www.spyur.am/...​mpanies/ddd-complex/82643

А я про DDD говорил.

Тебе совсем уже спиздануть нечего?

Просто я ещё ДиДиДи не освоил. Начал осваивать от сенсея «любая аппаратура». Правда про любую всё ещё не постиг, но про кипятильники, электрочайники и другие обогреватели кажется уже начал понимать зачем там вебсервер рест апи и лысоватый джавист.
Прошу, не останавливайся.

Ці три стадії не лише REST стосуються, а будь-чого :D

Насправді є ще 4 стадія, коли пишуть свій стандарт

І тут нижче вже згадували що ці стадії можна застосувати до будь чого.

Наприклад використання бібліотек. Пригадаєм старий добрий jquery

1. Спочатку новачки без знать наливного js щодуху використовували jq де треба і не треба, що породжувало інколи меми на стекоферфлоу з питанням як додати 2 числа у джейквері

2. Потім прийшло розуміння і хтось хотів зекономити байти, або попробувати щось зробити самому і почали відмовлятися від бібліотеки

3. Наступним кроком стало розуміння коли бібліотеку всеж доречно використати, тому що знали її сильні та слабкі місця

4. Ну і у випадку повного просвітління і виявлені критичної маси проблем — організовувалися ентузіасти або компанії які почали розвивати альтернативні бібліотеки, описувати документацію кращим способом і робити більш зручний API

Але важливо не плутати 2 і 4 стадії. Дехто просто лінується вивчити апі і починає робити велосипеди, але всім нав’язувати що технологія Х гірша. Тому тут все дуже відносно

А щодо статті, РЕСТ мабуть має право бути використаним. І в цьому немає нічого поганого. Краще хоч якийсь стандарт (порядок) аніж взагалі нічого

Таа, свого часу я пописав трохи кривих велосипедів, котрі, як я собі думав, були RESTful API.

«Пазл зійшовся» коли я відкрив для себе Django REST Framework. А конкретніше — його Generic Views. ListCreateView + RetrieveUpdateDestroyView — дуже хороша основа для правильних REST API, і бонусом — хороший приклад правильного дизайну класів.

За рідкісними вийнятками, в’юхи на цих класах більше подібні на конфігураційні файли, ніж на «справжній» код, багато речей легко реюзати — ідеальний наочний приклад, які _переваги_ ми дістаємо, прийнявши ті _обмеження_, що накладають принципи побудови правильних REST API.

При любых. Просто знать, что на ресте клин светом не сошёлся, что это просто мода и всё. И рест есть в чистом виде натягивание совы на глобус — о чём ты в топике можешь и прочитать.

Есть туева хуча других протоколов, которые можно использовать по любой потребности. Равно как и вертеть на причинном месте конвенции реста, отклоняясь от них ровно настолько, чтобы не нарушить единственное его преимущество: стандартизацию кеширования. Со всем остальным... да хоть сервер-сервер поднимай, хоть p2p, и нефиг слушать тех, кто учит что мир ограничен познаниями учителя.

По-перше, маси на ці нібито стандарти відверто клали, бо навіть не читали. А головне, що користі від того жодної, як і від «правильного» та «стандартизованого» коду. Бо зазвичай під правилами та стандартизацією розуміють БЮРОКРАТИЧНИЙ МАРАЗМ. Аж ніяк не читабельність коду та логічність зв′язків.

Я не кажу нічого поганого про REST. Лише про намагання загнати його усюди, де він взагалі не підходить.

де він взагалі не підходить

Если например, нам нужно ивенты перекидывать, то конечно он не подходит. Но речь в основном не за то.

Но речь в основном не за то.

«Вы не понимаете, это — другое!»

Речь ровно об этом, что REST хорош ровно для своей зоны применения. Пытаться делать через REST всё на свете — есть бюрократический маразм.

Ты опять таблетки не принял?

А вот и адепты бюрокртического маразма. Сорри, у меня к вам иммунитет.
Я код пишу дольше, чем вы баззворды учите, и пережил целые поколения «абсолютно правильных» стандартов, которые отправлялись на помойку после очередного писка моды на новое «правильное» (которое потом внезапно менялось самими авторами).

Я бы ещё понял, когда админы требовали бы от стабильных продуктов рефактора под единообразность и понятность всех точек влияния (управления). Потому что админам нужно поддерживать то, что уже работает, что уже стандартизировано опытом. Но когда про это пишут манки-кодеры, начитавшиеся писанины от писак (даже не кодеров), работающих в супер-мега-FAANGах, а то и в пересказе тех, кто не умеет но учит — я лишний раз убеждаюсь, что не зря получал образование.

Вы уподобляетесь тем, кто верит что владеет информацией, посмотрев телевизор или жёлтые СМИ. Разумеется, лучшей инфой, от лучших «экспертов». Которые в свою очередь не умнее верующих РПЦ. Кстати, а вы опять таблетки приняли? Так вам диллер фуфло подогнал, слабые.

Сорри, у меня к вам иммунитет

У тебя к лекарствам иммунитет )
Иначе бы ты не вываливал через раз в постах малосвязный поток сознания, не коррелирующий с темой разговора.

Чтобы удовлетворить тебя, который не способен прочесть несколько строчек и собственная мысль не длиннее твита? Не интересна тема — не обсуждай. Сам-то ты ничего не предлагаешь, просто показываешь какой ты токсичный

Вообще-то, Дмитрий приводил выше целый аргументированный список сценариев, в которых REST вполне подходит, а также примеры задач, в которых REST будет не лучшим выбором. А что мы видим от вас?

адепты бюрокртического маразма.
ЖОПА — универсальный интерфейс. Потому что сделать через жопу можно всё что угодно.

И, знаете, возникает вопрос — кто ещё токсичный в этой беседе?

Повторю ещё раз: проблема не в REST, а в анонировании на REST, и попытках типичным случаями (где он полезен) доказать что он вообще универсален для всего.

Хотя вся суть REST — тупо в ограничениях. И оправданы они ничем.

REST не то, чтобы универсален для всего, просто для ряда задач альтернативы не сильно лучше.

JSON-RPC с немалой вероятностью породит в неопытных руках чудовищные велосипеды, а в GraphQL при, опять же, не очень прямых руках, очень легко наделать серьёзных дыр в безопасности.

а в GraphQL при, опять же, не очень прямых руках, очень легко наделать серьёзных дыр в безопасности.

Существует два основных подхода:
1. Трехзвенка, которая пытается фиксить все проблемы безопасности, секурити, валидации в своем толстом слое бизнесс логики и ограниченом апи.
2. Двухзвенка (или как частный случай таже трехзвенка, но с тонким слоем бизнесс логики).

Так вот нужно понимать исторические предпосылки появления трехзвенки. Если у тебя достаточно примитивная база данных, которая не может обеспечить секурити, валидацию и другие фичи, то ты эту ф-сть неизбежно переносишь в бизнесс логику. И это дыра, потому что никто лучше не может заботится о безопасности и консистентности данных, кроме как база данных.

Поэтому по безопасности.
1. Связка примитивная база данных которая не заботится о секурити данных + GraphQL = это низкий уровень безопасности
2. Связка примитивная база данных которая не заботится о секурити данных + трехзвенка c 100500 методов REST = это средний уровень безопасности.
3. Связка база данных которая заботится о секурити данных и консистентности данных + GraphQL = это высокий уровень безопасности, в котором тяжело поломать или прочитать секурные данные не то что через интерфейсы, а даже через баги в приложении или запуск скриптов на стороне.

И по обьему кода.
1. Связка примитивная база данных которая не заботится о секурити данных + GraphQL = мало кода.
2. Связка примитивная база данных которая не заботится о секурити данных + трехзвенка c 100500 методов REST = много много кода
3. Связка база данных которая заботится о секурити данных и консистентности данных + GraphQL = мало кода

база данных которая заботится о секурити данных и консистентности

Возможно, месье желает рассказать, как реализовать RBAC на уровне базы? И много ли движков БД умеют в такое? А бесплатных?

Точнее, в приличных БД-то есть понятие ролей, вот только не очень понятно, как подвязать к этим ролям пользователей, которые аутентифицируются через какой-нибудь OAuth.

Справедливости ради, вот тут пытались делать нечто подобное:
www.2ndquadrant.com/...​rs-vs-row-level-security

но это RLS, которой вся безопасность не ограничивается...и этот подход требует постоянного наличия пароля, что не выполняется в случае того же OAuth.

Возможно, месье желает рассказать, как реализовать RBAC на уровне базы? И много ли движков БД умеют в такое?

Все меинстримовые RDMBS умеют в роли (Оракл, МС СКЛ, Постгре и др)

которые аутентифицируются через
какой-нибудь OAuth.

Да вроде как архитектурный шаблон не запрещает этим заниматься базе данных. На вскидку погуглил, в Оракл например есть целые мануалы по OAuth. Но подробней не подскажу, поскольку это не мой стек разработки.

Меня больше интересует так сказать история вопроса. А история такова, что подходы (Graph)QL намного более зрелые, гибкие и простые в разработке чем REST.
А REST это просто костыль, который был необходим трехзвенке, которая забрала на себя слишком много функциональности из СУБД.

Таких исторических казусов вообще не мало. Вот например взять двигатели внутреннго сгорания и электромобили. История ушла в ДВС по своим причинам еще в начале 19го столетия. В итоге, под капотом появились радиаторы, стартеры, генераторы, свечи зажигания, турбины, надувы, цилиндры, поршни, шрусы, коропки передач, карданы, замена масла каждые 10 тыс, сцепление и еще 100500 всякого барахла под капотом.
А потом вышли на сцену давно забытые электромобили, где мотор можно встроить вообще в колесо, контроллер под торпедой, «коробка автомат» по дефолту и разгон за 4 секунды.

Ничего не напоминает ?

А история такова, что подходы (Graph)QL намного более зрелые, гибкие

Чему равно число пи в той вселенной, откуда ты к нам выпал?

А REST это просто костыль, который был необходим трехзвенке, которая забрала на себя слишком много функциональности из СУБД.

то, что ты со всех сторон безграмотен, мы уже поняли, хватит об этом напоминать.

Тебе нужно научится думать. А потом писать сообщения.

А потом вышли на сцену давно забытые электромобили, где мотор можно встроить вообще в колесо, контроллер под торпедой, «коробка автомат» по дефолту и разгон за 4 секунды.

Электромобили прошли огромный технологический путь, прежде, чем достичь такого уровня. По сути, до появления литий-ионных аккумуляторов большой ёмкости любой транспорт на автономной электротяге был очень нишевым. И, даже на сегодняшний день, электромобили до сих пор не стали полноценной заменой классическому автомобилю с ДВС из-за маленького запаса хода и долгой зарядки (как минимум, это крайне неудобно городским жителям многоэтажек, которым бывает проблематично обеспечить себе личную зарядную станцию).

А REST это просто костыль, который был необходим трехзвенке, которая забрала на себя слишком много функциональности из СУБД.

Развитие пошло по этому пути совсем не случайно. Делать сложные вещи на уровне СУБД в начале 2000х было адским адом. Я ещё помню эти времена хранимых процедур на три экрана кода без нормального контроля версий и отладки, хоть и застал их зелёным джуном.

В последние несколько лет, да, были некие подвижки к утоньшению прослойки между БД и API, та же Hasura — но, массово пока не взлетает именно потому, что не всякую логику и функциональность имеет смысл пихать в БД, жертвуя удобством деплоя, юнит-тестами, масштабируемостью, и ещё Бог знает чем.

Электромобили прошли огромный технологический путь, прежде, чем достичь такого уровня.

Если бы в электро вливали столько денег, сколько вливали нефтяные корпорации в ДВС, то этот путь прошли бы намного быстрее.

Развитие пошло по этому пути совсем не случайно. Делать сложные вещи на уровне СУБД в начале 2000х было адским адом. Я ещё помню эти времена хранимых процедур на три экрана кода без нормального контроля версий и отладки, хоть и застал их зелёным джуном.

Многие эти проблемы уже исправлены. Остальную часть предстоит исправить.

жертвуя удобством деплоя, юнит-тестами, масштабируемостью, и ещё Бог знает чем.

Это все отголоски ДВС. Радиаторы, стартеры, сцепление и тд тп.
Их может не быть на проекте вообще, как часть инфраструктуры которая больше не нужна.
Например тот же хот фикс процедуры (он же деплой) в СУБД без рестарта (!) сервера проводился за 5 секунд еще в 2000х годах.

Например тот же хот фикс процедуры (он же деплой) в СУБД без рестарта (!) сервера проводился за 5 секунд еще в 2000х годах.

Да, проводился. И сайты деплоили по FTP. Потому, что требования к сложности, надёжности, автоматизации развёртывания, скорости выкатки новых фич и масштабируемости систем двадцать лет назад были совсем другими — по крайней мере, если не брать банки, системы резервирования авиабилетов и прочие mission-critical вещи.

Continuous deployment, canary releases, blue / green deployment — тогда таких терминов вообще никто не знал; даунтаймы по несколько часов — пусть даже не в бизнес-время — были вполне себе нормой.

даунтаймы по несколько часов — пусть даже не в бизнес-время — были вполне себе нормой

Не было нормой.

Например тот же хот фикс процедуры (он же деплой) в СУБД без рестарта (!) сервера проводился за 5 секунд еще в 2000х годах.

Здесь нужно еще понимать, что сложность многих современных приложений обсусловлена подходами в их проектировании. Одни проблемы тянут другие. Те третьи. И далее по кругу.

Continuous deployment, canary releases, blue / green
deployment

Это все крассивые аббревиатуры. Но по факту, как я писал, фикс накатывался за 5 секунд. Устроить тот же канареечный тест можно было буквально за минуты, а потом откатить все как было, без рестарта, без редеплоя. Zero down time при нулевых усилиях разработчиков/девопсов/админов.

Более того. Серьезный канареечный тест, возможен только в инфраструктуре где поврежденные данные можно будет быстро откатить в исходную точку. Получается нормальную канарейку можно провести только на уровне СУБД.

Не было нормой.

Было. Даже всемирно известные сайты, бывало, уходили в даунтайм на несколько часов, и ничего — мир не рушился. Потому, что тогда не было ожидания, что любой интернет-сервис (ключевое слово — Интернет, хотя, для НЕ mission critical корпоративных систем требования были ещё ниже) будет работать бесперебойно, как часы.

А сейчас пользователь ожидает получить результат мгновенно и 24/7. И если ты не можешь этого дать — пользователь уйдёт к конкурентам (которых в начале 2000х тоже было сильно меньше).

Здесь нужно еще понимать, что сложность многих современных приложений обсусловлена подходами в их проектировании.

А эти подходы, в свою очередь, обусловлены бизнес-требованиями (в первую очередь, нефункциональными), которые сильно поменялись за последние 20 лет.

Устроить тот же канареечный тест можно было буквально за минуты, а потом откатить все как было, без рестарта, без редеплоя.

Нельзя, потому что смысл канареечного теста — провести его на подмножестве пользователей

Было. Даже всемирно известные сайты, бывало, уходили в даунтайм на несколько часов, и ничего — мир не рушился. Потому, что тогда не было ожидания, что любой интернет-сервис (ключевое слово — Интернет, хотя, для НЕ mission critical корпоративных систем требования были ещё ниже) будет работать бесперебойно, как часы.

Не знаю что там уходило в даунтайм, но могу сказать что чем сложнее и навороченее (кстате неоправданно) система, тем проще ей выйти из строя. Кто работает с микросервисами тот поймет. На половину не работающая система в порядке вещей. Может редко на прод, но на всяких препрод и стеджах просто постоянно. Когда были монолиты, такое было крайне редко.

А эти подходы, в свою очередь, обусловлены бизнес-требованиями (в первую очередь, нефункциональными), которые сильно поменялись за последние 20 лет.

Что за требования ?

Нельзя, потому что смысл канареечного теста — провести его на подмножестве пользователей

Впиливались ифы, если такой-то пользователь или группа, то такая то ветка логики в базе аж бегом. Просто тогда никто не знал что это канарейка.

чем сложнее и навороченее (кстате неоправданно) система, тем проще ей выйти из строя. Кто работает с микросервисами тот поймет.

С этим я, как раз, не спорю. Чем больше возможных точек отказа, тем ниже надёжность. И, именно по этой причине, я ни сном ни духом не упоминал именно о микросервисах.

Что за требования ?

Выше описывал, но повторю вкратце основные моменты: скорость выкатки новых фич, гибкость архитектуры для быстрого проведения всяких канареечных и A/B тестов маркетологами, доступность 24/7/365

Впиливались ифы, если такой-то пользователь или группа, то такая то ветка логики в базе аж бегом. Просто тогда никто не знал что это канарейка.

Опять же, я не спорю, что технически нечто подобное можно было сделать и средствами БД. Вопрос только в вовлечении того же DBA на каждый такой тест и цене ошибки при «закате солнца вручную».

никто лучше не может заботится о безопасности и консистентности данных, кроме как база данных

ты опять не принял таблетки и несешь ахинею? рядом с тобой вообще есть санитары?

Вот же клоун, не угомонится.
Простой тебе тест.
Снимаешь на базе все констреинты. Оставляешь базу на какое-то время. Через пол года приглашаешь базовика наложить констреинты обратно или скриптами проверить битые ссылки. Много думаешь.

Как ты думаешь, почему не так давно был бум на NoSQL schemaless базы данных (а в классические реляционные начали активно добавлять поддержку JSON полей)?

А именно потому, что описанный тобой подход с констрейнтами хорошо работает только тогда, когда требования к модели данных очень хорошо известны и стабильны.

В современном высококонкурентном мире это — скорее, исключение, нежели правило.

Как ты думаешь, почему не так давно был бум на NoSQL schemaless базы данных

Здесь ключевое слово «был». НоуСкл имеет гарантировано проблемы с консистентностью данных. Поэтому хайп сильно спал, а монго начали срочно докручивать хотябы валидацию полей.

именно потому, что описанный тобой подход с констрейнтами хорошо работает только тогда, когда требования к модели данных очень хорошо известны и стабильны

А как твой код собрался работать если требования к модели неизвестны и не стабильны ?
Или ты считаешь великое благо в поле скажем Name случайно записать пару гигабайт, а в поле ID null даже если это json

А как твой код собрался работать если требования к модели неизвестны и не стабильны ?

Код обрабатывает все возможные инварианты, только и всего.

НоуСкл имеет гарантировано проблемы с консистентностью данных

Это слишком громкое заявление. Вначале предлагаю ознакомиться, для каких задач придумывался NoSQL, и с CAP теоремой, в частности.

Более того, и без распределённых сценариев с консистентностью там всё ОК, если не пытаться требовать от NoSQL базы того, для чего она изначально не была предназначена.

Или ты считаешь великое благо в поле скажем Name случайно записать пару гигабайт, а в поле ID null

Если специально не стараться выдумывать абсурдные примеры, то та же возможность делать поля опциональными без риска нарваться на адок, вызванный противоречиями уже накрученных на базу constraints c новыми данными — да, может быть великим благом.

Код обрабатывает все возможные инварианты, только и всего.

Раньше обрабатывала СУБД, а теперь пишет девелопер свой велосипед с валидацией. И поскольку девелоперы пишут с багами, то все баги инвариантов непременно окажутся в данных. А ещё кто-то запустил миграцию данных сбоку и конечно в обход всех валидаций, которые прописаны в бизнесс логике. Ещё раз — единственное нормальное место валидации данных — это СУБД. Это было очевидно ещё в 70х годах

Это слишком громкое заявление. Вначале предлагаю ознакомиться, для каких задач придумывался NoSQL, и с CAP теоремой, в частности.

НоуСкл задумывался для ускорения работы приложения в ущерб всему остальному. Читай в том числе проверок на консистентность данных.
Про кап теорему вам следует вспомнить модификации CA CP где предпочтение отдается консистентности данных. Ее никто не отменял в распределенных системах.

Если специально не стараться выдумывать абсурдные примеры

Валидации это вообще-то верхушка айсберга. Дальше там идёт секурити, внешние ключи и др
И примеры вполне себе валидны. Покажите мне девелопера хотябы с годом опыта работы, который не копал баги связанные с закорапчеными данными.

Ещё раз — единственное нормальное место валидации данных — это СУБД. Это было очевидно ещё в 70х годах

А где-то в 2010х стало очевидно, что строгость валидации данных и ригидность схемы данных в традиционных СУБД создаёт больше проблем, чем пользы там, где требования в процессе разработки продукта могут меняться на 180 градусов. А это — реалии чуть ли не всей современной продуктовой разработки, за исключением mission critical систем.

НоуСкл задумывался для ускорения работы приложения в ущерб всему остальному.

А также и для ускорения разработки приложения в ущерб многим другим вещам. Идеально спроектированное приложение с целостностью внешних ключей, секюрити, валидацией всего чего только можно на уровне схемы БД — окажется попросту ненужным, поскольку пока его будут пилить, конкуренты сделают MVP из говна и палок и захватят рыночную нишу.

поскольку пока его будут пилить, конкуренты сделают MVP из говна и палок и захватят рыночную нишу.

Какже они захватят, если топик о Рест, о мапперах, о дто и прочьих технологиях, которые черезмерно усложняют проект.
Пока Бугай будет пилить свой апдейт джисона через два фреймворка и наследование, конкуренты быстренько соберут прототип на СУБД и получат намного более надежное и гибкое решение.

Поскольку как не крути, чем меньше кода в проекте, тем гибче код. Вплоть до того, что его можно просто полностью переписать быстрее, чем поддерживать лапшу «правильной» архитектурой с кучей ненужных компонент.

Таких случаев, в целом, нет. Потому что рест это просто способ описать операции по управлению состояниям. Это универсальная концепция.

Я так и сказал: ЖОПА — универсальный интерфейс. Потому что сделать через жопу можно всё что угодно. Всё лучшее, что есть не только в IT, а вообще — специфично. И ровно потому лучше, что идеально работает в частных случаях, не пытаясь уничтожить сами кейзы.

Грубо говоря, я из тех идиотов, кто видит разницу между микроволновкой и индукционной плитой. А если бы цивилизацией правили бюрократы, то единым стандартом был бы огонь. И был бы единственно верный способ, как добывать его трением палочки.

Які є конвенції в REST API та для чого їх дотримуватись

REST ЖОПА есть интерфейс, и да не будет у тебя иных интерфейсов кроме неё.

До речі, як за допомогою REST API архітектури залогінитись? Тут же начебто не створюється ресурс, окрім хіба що сесії.

До речі, як за допомогою REST API архітектури залогінитись?

restcookbook.com/Basics/loggingin

Але саме формулювання «за допомогою Х API архітектури виконати_дію» звучить трохи дивно. Ще треба розуміти, що РЕСТ АПІ та РЕСТ архітектурний стиль — це трохи різні речі (хоча і тісно пов’язані), особливо якщо під РЕСТ АПІ розуміють ХТТП АПІ

Цей розділ про те, як отримати закриті ресурси, і про те як передавати дані для автентифікації/авторизації. А як залогінетись? По якому URL треба слати запит?

А як залогінетись? По якому URL треба слати запит?

У відповідності до OAuth, OAuth2 або того стандарту (скоріше навіть імплементації), який ви використовуєте.
Аутентифікація/авторизація — це ідеологічно процес, тут не потрібно натягувати сову на глобус, а скоріше слідувати гайдлайнам провайдера. Наприклад, іноді в САМЛі використовують гет запити. Хоча для РЕСТ типова історія це емуляція РПС на основі пост запитів.

Тут же начебто не створюється ресурс, окрім хіба що сесії.

1) Сесія існує не у всіх ААА протоколах
2) Щодо РЕСТ, то треба відучитись думати поняттями КРУД. Ніхто нічого не створює, є ресурс ви можете відправити йому щось або запросити у нього щось.

Дякую за статтю, цікава, добре написана. Тільки код краще вставляти у pre

Статья базовая, и в целом, почти теми же фразами дублируется в интернете в сотнях вариантов.

И ни в одной из подобных статей подробно не объясняется суть понятия «представление». А еще почти у всех почему-то возникает проблема, когда нужно создать рест интерфейс к каким-то асинхронным процессам на сервере.

А еще глобальная проблема реста в том, что даже эти простые концепции оказываются слишком сложными для большинства девелоперов. И, как и в

іноді неправильні варіанти є у відомих проєктах

в большинстве проектов валят полную дичь без какого-либо дизайна и структуры. А для некоторых рест стал заменой хттп и они даже не ловят разницу между этими понятиями.

И ни в одной из подобных статей подробно не объясняется суть понятия «представление».

Це треба читати дисертацію Філдінга :)

А еще почти у всех почему-то возникает проблема, когда нужно создать рест интерфейс к каким-то асинхронным процессам на сервере.

Є стандартне рішення restcookbook.com/...​asynchroneous-operations

А еще глобальная проблема реста в том, что даже эти простые концепции оказываются слишком сложными для большинства девелоперов.

Угу, досить часто все закінчується RPC over HTTP, аналогічно як часто ооп закінчується купою статичних методів.

стандартне рішення restcookbook.com/...​asynchroneous-operations

я не про це, а про моделювання апи до даних, які не є статичними по своїй природі.

я не про це, а про моделювання апи до даних, які не є статичними по своїй природі.

Ну дуже зрозумів. Якщо ви про проблему, яку описали в dou.ua/...​rums/topic/34550/#2216605

То це скоріше про відображення потоку даних і якщо вам потрібно отримувати їх не в історичному режимі (дай мені дані в проміжку Т1-Т2), то краще використовувати стрімінгові протоколи, наприклад вебсокети (або інші сокетоподібні, іноді специфічні для галузі, як то FIX). Не треба натягувати сову на глобус.

Ні, я знову таки не про це. І не про «відкладену відповідь» на запит.
Припустимо, ваш сервіс виконує якийсь пул тривалих робіт у бекграунді. Потрібен інтерфейс для запуску, паузи, зупинки, перевірки статусу, внутрішнього стану, зміни параметрів процесу тощо.

Неодноразово чув що «рест не підходить». На питання чому з’язовується що єдина причина в тому що для багатьох слово «рест» є виключно синонімом «веб круд до таблички» і потреба описати інтерфейс складнішого домену викликає розрив шаблонів і несвідомі конвульсії в сторону графкуєль в надії що складність і фічі фреймворку компенсують нездатність моделювати апі.

Припустимо, ваш сервіс виконує якийсь пул тривалих робіт у бекграунді. Потрібен інтерфейс для запуску, паузи, зупинки, перевірки статусу, внутрішнього стану, зміни параметрів процесу тощо.

Власне для цього використовують той паттерн, на який я навів посилання
restcookbook.com/...​asynchroneous-operations
В кукбуках зазвичай дають простостий приклад (умовний БЕ для прогресбара), але він чудово мапиться на описану вами задачу.

Неодноразово чув що «рест не підходить».

Грустішка, бо рест якраз ідеально підходить для сценарія, який ви описали. Маніпулювання станом розподіленого об’єкту — це один з головних сценаріїв.

Іноді є варіанти, які я б назвав workarounds:

Почему воркераунд?

PUT /books/100/activated
{"value": true}
Поле ресурса является легитимным под-ресурсом, гранулированным доступом к части состояния аггрегата.
Поэтому шаблон
PUT /objects/{id}/{property}
{ ...json... }
Вполне в духе реста. PUT можно заменить на PATCH. Некоторые пуристы считают что PUT можно использовать только для добавления ресурсов в коллекции.

Так і робимо зазвичай. Зручно, що можна на різні проперті повісити різні перевірки пермішенів.

Курси валют по API (Minfin for Developers) зроблені саме за стандартом REST, ражду тестувати та використовувати: minfin.com.ua/developers/api

Курси валют по API (Minfin for Developers) зроблені саме за стандартом REST,

Я розумію, що ви продаван, а не технічна людина, але проконсультувались би з девелоперами перед тим як позоритись, дизайн АПІ рівня лабораторна по вебу:
api.minfin.com.ua/mb[ключ]/[ГГГГ-ММ-ДД]/?currency=[код-валюты]

ключ — не має бути частиною ідентифікатора ресурса
currency=[код-валюты] — власне демонструє, що курси не розбиті по ресурсам, а є просто ділька уберресурсів в яких щось шукають, зробили б вже графкуел
[ГГГГ-ММ-ДД] — потенційні проблеми з гранулярністю, не дозволяє відстежувати історію протягом дня

[ГГГГ-ММ-ДД] — потенційні проблеми з гранулярністю, не дозволяє відстежувати історію протягом дня

Именно!
Я когда-то работал с говноапи самого НБУ, и там та же проблема. Возможно, именно поэтому здесь сделано так же, это же просто прокся.
НБУ мог менять курс в течении суток, при этом таймстемпа и истории данных нету, и потом в истории НБУ один курс за дату Х, а ты стянул в аппку другой курс за ту же дату Х. Делаешь запрос — и у тебя другой курс, за ту же дату.
Ужасы отечественного апистроения.

зроблені саме за стандартом REST

Писдежь.
«Апи» по ссылке — типичный треш, не имеющий к ресту никакого отношения. Кстати, довольно образцовый, из палаты мер и весов, стенд «кое-как названные урлы, безосновательно объявленные рестом»

/mb/{userKey}/
/api/nbu/{userKey}/{date}/
/api/auction/info/{userKey}/

Это рест ендпойнты? Трешняк это а не рест.

Ох.

Эти чуваки не могут нормально и единообразно обработать комментарий создаваемый или редактируемый посетителем сайта. Какие у них могут быть ресты? Очевидно что рукожопные что выше и демонстрирует Богдан и Дмитрий.

Підписатись на коментарі