PUT POST является идемпотентным (REST)

Я не совсем понимаю, как глаголы HTTP определяются как идемпотентные. Все, что я читал, это GET, а PUT идемпотент. POST не является идемпотентным. Но вы можете создать REST API с помощью POST, который ничего не меняет (например, в базе данных), или создать REST API для PUT, который меняется при каждом вызове.

Конечно, это, вероятно, неправильный способ сделать что-то, но если это можно сделать, почему PUT помечен как идемпотент (или POST как нет), когда это зависит от реализации? Я не оспариваю эту идею, я, вероятно, что-то упускаю, и я прошу прояснить мое понимание.

РЕДАКТИРОВАТЬ:

Я предполагаю, что один из способов задать мой вопрос: в чем была бы проблема, если бы я использовал PUT для выполнения неидемпотентного вызова и POST для этого?


person g_b    schedule 01.01.2015    source источник
comment
Да, реализация может делать все, что захочет, но тогда ее можно будет считать несовместимой со стандартом.   -  person icktoofay    schedule 01.01.2015
comment
stormpath.com/blog/put-or-post надеюсь, что приведенная выше ссылка поможет. .   -  person ihaider    schedule 01.01.2015
comment
@icktoofay: В таком случае, каковы будут последствия, если я использую PUT для POST и наоборот?   -  person g_b    schedule 01.01.2015
comment
@ManinGreen: Спасибо за отличную ссылку. В статье упоминается, что вы также можете реализовать неизменяемую реализацию POST. Вот что со мной непонятно. В чем проблема, если я использовал PUT для выполнения неидемпотентного вызова?   -  person g_b    schedule 01.01.2015
comment
@g_b Полностью согласен с этим вопросом, похоже на то, как люди говорят, что PUT является идемпотентным, это означает, что он идемпотент сам по себе, когда это совсем не так.   -  person Gabriel Rodriguez    schedule 15.11.2015


Ответы (3)


Вы правы, указывая на то, что в протоколе HTTP нет ничего неотъемлемого, что обеспечивает идемпотентный атрибут методов/глаголов, таких как PUT и DELETE. HTTP, будучи протоколом без сохранения состояния, не сохраняет никакой информации или статуса каждого запроса, который делает пользователь. ; каждый отдельный запрос рассматривается как независимый.

Процитируем Википедию по поводу идемпотентного атрибута HTTP-методов (выделено мной):

Обратите внимание, что идемпотентность метода не зависит от протокола или веб-сервера. Вполне возможно написать веб-приложение, в котором (например) вставка в базу данных или другое неидемпотентное действие запускается GET или другим запросом. Однако игнорирование этой рекомендации может привести к нежелательным последствиям, если пользовательский агент предполагает, что повторение одного и того же запроса безопасно, хотя это не так.

Так что да, можно отклоняться от обычной реализации и развертывать такие вещи, как неизменяемая реализация POST, неидемпотентный PUT и т. д., вероятно, без значительных, опасных для жизни технических проблем. Но вы рискуете расстроить других программистов, использующих ваши веб-сервисы, думая, что вы не знаете, что делаете.

Вот важная цитата из RFC2616 о методах HTTP, безопасно (выделено мной):

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

В частности, было установлено соглашение о том, что методы GET и HEAD НЕ ДОЛЖНЫ иметь значения выполнения каких-либо действий, кроме извлечения. Эти методы следует считать «безопасными». Это позволяет пользовательским агентам представлять другие методы, такие как POST, PUT и DELETE, особым образом, чтобы пользователь знал о том, что запрашивается возможно небезопасное действие.

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

ОБНОВЛЕНИЕ: как указал Джулиан, RFC 2616 был заменен RFC 7231. Вот соответствующий раздел.

Итак, когда вы публикуете веб-сервис как метод PUT, и я отправляю запрос, который выглядит так:

PUT /users/<new_id> HTTP/1.1
Host: example.com

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

PUT /users/<existing_id> HTTP/1.1
Host: example.com

Я ожидаю, что соответствующий существующий пользователь будет обновлен. Если я повторяю один и тот же запрос, отправляя форму несколько раз, пожалуйста, не выводит диалоговое окно с предупреждением (потому что мне нравится установленное соглашение).

И наоборот, как потребитель веб-службы POST, я буду ожидать таких запросов, как:

POST /users/<existing_id> HTTP/1.1
Host: example.com

для обновления соответствующего существующего пользователя, а запрос выглядит так:

POST /users/<new_id> HTTP/1.1
Host: example.com

чтобы вызвать ошибку, потому что URL-адрес еще не существует.

person ivan.sim    schedule 01.01.2015
comment
@JulianReschke Спасибо за информацию. Я обновил свой ответ, чтобы указать, что RFC 2616 устарел. Хотя цитата, которую я использовал, больше не появляется в RFC 7231, не думаете ли вы, что она все еще применима? Спасибо. - person ivan.sim; 01.01.2015
comment
@ivan.sim Ваш пример для POST нужно поменять местами, а вопросы заменить пользователями - person JJ.; 16.12.2016
comment
@ivan.sim Как узнать existing_id или сгенерировать new_id для http без сохранения состояния, где хранить существующий идентификатор? Хорошо, можете ли вы объяснить реальное использование UUID or GUID? - person Ashish Kamble; 22.10.2019

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

person Julian Reschke    schedule 01.01.2015

надеюсь, что ссылка поможет вам: Идемпотентность метода HTTP

Будьте осторожны при работе с безопасными методами: если кажущийся безопасным метод, такой как GET, изменит ресурс, возможно, что любые прокси-системы клиента промежуточного программного обеспечения между вами и сервером будут кэшировать этот ответ. Другой клиент, который хочет изменить этот ресурс через тот же URL-адрес (например: http://example.org/api/article/1234/delete), не будет вызывать сервер, а вернет информацию непосредственно из кеша. Небезопасные (и неидемпотентные) методы никогда не будут кэшироваться никакими прокси-серверами промежуточного слоя.

person Ali Kemal Saglam    schedule 01.01.2015