ВІДПУСК - вкладати посвідчення в тіло чи ні?


96

Скажімо, я хочу мати ресурс RESTful для людей, де клієнт може призначити ідентифікатор.

Людина виглядає так: {"id": <UUID>, "name": "Jimmy"}

Тепер, як клієнт повинен його зберегти (або "ВСТАНОВИТИ")?

  1. PUT /person/UUID {"id": <UUID>, "name": "Jimmy"} - тепер у нас є це неприємне дублювання, яке нам доводиться постійно перевіряти: Чи відповідає ідентифікатор в тілі тому, що на шляху?
  2. Асиметричне представлення:
    • PUT /person/UUID {"name": "Jimmy"}
    • GET /person/UUID повертається {"id": <UUID>, "name": "Jimmy"}
  3. У тілі немає ідентифікаторів - ідентифікатор лише у місцезнаходженні:
    • PUT /person/UUID {"name": "Jimmy"}
    • GET /person/UUID повертається {"name": "Jimmy"}
  4. Ніщо не POSTздається гарною ідеєю, оскільки ідентифікатор генерується клієнтом.

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

Відповіді:


62

Немає нічого поганого в тому, щоб мати різні моделі читання / запису: клієнт може написати одне представлення ресурсу, де після того, як сервер може повернути інше подання з доданими / обчисленими елементами (або навіть зовсім інше подання - нічого проти цього немає , єдина вимога полягає в тому, що PUT повинен створити або замінити ресурс).

Тому я б пішов на асиметричне рішення в (2) і уникав "неприємної перевірки дублювання" на стороні сервера під час написання:

PUT /person/UUID {"name": "Jimmy"}

GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}

2
І якщо ви застосовуєте набір тексту (статичний або динамічний), ви не можете безболісно мати моделі без ідентифікатора ... Тож набагато простіше видалити ідентифікатор із URL-адреси для запитів PUT. Це не буде "спокійно", але буде правильно.
Іван Клешнін

2
Зберігайте додаткові TO без, idразом з TO з ідентифікатором та суттю, додаткові перетворювачі та занадто великі накладні витрати для програмістів.
Григорій Кіслін,

Що робити, якщо я отримаю посвідчення особи від BODY, напр .: PUT / person {"id": 1, "name": "Jimmy"}. Це була б погана практика?
Бруно Сантос,

Покласти посвідчення особи в тіло було б добре. Використовуйте GUID для ідентифікатора замість цілого числа - інакше ви ризикуєте повторити ідентифікатори.
Jørn

Це неправильно. Дивіться мою відповідь. PUT повинен містити весь ресурс. Використовуйте PATCH, якщо ви хочете виключити ідентифікатор та оновити лише частини запису.
CompEng88

27

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

Під цим я маю на увазі, що ви повинні підтримувати і 1, і 2. Я згоден, що 3 не має сенсу.

Спосіб підтримувати як 1, так і 2 - отримати ідентифікатор з URL-адреси, якщо жоден не вказаний у тілі запиту, а якщо він є в тілі запиту, то перевірте, чи відповідає він ідентифікатору в URL-адресі. Якщо ці два збіги не збігаються, поверніть відповідь 400 Помилковий запит.

Повертаючи ресурс людини, будьте консервативними і завжди включайте ідентифікатор у json, хоча він не є обов’язковим для путу.


3
Це має бути прийнятим рішенням. API завжди повинен бути зручним для користувача. Це має бути необов’язково в організмі. Я не повинен отримувати ідентифікатор від POST, а потім повинен робити його невизначеним у PUT. Крім того, 400 пунктів відповіді зроблено прямо на.
Майкл

Близько 400 кодів див. Також обговорення softwareengineering.stackexchange.com/questions/329229/… . Коротше, код 400 не є невідповідним, просто менш конкретним, порівняйте з 422.
Григорій Кіслін,

8

Одне з вирішень цього питання включає дещо заплутану концепцію "Гіпертекст як двигун стану програми" або "HATEOAS". Це означає, що відповідь REST містить доступні ресурси або дії, які слід виконати як гіперпосилання. Використовуючи цей метод, який був частиною початкової концепції REST, унікальні ідентифікатори / ідентифікатори ресурсів самі по собі є гіперпосиланнями. Так, наприклад, у вас може бути щось на зразок:

GET /person/<UUID> {"person": {"location": "/person/<UUID>", "data": { "name": "Jimmy"}}}

Потім, якщо ви хочете оновити цей ресурс, ви можете зробити (псевдокод):

updatedPerson = person.data
updatedPerson.name = "Timmy"
PUT(URI: response.resource, data: updatedPerson)

Однією з переваг цього є те, що клієнт не повинен мати уявлення про внутрішнє представлення сервера ідентифікаторів користувачів. Ідентифікатори можуть змінюватися, і навіть самі URL-адреси можуть змінюватися, якщо у клієнта є спосіб їх виявити. Наприклад, отримуючи колекцію людей, ви можете повернути таку відповідь:

GET /people
{ "people": [
    "/person/1",
    "/person/2"
  ]
}

(Ви, звичайно, можете також повернути об'єкт з повною особою для кожної людини, залежно від потреб програми).

За допомогою цього методу ви думаєте про свої об'єкти більше з точки зору ресурсів та розташування, а менше з точки зору ідентифікатора. Таким чином, внутрішнє подання унікального ідентифікатора відокремлено від логіки вашого клієнта. Це було початковим поштовхом до REST: створити архітектури клієнт-сервер, які є більш вільно пов'язаними, ніж системи RPC, що існували раніше, за допомогою функцій HTTP. Щоб отримати додаткову інформацію про HATEOAS, перегляньте статтю Вікіпедії , а також цю коротку статтю .


4

У вставці не потрібно додавати ідентифікатор у URL-адресу. Таким чином, якщо ви надсилаєте ідентифікатор в PUT, ви можете інтерпретувати це як ОНОВЛЕННЯ для зміни первинного ключа.

  1. ВСТАВИТИ:

    PUT /persons/ 
      {"id": 1, "name": "Jimmy"}
    HTTP/1.1 201 Created     
      {"id": 1, "name": "Jimmy", "other_field"="filled_by_server"}
    
    GET /persons/1
    
    HTTP/1.1 200 OK
      {"id": 1, "name": "Jimmy", "other_field"="filled_by_server"}  
    
  2. ОНОВЛЕННЯ

    PUT /persons/1 
         {"id": "2", "name": "Jimmy Jr"} - 
    HTTP/1.1 200 OK
         {"id": "2", "name": "Jimmy Jr", "other_field"="filled_by_server"}
    
    GET /persons/2 
    
    HTTP/1.1 200 OK
         {"id": "2", "name": "Jimmy Jr", "other_field"="updated_by_server"}
    

JSON API використовує цей стандарт і вирішують деякі проблеми , які повертаються вставлений або оновлений об'єкт з посиланням на новий об'єкт. Деякі оновлення або вставки можуть містити певну бізнес-логіку, яка змінить додаткові поля

Ви також побачите, що можете уникнути отримання після вставки та оновлення.


3

Це вже задавали раніше - обговорення варто подивитися:

Чи має відповідь RESTful GET повернути ідентифікатор ресурсу?

Це одне з тих питань, де легко вплутатись у суперечки щодо того, що є, а що не "ВІДПОВІДНО" .

Для чого це варте, я намагаюся думати з точки зору послідовних ресурсів, а не змінювати їх дизайн між методами. Однак IMHO найголовніше з точки зору юзабіліті - це те, щоб ви були послідовними у всьому API!


2

Хоча нормально мати різні уявлення для різних операцій, загальною рекомендацією для PUT є вміщення ЦІЛЬКО корисного навантаження . Це означає, що це idповинно бути і там. В іншому випадку слід використовувати PATCH.

Сказавши це, я думаю, що PUT переважно повинен використовуватися для оновлень, а також idзавжди повинен передаватися в URL-адресі. В результаті використання PUT для оновлення ідентифікатора ресурсу є поганою ідеєю. Це залишає нас у небажаній ситуації, коли idURL-адреса може відрізнятися від idтіло в тілі.

Отже, як нам вирішити такий конфлікт? В основному у нас є 2 варіанти:

  • викиньте 4XX виняток
  • додати заголовок Warning( X-API-Warnі т.д.).

Це якомога ближче до відповіді на це запитання, оскільки тема загалом є питанням думки.


2

Тільки FYI, відповіді тут неправильні.

Побачити:

https://restfulapi.net/rest-api-design-tutorial-with-example/

https://restfulapi.net/rest-put-vs-post/

https://restfulapi.net/http-methods/#patch

ВСТАНОВИТИ

Використовуйте API PUT в першу чергу для оновлення існуючого ресурсу (якщо ресурс не існує, тоді API може вирішити створити новий ресурс чи ні). Якщо за допомогою API PUT створено новий ресурс, початковий сервер ПОВИНЕН інформувати агента користувача за допомогою відповіді коду відповіді HTTP 201 (Створено), а якщо існуючий ресурс модифікований, то 200 (OK) або 204 (Без вмісту) коди відповідей ПОВИННІ надсилатись, щоб вказати на успішне заповнення запиту.

Якщо запит проходить через кеш, а Request-URI ідентифікує одну або кілька кешованих сутностей, ці записи ПОВИННІ розглядатися як застарілі. Відповіді на цей метод не можна кешувати.

Використовуйте PUT, коли ви хочете змінити окремий ресурс, який уже є частиною збору ресурсів. PUT повністю замінює ресурс. Використовуйте PATCH, якщо запит оновлює частину ресурсу.

ЛІП

Запити HTTP PATCH мають частково оновити ресурс. Якщо ви бачите, що запити PUT також модифікують сутність ресурсу, щоб зробити це більш зрозумілим - метод PATCH є правильним вибором для часткового оновлення існуючого ресурсу, і PUT слід використовувати лише в тому випадку, якщо ви замінюєте ресурс повністю.

Тому ви повинні використовувати його таким чином:

POST    /device-management/devices      : Create a new device
PUT     /device-management/devices/{id} : Update the device information identified by "id"
PATCH   /device-management/devices/{id} : Partial-update the device information identified by "id"

Практики RESTful вказують, що не має значення, що ви ВСТАНОВИТИ в / {id} - вміст запису має бути оновлений до того, що надається корисним навантаженням, але GET / {id} все одно має посилатися на той самий ресурс.

Іншими словами, PUT / 3 може оновитись до ідентифікатора корисного навантаження до 4, але GET / 3 все одно повинен посилатись на те саме корисне навантаження (і повертати той із ідентифікатором, встановленим на 4).

Якщо ви вирішили, що ваш API вимагає однакових ідентифікаторів в URI та корисному навантаженні, ваша робота - переконатися, що він збігається, але обов’язково використовуйте PATCH замість PUT, якщо ви виключаєте ідентифікатор із корисного навантаження, який повинен бути там повністю . Тут прийнята відповідь помилилася. PUT повинен замінити весь ресурс, де-як виправлення може бути частковим.


1

У використанні різних підходів немає нічого поганого. але я думаю, що найкращий спосіб - це рішення з 2-м .

 PUT /person/UUID {"name": "Jimmy"}

 GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}

він в основному використовується таким чином, навіть сутність сутності використовує цю техніку, коли сутність додається в dbContext, клас без згенерованого ідентифікатора є ідентифікатором, сформованим за допомогою посилання в Entity Framework.


1

Я дивлюся на це з точки зору JSON-LD / Semantic Web, тому що це хороший спосіб досягти справжньої відповідності REST, як я зазначив у цих слайдах . Дивлячись на це з цієї точки зору, немає жодних сумнівів у виборі варіанту (1.), оскільки ідентифікатор (IRI) веб-ресурсу завжди повинен бути рівним URL-адресі, яку я можу використати для пошуку / розмежування ресурсу. Я думаю, що перевірку насправді не складно здійснити, ані вона обчислювально інтенсивна; тому я не вважаю це вагомою причиною переходу до варіанту (2.). Я думаю, що параметр (3.) насправді не є варіантом, оскільки POST (створити новий) має іншу семантику, ніж PUT (оновлення / заміна).


0

Можливо, вам доведеться вивчити типи запитів PATCH / PUT.

Запити PATCH використовуються для часткового оновлення ресурсу, тоді як у запитах PUT вам потрібно відправити весь ресурс там, де він перевизначається на сервері.

Що стосується наявності ідентифікатора в URL-адресі, я думаю, ви повинні мати його завжди, оскільки це стандартна практика ідентифікації ресурсу. Навіть Stripe API працює так.

Ви можете використовувати запит PATCH для оновлення ресурсу на сервері з ідентифікатором для його ідентифікації, але не оновлювати фактичний ідентифікатор.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.