Чи варто використовувати PATCH або PUT в моєму API REST?


274

Я хочу створити свою кінцеву точку спокою за допомогою відповідного методу для наступного сценарію.

Є група. Кожна група має статус. Група може бути активована або інактивована адміністратором.

Чи повинен я визначити свою кінцеву точку як

PUT /groups/api/v1/groups/{group id}/status/activate

АБО

PATCH /groups/api/v1/groups/{group id}

with request body like 
{action:activate|deactivate}

1
Обоє добре. Але подивіться на RFC для формату JSON PATCH ( tools.ietf.org/html/rfc6902 ). PATCH розраховує отримати якийсь документ diff / patch для корисного навантаження (і JSON не є одним із них).
Jørn Wildt

1
@ JørnWildt ні, PUT був би жахливим вибором. Що ти туди кладеш? PATCH - єдиний розумний варіант. Ну, у цьому випадку ви можете використовувати формат PATCH, представлений у запитанні, та просто використовувати метод PUT; приклад PUT просто неправильний.
thecoshman

3
Немає нічого поганого в розкритті однієї або декількох властивостей як самостійних ресурсів, які клієнт може отримати та змінити за допомогою PUT. Але, так, URL-адреса повинна бути / groups / api / v1 / groups / {group id} / статус, до якого ви можете ВИМКАТИ "активний" або "неактивний" або GET, щоб прочитати поточний стан.
Jørn Wildt

3
Ось гарне пояснення, як реально використовувати PATCH: williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot
rishat

4
" activate" не є адекватною конструкцією REST. Ви, ймовірно, намагаєтесь оновити значення " statusдо активного" чи "дилективного". в такому випадку ви можете ЗАТВОРИТИ .../statusза допомогою "активного" або "делективного" рядка в тілі. Або якщо ви намагаєтесь оновити булеву програму на status.active, ви можете ЗАТВЕРДИТИ .../status/activeз булевим тілом
Аугі Гарднер

Відповіді:


328

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

Додаткова інформація про часткову модифікацію ресурсів доступна в RFC 5789 . Конкретно PUTспосіб описаний наступним чином:

Кілька додатків, що розширюють протокол передачі гіпертексту (HTTP), потребують функції для часткової модифікації ресурсів. Існуючий метод HTTP PUT дозволяє лише здійснити повну заміну документа. Ця пропозиція додає новий метод HTTP, PATCH, для зміни існуючого HTTP-ресурсу.


1
Справедливості, ви можете ввести рядок "активувати" або "дезактивувати" ресурс Оскільки, здається, варто переключити лише одне, повністю замінивши це не така вже й велика справа. І це дозволяє (незначно) менший запит.
thecoshman

35
Важливо зауважити, що RFC 5789 все ще знаходиться на етапі пропозиції та офіційно не прийнятий і в даний час позначений як "irrata postoji". Ця "найкраща практика" є дуже дискусійною, і технічно PATCH ще не є частиною стандарту HTTP.
fishpen0

4
Через кілька років мої 2 копійки: ви можете вважати сам статус ресурсом, і якщо так, використання PUT проти / status технічно замінить ресурс статусу на цій кінцевій точці.
Джоно Стюарт

3
Я б наважився сперечатися з документами, навіть якщо це "the" RFC. Документи стверджують, що ви повинні використовувати PATCH для зміни лише частини ресурсу, але він опустив важливе, що метод PATCH визначається як неідентичний метод. Чому? Якщо метод PUT був створений з урахуванням оновлення / заміни всього ресурсу, то чому метод PATCH не був створений як ідентичний потенціал, як PUT, якщо його метою було просто оновити частину ресурсу? Для мене це виглядає більшою мірою різницею в idempotency оновлення, як-от "a = 5" (PUT) і "a = a + 5" (PATCH). Обидва можуть оновити весь ресурс.
Младен Б.

179

R в REST позначає ресурс

(Що не відповідає дійсності, оскільки воно розшифровується як Представництво, але це хороший трюк, щоб пам’ятати про важливість Ресурсів у REST).

Про PUT /groups/api/v1/groups/{group id}/status/activate: ви не оновлюєте "активувати". "Активувати" - це не річ, це дієслово. Дієслова ніколи не є хорошими ресурсами. Основне правило: якщо дія, дієслово, є в URL-адресі, воно, ймовірно, не RESTful .

Що ти робиш замість цього? Або ви "додаєте", "видаляєте" або "оновлюєте" активацію в групі, або, якщо вам зручніше: маніпулювати "статусом" -ресурсом у групі. Особисто я би використовував "активації", тому що вони менш неоднозначні, ніж поняття "статус": створення статусу неоднозначне, створення активації - не.

  • POST /groups/{group id}/activation Створює (або вимагає створення) активації.
  • PATCH /groups/{group id}/activationОновляє деякі деталі існуючої активації. Оскільки група має лише одну активацію, ми знаємо, про який активаційний ресурс ми маємо на увазі.
  • PUT /groups/{group id}/activationВставляє або замінює стару активацію. Оскільки група має лише одну активацію, ми знаємо, про який активаційний ресурс ми маємо на увазі.
  • DELETE /groups/{group id}/activation Скасує або видалить активацію.

Ця модель корисна, коли "активація" Групи має побічні ефекти, такі як платежі, що надсилаються листи тощо. Тільки POST та PATCH можуть мати такі побічні ефекти. Якщо, наприклад, про видалення активації потрібно, скажімо, повідомляти користувачів поштою, DELETE не є правильним вибором; в цьому випадку ви , ймовірно , хочете створити деактивацію ресурс : POST /groups/{group_id}/deactivation.

Добре дотримуватися цих вказівок, тому що цей стандартний договір дає зрозуміти вашим клієнтам, а всі проксі та шари між клієнтом і вами знають, коли це безпечно для повторної спроби, а коли ні. Скажімо, клієнт десь із пластовим Wi-Fi, і його користувач натискає «деактивувати», що викликає DELETE: Якщо це не вдасться, клієнт може просто повторити спробу, доки він не отримає 404, 200 або будь-що інше, з чим може працювати. Але якщо це спрацьовує, POST to deactivationвін знає, що не повторювати: POST має на увазі це.
У будь-якого клієнта зараз є контракт, який у подальшому захищатиме від розсилки 42 електронних листів, "ваша група була вимкнена", просто тому, що її HTTP-бібліотека продовжувала повторний виклик до бекенда.

Оновлення одного атрибута: використовуйте PATCH

PATCH /groups/{group id}

У випадку, якщо ви хочете оновити атрибут. Наприклад, "статус" може бути атрибутом для груп, які можна встановити. Такий атрибут, як "статус", часто є хорошим кандидатом, щоб обмежитися білим списком значень. Приклади використовують деякі невизначені схеми JSON:

PATCH /groups/{group id} { "attributes": { "status": "active" } }
response: 200 OK

PATCH /groups/{group id} { "attributes": { "status": "deleted" } }
response: 406 Not Acceptable

Заміна ресурсу без побічних ефектів використовуйте PUT.

PUT /groups/{group id}

У випадку, якщо ви хочете замінити цілу Групу. Це не обов'язково означає, що сервер фактично створює нову групу і викидає стару, наприклад, ідентифікатори можуть залишатися однаковими. Але для клієнтів це може означати PUT : клієнт повинен припустити, що він отримує абсолютно новий елемент на основі відповіді сервера.

Клієнт повинен, у випадку PUTзапиту, завжди надсилати весь ресурс, маючи всі дані, необхідні для створення нового елемента: зазвичай такі самі дані, як і для POST-створення.

PUT /groups/{group id} { "attributes": { "status": "active" } }
response: 406 Not Acceptable

PUT /groups/{group id} { "attributes": { "name": .... etc. "status": "active" } }
response: 201 Created or 200 OK, depending on whether we made a new one.

Дуже важливою вимогою є PUTідентичність: якщо вам потрібні побічні ефекти під час оновлення групи (або зміни активації), ви повинні використовувати PATCH. Отже, коли оновлення призводить до, наприклад, надсилання пошти, не використовуйте PUT.


3
Це було дуже інформативно для мене. "Ця закономірність корисна, коли" активація "групи має побічні ефекти" - Чому ця модель корисна, зокрема стосовно того, коли дії мають побічні ефекти, на відміну від початкових кінцевих точок ОП
Абдул,

1
@ Abdul, модель корисна з багатьох причин, але wrt побічні ефекти, клієнту повинно бути дуже зрозуміло, які ефекти має дія. Коли, скажімо, додаток для iOS вирішує надіслати всю адресну книгу як "контакти", то повинно бути надзвичайно зрозуміло, які побічні ефекти створює, оновлює, видаляє і т.д. Контакту. Наприклад, щоб уникнути масової розсилки всіх контактів, наприклад.
berkes

1
У RESTfull PUT також може змінити ідентифікацію сутностей - наприклад, ідентифікатор PrimaryKey, де це може призвести до відмови паралельного запиту. (наприклад, для оновлення всієї сутності потрібно видалити деякі рядки та додати нові, отже, створити нові об'єкти). Де PATCH ніколи не може робити цього, дозволяючи необмежену кількість запитів PATCH, не впливаючи на інші "програми"
Piotr Kula

1
Дуже корисна відповідь. Дякую! Я також додав би коментар, як у відповіді Луки, вказуючи, що різниця між PUT / PATCH - це не лише повне / часткове оновлення, це ще й ідентифікація, яка відрізняється. Це не було помилкою, це було навмисним рішенням, і, думаю, не так багато людей беруть це до уваги, приймаючи рішення про використання методу HTTP.
Младен Б.

1
Сервіси @richremer, як і моделі, - це внутрішні абстракції. Так як погана абстракція вимагає відношення 1-1 між моделями REST-кінцевих точок-та-ORM або навіть таблицями баз даних, це погана абстракція для викриття Служб. Зовнішній вигляд, ваш API, повинен повідомляти моделі доменів. Те, як ви їх внутрішньо реалізуєте, не стосується API. Ви повинні вільно переходити від ActivationService до потоку активації на основі CQRS, не змінюючи API.
Берки

12

Я рекомендую використовувати PATCH, оскільки ваша група "ресурсів" має багато властивостей, але в цьому випадку ви оновлюєте лише поле активації (часткова модифікація)

відповідно до RFC5789 ( https://tools.ietf.org/html/rfc5789 )

Існуючий метод HTTP PUT дозволяє лише здійснити повну заміну документа. Ця пропозиція додає новий метод HTTP, PATCH, для зміни існуючого HTTP-ресурсу.

Також більш детально,

Різниця між запитами PUT та PATCH відображається в тому, як сервер обробляє додану сутність для зміни ресурсу,
визначеного Request-URI. У запиті PUT вкладений об'єкт вважається модифікованою версією ресурсу, що зберігається на
початковому сервері, і клієнт вимагає
замінити збережену версію . Однак із PATCH додане об'єднання містить набір інструкцій, що описують, як може бути створений ресурс, що перебуває в даний час , або модифікований існуючий додаток PATCH.
початковому сервері, повинен бути модифікований для створення нової версії. Метод PATCH впливає на ресурс, визначений Request-URI, а
також МОЖЕ мати побічні ефекти на інші ресурси; тобто нові ресурси

PATCH не є ні безпечним, ні безсильним, як визначено [RFC2616], Розділ 9.1.

Клієнти повинні вибрати, коли використовувати PATCH, а не PUT. Для
прикладу, якщо розмір латки документа більше розміру
нових даних про ресурсах , які будуть використані в PUT, то це може зробити
сенс використовувати PUT замість пластиру. Порівняння з POST є ще складнішим, оскільки POST використовується дуже різними способами і може
охоплювати операції, подібні PUT та PATCH, якщо сервер обирає. Якщо
операція не змінює ресурс, ідентифікований Request-URI, передбачуваним способом, POST слід розглядати замість PATCH
або PUT.

Код відповіді для PATCH є

Код відповіді 204 використовується тому, що відповідь не містить тіла повідомлення (яке відповідь з кодом 200 матиме). Зауважте, що можна використовувати й інші коди успіху.

також зверніться до thttp: //restcookbook.com/HTTP%20Methods/patch/

Caveat: API, що реалізує PATCH, повинен виправити атомно. НЕ МОЖЕ бути неможливим, щоб ресурси були напівзаклеєні на запит GET.


7

Оскільки ви хочете розробити API за допомогою архітектурного стилю REST, вам потрібно подумати про ваші випадки використання, щоб вирішити, які поняття достатньо важливі для викриття в якості ресурсів. Якщо ви вирішите розкрити статус групи як підресурсу, ви можете надати їй наступний URI і реалізувати підтримку як методів GET, так і PUT:

/groups/api/groups/{group id}/status

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

Якщо ви все-таки вирішите викрити статус як підресурс групи, він повинен бути ланкою в представленні групи. Наприклад, якщо агент отримує групу 123 і приймає XML, орган відповіді може містити:

<group id="123">
  <status>Active</status>
  <link rel="/linkrels/groups/status" uri="/groups/api/groups/123/status"/>
  ...
</group>

Гіперпосилання потрібне для виконання гіпермедіа як двигуна стану стану застосування архітектурного стилю REST.


0

Я, як правило, віддаю перевагу дещо простішому, як activate/ deactivateпідресурс (пов’язаний Linkзаголовком із rel=service).

POST /groups/api/v1/groups/{group id}/activate

або

POST /groups/api/v1/groups/{group id}/deactivate

Для споживача цей інтерфейс є простим, і він керується принципами REST, не замислюючи вас, осмислюючи "активацію" як окремий ресурс.


0

Один з можливих варіантів реалізації такої поведінки - це

PUT /groups/api/v1/groups/{group id}/status
{
    "Status":"Activated"
}

І очевидно, якщо комусь потрібно його деактивувати, PUTвін матиме Deactivatedстатус у JSON.

У разі необхідності масової активації / дезактивації, PATCHможна вступити в гру (не для точної групи, а для groupsресурсу:

PATCH /groups/api/v1/groups
{
    { “op”: “replace”, “path”: “/group1/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group7/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group9/status”, “value”: “Deactivated” }
}

Загалом це ідея, як підказує @Andrew Dobrowolski, але з незначними змінами в точній реалізації.

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