Удаление ресурса с помощью http DELETE

Итак, учитывая, что глагол DELETE в Http является идемпотентным, когда я выполняю следующий запрос, что должно произойти со вторым (или третьим, или четвертым и т. Д.)?

DELETE /person/123

В первый раз ресурс удаляется и я возвращаю 204 (успешно, контента нет). Должен ли я вернуть 204 при последующих вызовах или 404 (не найдено)?

6 ответов

Решение

Поскольку HTTP-запросы в системе без сохранения состояния должны быть независимыми, результаты одного запроса не должны зависеть от предыдущего запроса. Подумайте, что должно произойти, если два пользователя сделали УДАЛЕНИЕ на одном и том же ресурсе одновременно. Для второго запроса имеет смысл получить 404. То же самое должно быть верно, если один пользователь делает два запроса.

Я полагаю, что возвращение DELETE двух разных ответов не кажется вам идемпотентным. Я считаю полезным думать о идемпотентных запросах как о том, что они оставляют систему в одном и том же состоянии, не обязательно имея одинаковый ответ. Поэтому независимо от того, УДАЛИТЕ ли вы существующий ресурс или попытаетесь УДАЛИТЬ ресурс, который не существует, состояние ресурса сервера будет одинаковым.

Я согласен с тем, что сказано в текущем выбранном ответе, что 2-й (и 3-й, 4-й, ...) DELETE должен получить 404. И я заметил, что у этого ответа 143 голоса "за", но есть и противоположный комментарий, в котором 54 голоса "за", поэтому сообщество разделено на 2 лагеря примерно в соотношении 3:1. А вот и дополнительная информация, чтобы разрешить этот давний спор.

  1. Прежде всего, давайте НЕ будем начинать с того, что думаю "я", что думаешь "вы" или что думает еще один автор книги. Начнем со спецификаций HTTP, т.е. RFC 7231.

    • RFC 7231, раздел 4.3.5 DELETE упомянул только успешный ответ, который должен быть 2xx, но не упоминал, что получит последующее DELETE. Так что давайте копнем глубже.
    • RFC 7231, раздел 6.5.4 404 Not Found говорит, что ответ 404 предназначен для ресурса, который не существует. Поскольку никакой конкретный метод http (в частности, не DELETE) не вызывается для обработки в противном случае, мы можем интуитивно получить впечатление (и это справедливо), что мой запросDELETE /some/resource/which/does/not/exist должен привести к ошибке 404. Затем DELETE /some/resource/which/happened/to/be/removed/by/someone/else/five/days/ago может также вернуть 404. Тогда зачем DELETE /some/resource/i/deleted/five/seconds/agoбыть другим? "А как насчет идемпотентности?!", я слышу, как вы это кричите. Погодите, мы вот-вот займемся этим.
    • Исторически RFC 2616, опубликованный в 1999 году, был наиболее упоминаемой спецификацией HTTP 1.1. К сожалению, его описание идемпотентности было расплывчатым, что оставляет место для всех этих споров. Но эти спецификации были заменены RFC 7231. Цитируется из RFC 7231, раздел 4.2.2 Идемпотентные методы, выделено мной:

      Метод запроса считается "идемпотентным", если предполагаемое ВЛИЯНИЕ НА СЕРВЕР нескольких идентичных запросов с этим методом такое же, как эффект для одного такого запроса. Из методов запроса, определенных в этой спецификации, методы запроса PUT, DELETE и безопасные являются идемпотентными.

      Итак, в спецификациях написано, что идемпотентность - это влияние на сервер. Первый DELETE, возвращающий 204, а затем последующий DELETE, возвращающий 404, такой другой код состояния НЕ делает DELETE неидемпотентным. Использование этого аргумента для обоснования последующего возврата 204 не имеет значения.

  2. ОК, значит, дело не в идемпотентности. Но тогда может возникнуть следующий вопрос: что, если мы все же решим использовать 204 в последующем DELETE? Это нормально?

    Хороший вопрос. Мотивация понятна: позволить клиенту по-прежнему достичь желаемого результата, не беспокоясь об обработке ошибок. Я бы сказал, что возвращение 204 в последующем DELETE- это безобидная "белая ложь" на стороне сервера, в которой клиентская сторона не сразу заметит разницу. Вот почему около 25% людей делают это в дикой природе, и, похоже, это все еще работает. Только учтите, что такую ​​ложь можно считать семантически странной, потому чтоGET /non-exist возвращает 404, но DELETE /non-existдает 204, тогда клиент поймет, что ваш сервис не полностью соответствует разделу 6.5.4 404 Not Found.

    Но я хочу отметить, что предполагаемый способ, на который намекает RFC 7231, то есть возврат 404 при последующем DELETE, в первую очередь не должен быть проблемой. Это сделали в 3 раза больше разработчиков, и слышали ли вы когда-нибудь о серьезных инцидентах или жалобах, вызванных тем, что клиент не может обработать ошибку 404? По-видимому, нет, и это потому, что любой достойный клиент, который реализует HTTP DELETE (или любой HTTP-метод, если на то пошло), не будет слепо предполагать, что результат всегда будет успешным 2xx. И затем, как только разработчик начнет рассматривать обработку ошибок, ошибка 404 Not Found будет одной из первых ошибок, которые приходят в голову. В этот момент он / она, вероятно, сделает вывод, что для операции HTTP DELETE семантически безопасно игнорировать ошибку 404. Так и сделали.

Проблема решена.

Поваренная книга веб-сервисов RESTful - отличный ресурс для этого. Случайно, в своем превью Google показывает страницу об УДАЛЕНИИ (стр. 11):

Метод DELETE является идемпотентным. Это подразумевает, что сервер должен вернуть код ответа 200 (ОК), даже если сервер удалил ресурс в предыдущем запросе. Но на практике реализация DELETE как идемпотентной операции требует, чтобы сервер отслеживал все удаленные ресурсы. В противном случае он может вернуть 404 (не найдено).

Первое УДАЛЕНИЕ: 200 или 204.

Последующие УДАЛЕНИЯ: 200 или 204.

Обоснование: УДАЛЕНИЕ должно быть идемпотентным. Если вы вернете 404 при втором УДАЛЕНИИ, ваш ответ изменится с кода успеха на код ошибки. Клиентская программа может предпринять неправильные действия, исходя из предположения, что УДАЛИТЬ не удалось.

Пример:

  • Предположим, что ваша операция DELETE является частью многошаговой операции (или "саги"), выполняемой клиентской программой.
  • Например, клиентская программа может быть мобильным приложением, выполняющим банковскую транзакцию.
  • Допустим, клиентская программа имеет автоматическую повторную попытку операции DELETE (это имеет смысл, потому что DELETE должен быть идемпотентным).
  • Допустим, первый DELETE был успешно выполнен, но ответ 200 был потерян на пути к клиентской программе.
  • Клиентская программа попытается УДАЛИТЬ.
  • Если вторая попытка возвращает 404, клиентская программа может отменить всю операцию из-за этого кода ошибки.
  • Но поскольку первое УДАЛЕНИЕ успешно выполнено на сервере, система может остаться в несогласованном состоянии.
  • Если вторая попытка вернет 200 или 204, клиентская программа продолжит работу, как ожидалось.

я собираюсь бросить410 Goneв смесь. RFC 7231, раздел 6.5.4 410 Gone говорит

Код состояния 410 (Gone) указывает, что доступ к целевому ресурсу больше не доступен на исходном сервере и что это состояние, вероятно, будет постоянным.

Так204для первого и для последующегоDELETEс. Это тоже приятно и последовательно, какGET,PUTи т. д. также вернут410.

Однако для этого потребуется, чтобы сервер отслеживал то, что было удалено, и далее в спецификации говорится:

Если исходный сервер не знает или не имеет возможности определить, является ли состояние постоянным, вместо этого следует использовать код состояния 404 (не найден).

Итак, если важно отслеживать удаленные ресурсы (например, если вам нужно предотвратить их повторное создание), 410, в противном случае — 404, как рекомендовано в этом ответе , или, другими словами, «это зависит» .

Другая альтернатива:

Всегда возвращайте 200 (ОК), выводя количество удаленных ресурсов (0 или 1).

Преимущества:

  • Это нормально, если у вас есть другой маршрут, позволяющий удалять несколько объектов путем поиска/фильтрации.
  • Пользователь вашего API всегда знает, удалился ресурс или нет (и проверка тривиальна).
  • Код состояния остается прежним, и это кажется правильным; по крайней мере мне.
  • Подход возврата количества удаленных документов/строк/объектов (которое может быть равно 0) распространен в базах данных.
Другие вопросы по тегам