RESTful альтернативи ВИДАЛИТИ тіло запиту


93

Хоча специфікація HTTP 1.1, схоже, дозволяє тілам повідомлень на запити DELETE , схоже, це вказує на те, що сервери повинні ігнорувати це, оскільки для нього немає визначеної семантики.

4.3 Тіло повідомлення

Сервер ПОВИНЕН читати та пересилати тіло повідомлення на будь-який запит; якщо метод запиту не включає визначену семантику для тіла-сутності, то при обробці запиту тіло повідомлення ПОВИННО ігнорувати.

Я вже переглянув кілька суміжних дискусій на цю тему щодо SO та не тільки, таких як:

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

Крім того, я помітив тенденцію в різних бібліотеках клієнтів HTTP, де все більше і більше вдосконалень, схоже, реєструються для цих бібліотек для підтримки тіл запитів на DELETE. Здається, більшість бібліотек зобов’язують, хоча зрідка і з невеликим початковим опором.

У моєму випадку використання передбачено додавання деяких необхідних метаданих у DELETE (наприклад, "причина" для видалення, разом з деякими іншими метаданими, необхідними для видалення). Я розглянув наступні варіанти, жоден з яких не здається цілком доречним і відповідає специфікаціям HTTP та / або найкращим практикам REST:

  • Текст повідомлення - специфікація вказує на те, що тіла повідомлень на DELETE не мають семантичного значення; не повністю підтримується клієнтами HTTP; не стандартна практика
  • Спеціальні заголовки HTTP - вимагати спеціальних заголовків, як правило, суперечить стандартній практиці ; їх використання суперечить решті мого API, жоден з яких не вимагає спеціальних заголовків; крім того, немає хорошої відповіді HTTP для вказівки на погані користувацькі значення заголовків (можливо, це взагалі окреме питання)
  • Стандартні заголовки HTTP - жодні стандартні заголовки не підходять
  • Параметри запиту - додавання параметрів запиту фактично змінює видалений URI-запит; проти стандартної практики
  • Метод POST - (наприклад POST /resourceToDelete { deletemetadata }) POST не є семантичним варіантом видалення; POST насправді являє собою протилежну бажану дію (тобто POST створює підлеглих ресурсів; але мені потрібно видалити ресурс)
  • Кілька методів - Розбиття запиту DELETE на дві операції (наприклад, PUT видалити метадані, потім DELETE) розбиває атомну операцію на дві, що може призвести до несумісного стану. Причина видалення (та інші пов’язані метадані) не є частиною самого подання ресурсу.

Моїм першим уподобанням було б, мабуть, використовувати тіло повідомлення, друге - користувацькі заголовки HTTP; однак, як зазначалося, у цих підходів є деякі мінуси.

Чи існують якісь рекомендації чи найкращі практики, що відповідають стандартам REST / HTTP для включення таких необхідних метаданих у запити DELETE? Чи є інші альтернативи, які я не розглядав?


2
Деякі реалізації, такі як Jerseyне дозволяють тіло для deleteзапитів.
basiljames

Відповіді:


44

Незважаючи на деякі рекомендації не використовувати тіло повідомлення для запитів DELETE, цей підхід може бути доречним у певних випадках використання. Саме такий підхід ми застосували після оцінки інших варіантів, згаданих у питанні / відповідях, і після співпраці зі споживачами послуги.

Хоча використання тіла повідомлення не є ідеальним, жоден з інших варіантів також не підходить ідеально. Тіло запиту DELETE дозволило нам легко та чітко додати семантику навколо додаткових даних / метаданих, необхідних для супроводу операції DELETE.

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


12
Це погана ідея. Це може призвести до проблем: якщо згодом ви вирішите використовувати службу прискорення HTTP, таку як Akamai EdgeConnect. Я точно знаю, що EdgeConnect вилучає тіла із запитів HTTP DELETE (оскільки вони споживають пропускну здатність, ймовірно, недійсні). Також ймовірно, що подібні служби роблять те саме (див. Функцію прискорення Kindle та інші послуги, подібні до CDN). Ймовірно, вам слід перепроектувати, щоб не використовувати для вашої служби дієслова HTTP. Більшість API не мають сенсу, використовуючи HTTP-дієслова / класичний-REST та HTTP-питання, пов'язані з транспортуванням дієслів, дуже важко усунути.
Гейб,

3
Я погоджуюся з @Gabe, надсилання тіла з методами, які не мають тіла за визначенням, є надійним способом випадкової втрати даних, коли ваші біти перетинають Інтернет-канали, і вам буде дуже складно його налагодити.
Ніколас Шенкс,

3
Ці зауваження проти використання ВИДАЛЕННЯ є нерелевантними, поки вони не стосуються дуже дійсних проблем, які є у ОП. Я намагаюся з усіх сил дотримуватися духу REST, але видалення без оптимістичної одночасності та видалення, яке не має атомної пакетної операції, не є практичним у реальних ситуаціях. Це серйозний недолік за зразком REST.
Кварклі

Я з @Quarkly щодо цього. Я не розумію, що таке RESTFUL ідея про те, як нам слід перевіряти паралельність тощо. Перевірки паралельності не належать клієнту.
Дірк Вессель,

13

Те, що ви, напевно, хочете - це одна з двох речей, жодна з яких не є чистою DELETE:

  1. У вас є дві операції, a PUTз причини видалення, за якою йде a DELETEресурсу. Після видалення вміст ресурсу більше нікому не доступний. Причина не може містити гіперпосилання на видалений ресурс. Або,
  2. Ви намагаєтесь змінити ресурс з state=activeна state=deletedза допомогою DELETEметоду. Ресурси з state = delete ігноруються вашим основним API, але все ще можуть бути прочитані адміністратору або комусь із доступом до бази даних. Це дозволено - DELETEне потрібно стирати дані резервної копії для ресурсу, а лише видаляти ресурс, відкритий за цим URI.

Будь-яку операцію, яка вимагає тіло повідомлення за DELETEзапитом, може бути розбито на найзагальніший спосіб: a POSTдля виконання всіх необхідних завдань із тілом повідомлення та a DELETE. Я не бачу причин порушувати семантику HTTP.


2
Що станеться, якщо PUTпричина вдається, а DELETEресурс не вдається? Як можна запобігти непослідовному стану?
Lightman

1
@Lightman PUT лише вказує намір. Він може існувати без відповідного DELETE, що означало б, що хтось хотів видалити, але або це не вдалося, або вони передумали. Змінення порядку викликів також дозволить DELETE відбуватися без причини - надання причини тоді буде розглядатися просто як анотація. Саме з обох цих причин я б рекомендував використовувати варіант 2 із зазначеного вище, тобто змінити стан базового запису, таким чином, щоб ресурс HTTP зник з поточної URL-адреси. Потім збирач сміття / адміністратор може очищати записи
Ніколас Шенкс,

7

З огляду на вашу ситуацію, я б скористався одним із таких підходів:

  • Надіслати PUT або PATCH : Я роблю висновок, що операція видалення є віртуальною, оскільки вона вимагає причини видалення. Тому я вважаю, що оновлення запису за допомогою операції PUT / PATCH є допустимим підходом, хоча це не є операцією DELETE як такою.
  • Використовуйте параметри запиту : uri ресурсу не змінюється. Я насправді вважаю, що це також правильний підхід. Питання, яке ви пов’язали, говорило про те, що заборонено видаляти, якщо параметр запиту відсутній. У вашому випадку у мене буде просто причина за замовчуванням, якщо причина не вказана в рядку запиту. Ресурс все одно буде resource/:id. Ви можете зробити його доступним за допомогою заголовків Link на ресурсі для кожної причини (з relпозначкою на кожному, щоб визначити причину).
  • Використовуйте окрему кінцеву точку для кожної причини : Використовуючи url like resource/:id/canceled. Це насправді змінює Request-URI і точно не є RESTful. Знову ж таки, заголовки посилань можуть зробити це виявленим.

Пам’ятайте, що REST - це не закон чи догма. Думайте про це більше як про керівництво. Отже, коли є сенс не дотримуватися вказівок для вашого проблемного домену, не слід. Просто переконайтеся, що споживачі API поінформовані про відхилення.


Що стосується використання параметрів запиту, я розумію, що запит є частиною запиту-URI відповідно до розділу 3.2 , і тому використання цього підходу (або аналогічно окремих кінцевих точок) суперечить визначенню методу DELETE , наприклад, що "ресурс" ідентифікований запитом-URI "видаляється.
shelley

Ресурс ідентифікується шляхом uri. Отже, GET to /orders/:idповерне той самий ресурс, що і /orders/:id?exclude=orderdetails. Рядок запиту лише дає підказки серверу - у цьому випадку виключити деталі замовлення у відповіді (якщо підтримується). Подібним чином, якщо ви надсилаєте DELETE до /orders/:idабо /orders/:id?reason=canceledабо /orders/:id?reason=bad_credit, ви все ще дієте на тому самому базовому ресурсі. Щоб зберегти "єдиний інтерфейс", у мене буде причина за замовчуванням, так що надсилання параметра запиту не потрібно.
codeprogression

@shelley Ви маєте рацію у своїх занепокоєннях щодо рядків запитів. Рядок запиту є частиною URI. Надсилання запиту на ВИДАЛЕННЯ /foo?123означає, що ви видаляєте інший ресурс, ніж якщо б ви надсилали ВИДАЛИТИ на /foo?456.
Ніколас Шенкс,

@codeprogression Вибачте, але багато з того, що ви говорите, є неправильним. Ресурс визначається усім URI, а не лише шляхом. Різні рядки запитів - це різні ресурси (у сенсі HTTP слова „ресурс“). Крім того, для єдиного інтерфейсу не потрібна причина за замовчуванням. Цей термін стосується використання GET, PUT, POST, PATCH та DELETE, як їх визначив HTTP. Спільне є між постачальниками (постачальниками агентів користувачів, постачальниками API, постачальниками кешування проксі-серверів, провайдерами тощо), а не в межах власного API (хоча це теж повинно бути єдиним у дизайні для розумності користувачів!).
Ніколас Шенкс,

@Nicholas Я не розумію вашої потреби аргументувати дискусію, яка закінчилася три роки тому. Відповідь та коментарі, які я зробив, є достовірними та правильними з точки зору REST-орієнтованого. REST не є HTTP (ані будь-яка реалізація HTTP від ​​постачальника). У контексті REST ресурси однакові. І як я вже сказав у своїй відповіді, REST - це не закон чи догма, а керівництво.
codeprogression

0

Пропоную включити необхідні метадані як частину самої ієрархії URI. Приклад (наївний):

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

напр

DELETE /entries/range/01012012/31122012 - Видалити всі записи з 01 січня 2012 року по 31 грудня 2012 року

Сподіваюся, це допомагає.


5
Не охоплює випадків, таких як надсилання причини для видалення, тобто поля коментарів.
Кугель

3
Ого. це жахлива ідея. Якщо у вас забагато метаданих, це призведе до обмеження розміру URI.
Баладжі Боггарам Раманараян

1
Цей підхід не відповідає практиці RESTful і не рекомендується, оскільки у вас буде заплутана структура URI. Кінцеві точки заплутані в взаємозв'язку ідентифікації ресурсів із метаданими, і з часом це стане кошмаром обслуговування, коли ваш API змінюється. Набагато більше бажано мати rangeзазначені в запитах параметри або корисне навантаження, що є основою цього питання: щоб зрозуміти підхід найкращих практик до проблеми, яка, на мою думку, не є такою.
digitaldreamer

@digitaldreamer - Я не розумію, що ви маєте на увазі під заплутаною структурою URI? Крім того, це HTTP DELETE, тому корисне навантаження не є опцією, але параметри запиту - так.
Суреш Кумар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.