ПОСТ против ПОЧТЫ в ОТДЫХЕ

5378

Согласно спецификации HTTP / 1.1:

Этот POSTметод используется для запроса, чтобы исходный сервер принял объект, включенный в запрос, в качестве нового подчиненного ресурса, идентифицированного Request-URIвRequest-Line

Другими словами, POSTиспользуется для создания .

В PUTзапрашивает метод , что включенный объект хранить под входящий в комплект поставки Request-URI. Если Request-URIссылка относится к уже существующему ресурсу, вложенный объект СЛЕДУЕТ рассматривать как модифицированную версию, находящуюся на исходном сервере. Если Request-URIобъект не указывает на существующий ресурс и этот URI может быть определен запрашивающим пользовательским агентом как новый ресурс, исходный сервер может создать ресурс с этим URI. "

То есть PUTиспользуется для создания или замены .

Итак, какой из них следует использовать для создания ресурса? Или нужно поддерживать оба?

Алекс
источник
56
Может быть полезно использовать определения в HTTPbis - Рой приложил немало усилий для их выяснения. См .: tools.ietf.org/html/…
Марк Ноттингем,
16
Просто для того, чтобы привести комментарий @ MarkNottingham к последней редакции, приведем POST и PUT , как определено в HTTPbis.
Мариус Бутук
38
Мне кажется, что эта дискуссия возникла из обычной практики упрощения REST путем описания HTTP-методов в терминах операций CRUD.
Ступорман
5
К сожалению, первые ответы о POST неверны. Проверьте мой ответ для лучшего объяснения различий: stackoverflow.com/a/18243587/2458234
7hi4g0
23
PUT и POST - небезопасные методы. Однако PUT является идемпотентом, а POST - нет. - См. Больше на: restcookbook.com/HTTP%20Methods/put-vs-post/…
Динеш

Ответы:

4240

В целом:

Оба PUT и POST могут быть использованы для создания.

Вы должны спросить "для чего вы выполняете действие?" чтобы отличить то, что вы должны использовать. Предположим, вы разрабатываете API для вопросов. Если вы хотите использовать POST, вы должны сделать это со списком вопросов. Если вы хотите использовать PUT, вы должны сделать это для конкретного вопроса.

Великолепно можно использовать оба, поэтому какой из них я должен использовать в своем дизайне RESTful:

Вам не нужно поддерживать PUT и POST.

Что используется, остается за вами. Но просто не забудьте использовать правильный в зависимости от того, на какой объект вы ссылаетесь в запросе.

Некоторые соображения:

  • Вы называете свои объекты URL, которые вы создаете явно, или позволяете серверу решать? Если вы называете их, используйте PUT. Если вы позволите серверу решить, используйте POST.
  • PUT идемпотентен, поэтому, если вы поместите объект дважды, это не даст никакого эффекта. Это хорошее свойство, поэтому я бы использовал PUT, когда это возможно.
  • Вы можете обновить или создать ресурс с PUT с тем же URL объекта
  • С помощью POST вы можете получать 2 запроса одновременно, внося изменения в URL, и они могут обновлять различные части объекта.

Пример:

Я написал следующее как часть другого ответа на SO относительно этого :

ПОСЛЕ:

Используется для изменения и обновления ресурса

POST /questions/<existing_question> HTTP/1.1
Host: www.example.com/

Обратите внимание, что следующее является ошибкой:

POST /questions/<new_question> HTTP/1.1
Host: www.example.com/

Если URL еще не создан, вам не следует использовать POST для его создания при указании имени. Это должно привести к ошибке «ресурс не найден», потому что <new_question>еще не существует. <new_question> Сначала вы должны поместить ресурс на сервер.

Вы можете сделать что-то вроде этого, чтобы создать ресурсы, используя POST:

POST /questions HTTP/1.1
Host: www.example.com/

Обратите внимание, что в этом случае имя ресурса не указано, путь к URL-адресу новых объектов будет возвращен вам.

ПОЛОЖИЛ:

Используется для создания ресурса или его перезаписи. Пока вы указываете ресурсам новый URL.

Для нового ресурса:

PUT /questions/<new_question> HTTP/1.1
Host: www.example.com/

Чтобы перезаписать существующий ресурс:

PUT /questions/<existing_question> HTTP/1.1
Host: www.example.com/

Кроме того, и более кратко, в разделе 4.3.4 RFC 7231 раздел PUT (выделение добавлено),

4.3.4. ПОЛОЖИЛ

Метод PUT запрашивает, чтобы состояние целевого ресурса было createdили replacedс состоянием, определенным представлением, заключенным в полезную нагрузку сообщения запроса.

Брайан Р. Бонди
источник
1027
Я думаю, что нельзя не подчеркнуть тот факт, что PUT является идемпотентом: если сеть перегружена, и клиент не уверен, что его запрос был выполнен, он может просто отправить его второй (или сотый) раз, и это гарантируется HTTP-спецификации, что это имеет точно такой же эффект, как отправка один раз.
Йорг Миттаг
77
@ Йорг W Mittag: не обязательно. Второй раз может вернуть 409 Conflict или что-то еще, если запрос был изменен за это время (каким-то другим пользователем или первым полученным запросом).
Mitar
633
Если я не ошибаюсь, мы должны подчеркнуть, что PUT определен как идемпотент. Вы все еще должны написать свой сервер таким образом, чтобы PUT вел себя правильно, да? Возможно, лучше сказать: «PUT заставляет транспорт принимать идемпотентность, которая может влиять на поведение транспорта, например, кэширование».
Ян Ни-Льюис
151
@ JörgWMittag Идемпотентная фраза? Как насчет «Отправь и отправь и отправь моему другу, в конце концов это не имеет значения».
Джеймс Бенинджер
39
Думает о них как: PUT = вставить или обновить; POST = вставить. Поэтому, когда вы делаете два PUT - вы получаете одну новую запись, когда вы делаете две POST - вы получаете две новые записи.
Евгений Коньков
2219

Вы можете найти утверждения в Интернете, которые говорят

Ни один не совсем прав.


Лучше выбирать между PUT и POST, основываясь на идемпотентности действия.

PUT подразумевает размещение ресурса - полная замена того, что доступно по данному URL, на другое. По определению, PUT является идемпотентом. Делайте это столько раз, сколько хотите, и результат тот же. x=5идемпотент. Вы можете положить ресурс независимо от того, существует ли он ранее или нет (например, создать или обновить)!

POST обновляет ресурс, добавляет вспомогательный ресурс или вызывает изменение. ПОЧТА не идемпотентна, как x++не идемпотентна.


По этому аргументу PUT предназначен для создания, когда вы знаете URL-адрес того, что вы создадите. POST может использоваться для создания, когда вы знаете URL «фабрики» или менеджера для категории вещей, которые вы хотите создать.

так:

POST /expense-report

или:

PUT  /expense-report/10929
Cheeso
источник
72
Я согласен, что когда идемпотентность важна, она должна превосходить любые другие проблемы, так как неправильное использование может вызвать много неожиданных ошибок.
Джош
16
Если POST может обновить ресурс, как это не идемпотент? Если я меняю возраст учеников, используя PUT, и делаю это в 10 раз, возраст учеников остается прежним, если я это делал один раз.
Джек Уклея
28
@Schneider, в этом случае ваш сервер прилагает дополнительные усилия, чтобы гарантировать идемпотентность, но не рекламирует ее. Браузеры по-прежнему будут предупреждать пользователя, если они попытаются перезагрузить такой запрос POST.
Тобу
47
@Schneider POST может создать вспомогательный ресурс; следовательно, вы можете собирать POST, например, отчеты POST / Расходы, и на вашем сервере будет создано столько объектов (отчетов о расходах), сколько запросов вы отправили, даже если они полностью схожи. Думайте об этом как о вставке той же строки в таблицу БД (/ отчеты о расходах) с автоинкрементным первичным ключом. Данные остаются теми же, ключ (в данном случае URI) генерируется сервером и отличается для каждой другой вставки (запроса). Таким образом, эффект POST может быть идемпотентным, но может и не быть. Следовательно, POST не идемпотентен.
Snifff
11
Допустим, у нас есть объекты, которые могут иметь два свойства - nameи date. Если у нас есть сущность с существующей nameи date, но затем сделать к ней запросы с указанием только a name, правильное поведение PUT будет состоять в том, чтобы уничтожить dateсущность, тогда как POST может обновить только указанные свойства, оставив неуказанные свойства такими, какими они были. до того, как запрос был сделан. Это звучит правильно / разумно, или это неправильное использование PUT (я видел ссылки на PATCH , который кажется более подходящим, но еще не существует)?
Jon Z
707
  • POST для URL-адреса создает дочерний ресурс по URL-адресу, определенному сервером .
  • PUT to URL создает / заменяет ресурс полностью по URL- адресу, указанному клиентом .
  • PATCH to URL обновляет часть ресурса по указанному клиенту URL.

Соответствующие спецификации для PUT и POST - RFC 2616 §9.5ff.

POST создает дочерний ресурс , поэтому POST /itemsсоздает ресурс, который находится под этим /itemsресурсом. Например. /items/1, Отправка одного и того же почтового пакета дважды создаст два ресурса.

PUT предназначен для создания или замены ресурса по URL-адресу, известному клиенту .

Следовательно: PUT является только кандидатом на CREATE, где клиент уже знает URL-адрес до создания ресурса. Например. /blogs/nigel/entry/when_to_use_post_vs_putв качестве заголовка используется ключ ресурса

PUT заменяет ресурс по известному URL, если он уже существует, поэтому отправка одного и того же запроса дважды не имеет никакого эффекта. Другими словами, вызовы PUT являются идемпотентными .

RFC звучит так:

Принципиальное различие между запросами POST и PUT отражается в различном значении Request-URI. URI в запросе POST идентифицирует ресурс, который будет обрабатывать вложенный объект. Этот ресурс может быть процессом приема данных, шлюзом к другому протоколу или отдельным объектом, принимающим аннотации. Напротив, URI в запросе PUT идентифицирует объект, заключенный в запросе - пользовательский агент знает, для чего предназначен URI, и сервер НЕ ДОЛЖЕН пытаться применить запрос к какому-либо другому ресурсу. Если сервер желает, чтобы запрос был применен к другому URI,

Примечание: PUT в основном использовался для обновления ресурсов (путем их полной замены), но в последнее время наблюдается движение к использованию PATCH для обновления существующих ресурсов, поскольку PUT указывает, что он заменяет весь ресурс. RFC 5789.

Обновление 2018 : есть дело, которое можно избежать, чтобы избежать PUT. Смотрите "ОТДЫХ без ПУТА"

С техникой «REST без PUT» идея заключается в том, что потребители вынуждены публиковать новые «несущественные» ресурсы запросов. Как обсуждалось ранее, изменение почтового адреса клиента - это POST на новый ресурс «ChangeOfAddress», а не PUT ресурса «Customer» с другим значением поля почтового адреса.

взято из REST API Design - Моделирование ресурсов Пракашем Субраманиамом из Thoughtworks

Это вынуждает API избегать проблем с переходом состояний при обновлении одного ресурса несколькими клиентами и более точно соответствует источникам событий и CQRS. Когда работа выполняется асинхронно, установка POST и ожидание ее применения представляется целесообразной.

Найджел Торн
источник
53
Или с другой стороны границы: PUT, если клиент определяет адрес результирующего ресурса, POST, если это делает сервер.
DanMan
3
Я думаю, что этот ответ следует отредактировать, чтобы было более понятно, на что @DanMan указал очень простым способом. Здесь я считаю наиболее ценным примечание в конце, в котором говорится, что PUT следует использовать только для замены всего ресурса.
Гермес
3
PATCH не является реалистичным вариантом, по крайней мере, несколько лет, но я согласен с идеологией.
раздавить
4
Я пытаюсь понять, но использование PUT для создания чего-либо имеет смысл, только если клиент точно знает, что ресурс еще не существует, верно? Следуя примеру блога, скажем, вы создали сотни постов за пару лет, а затем случайно выбрали тот же заголовок, что и для поста два года назад. Теперь вы пошли и заменили тот пост, который не был предназначен. Таким образом, использование PUT для создания потребует от клиента отслеживания того, что принято, а что нет, и может привести к несчастным случаям и непреднамеренным побочным эффектам, а также к наличию маршрутов, которые делают две совершенно разные вещи?
galaxyAbstractor
5
Ты прав. Размещение сообщения в блоге по тому же URL-адресу, что и существующий, приведет к обновлению этого существующего сообщения (хотя вы, очевидно, могли бы сначала проверить это с помощью GET). Это указывает на то, что было бы плохой идеей использовать только заголовок в качестве URL. Однако он будет работать везде, где есть естественный ключ в данных ... что, по моему опыту, редко. Или если вы использовали GUID
Найджел Торн
221

Резюме:

Создайте:

Можно выполнить как PUT, так и POST следующим образом:

ПОЛОЖИЛ

Создает THE новый ресурс с newResourceId в качестве идентификатора, в соответствии с / ресурсов URI или сбора .

PUT /resources/<newResourceId> HTTP/1.1 

ПОСЛЕ

Создает А новый ресурс под / ресурсов URI, или коллекции . Обычно идентификатор возвращается сервером.

POST /resources HTTP/1.1

Обновить:

Может выполняться только с помощью PUT следующим образом:

ПОЛОЖИЛ

Обновляет ресурс с существующим ResourceId в качестве идентификатора в URI / resources или в коллекции .

PUT /resources/<existingResourceId> HTTP/1.1

Объяснение:

При работе с REST и URI , как правило, у вас есть общие на левый и конкретном по праву . В дженерики , как правило , называют коллекции и более конкретные детали можно назвать ресурс . Обратите внимание, что ресурс может содержать коллекцию .

Примеры:

<- универсальный - специфичный ->

URI: website.com/users/john
website.com  - whole site
users        - collection of users
john         - item of the collection, or a resource

URI:website.com/users/john/posts/23
website.com  - whole site
users        - collection of users
john         - item of the collection, or a resource
posts        - collection of posts from john
23           - post from john with identifier 23, also a resource

Когда вы используете POST, вы всегда ссылаетесь на коллекцию , поэтому всякий раз, когда вы говорите:

POST /users HTTP/1.1

Вы публикуете нового пользователя в коллекции пользователей .

Если вы продолжите и попробуете что-то вроде этого:

POST /users/john HTTP/1.1

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

Как только вы используете PUT, вы ссылаетесь на ресурс или отдельный элемент, возможно, внутри коллекции . Поэтому, когда вы говорите:

PUT /users/john HTTP/1.1

вы сообщаете серверу об обновлении или создаете, если он не существует, ресурс john в коллекции пользователей .

Spec:

Позвольте мне выделить некоторые важные части спецификации:

ПОСЛЕ

Метод POST используется для запроса, чтобы исходный сервер принял объект, заключенный в запросе, в качестве нового подчиненного ресурса, идентифицируемого Request-URI в строке запроса

Следовательно, создает новый ресурс в коллекции .

ПОЛОЖИЛ

Метод PUT запрашивает, чтобы вложенный объект был сохранен под предоставленным Request-URI. Если Request-URI ссылается на уже существующий ресурс, вложенный объект СЛЕДУЕТ рассматривать как модифицированную версию, находящуюся на исходном сервере. Если Request-URI не указывает на существующий ресурс, и этот URI может быть определен как новый ресурс запрашивающим пользовательским агентом, сервер происхождения может создать ресурс с этим URI. "

Следовательно, создавать или обновлять в зависимости от наличия ресурса .

Ссылка:

7hi4g0
источник
11
Этот пост помог мне понять, что POST добавляет «что-то» в качестве дочернего элемента к данной коллекции (URI), тогда как PUT явно определяет «что-то» в данном местоположении URI.
Kwah
3
Это лучший ответ, здесь, я думаю: ни одна из этих глупостей «POST может обновить ресурс». Мне нравится ваше заявление: «Обновление может быть выполнено только с помощью PUT».
Томас,
4
Нет, PUT не для обновления или создания. Это для замены. Обратите внимание, что вы ничего не можете заменить чем-то для создания эффекта.
Thecoshman
2
@ 7hi4g0 PUT предназначен для обновления с полной заменой, другими словами, заменяет. Вы ничего не заменяете чем-то или чем-то совершенно новым. PUT не предназначен для внесения незначительных изменений (если только клиент не внесет это незначительное изменение и не предоставит всю новую версию, даже то, что осталось прежним). Для частичной модификации PATCH является методом выбора.
Thecoshman
1
@thecoshman Вы могли бы, но это не было бы слишком ясно, что создание также покрыто там. В этом случае лучше быть явным.
7hi4g0
175

POST означает «создать новый», как в «Вот вход для создания пользователя, создайте его для меня».

PUT означает «вставить, заменить, если уже существует», как в «Вот данные для пользователя 5».

Вы POSTна example.com/users, поскольку вы еще не знаете URLпользователя, вы хотите, чтобы сервер его создал.

Вы PUTна example.com/users/id, так как вы хотите заменить / создать конкретного пользователя.

Двойная публикация с одинаковыми данными означает создание двух идентичных пользователей с разными идентификаторами. PUT дважды с одними и теми же данными создает пользователя первым и обновляет его до того же состояния во второй раз (без изменений). Так как вы получаете PUTодно и то же состояние после того, сколько раз вы его выполняете, оно называется «одинаково мощным» каждый раз - идемпотентным. Это полезно для автоматической повторной попытки запросов. Нет больше «вы уверены, что хотите отправить», когда вы нажимаете кнопку «Назад» в браузере.

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

Александр Торстлинг
источник
12
Небрежность может быть причиной того, что обычно учат, что вам нужны только два глагола: GET и POST. ПОЛУЧИТЕ получить, ПОЧТУ изменить. Даже PUT и DELETE были выполнены с использованием POST. Спросить, что на самом деле означает PUT 25 лет спустя, возможно, признак того, что мы сначала неправильно поняли. Популярность REST заставила людей вернуться к основам, где мы должны теперь отучиться от прошлых ошибок. POST использовался слишком часто и теперь преподавался неправильно. Лучшая часть: «Размещение дважды с одинаковыми данными означает создание двух идентичных [ресурсов]». Отличный момент!
maxpolk
1
Как вы можете использовать PUT для создания записи по идентификатору, как в вашем примере, user 5если она еще не существует? Ты имеешь в виду update, replace if already exists? или что-то
Лука
@Coulton: я имел в виду то, что я написал. Вы вставляете пользователя 5, если вы помещаете PUT в / users / 5 и № 5 еще не существует.
Александр Торстлинг
@Coulton: И PUTможет также использоваться, чтобы полностью заменить стоимость существующего ресурса.
DavidRR
1
«Предпочитаю PUT, а не POST» ... хочешь оправдать это?
Thecoshman
173

Я хотел бы добавить мой "прагматичный" совет. Используйте PUT, когда вы знаете «идентификатор», по которому можно получить сохраняемый объект. Использование PUT не будет работать слишком хорошо, если вам понадобится, скажем, идентификатор, сгенерированный базой данных, который будет возвращен вам для будущих поисков или обновлений.

Итак: Чтобы сохранить существующего пользователя или пользователя, для которого клиент генерирует идентификатор, и было подтверждено, что идентификатор является уникальным:

PUT /user/12345 HTTP/1.1  <-- create the user providing the id 12345
Host: mydomain.com

GET /user/12345 HTTP/1.1  <-- return that user
Host: mydomain.com

В противном случае используйте POST для первоначального создания объекта и PUT для обновления объекта:

POST /user HTTP/1.1   <--- create the user, server returns 12345
Host: mydomain.com

PUT /user/12345 HTTP/1.1  <--- update the user
Host: mydomain.com
ThaDon
источник
17
На самом деле, так и должно быть POST /users. (Обратите внимание, что /usersэто множественное число.) Это влияет на создание нового пользователя и делает его дочерним ресурсом /usersколлекции.
DavidRR
6
@DavidRR, чтобы быть справедливым, как обращаться с группами, это еще одна дискуссия в целом. GET /usersимеет смысл, он читает, как вы хотите, но я был бы в порядке с GET /user/<id>или POST /user(с полезной нагрузкой для указанного нового пользователя), потому что он читает правильно, «получить меня пользователей 5» странно, но «получить меня пользователь 5» является более естественным. Я бы, наверное, все-таки упал на сторону плюрализма :)
thecoshman
126

Используйте POST для создания и PUT для обновления. Вот как это делает Ruby on Rails.

PUT    /items/1      #=> update
POST   /items        #=> create
Тим Салливан
источник
4
POST /itemsдобавляет новый элемент к уже определенному ресурсу ('item'). Как говорится в ответе, он не «создает группу». Я не понимаю, почему это имеет 12 голосов.
Дэвид Дж.
Из коробки Rails не поддерживает «создание группы» через REST. Чтобы «создать группу», под которой я подразумеваю «создать ресурс», вы должны сделать это с помощью исходного кода.
Дэвид Дж.
8
Это справедливое руководство, но упрощение. Как уже упоминалось в других ответах, любой из этих методов можно использовать как для создания, так и для обновления.
Брэд Кох
2
Я согласен с ответом с небольшой модификацией. Используйте POST для создания и PUT для полного обновления ресурса. Для частичных обновлений мы можем использовать PUT или PATCH. Допустим, мы хотим обновить статус группы. Мы можем использовать PUT / groups / 1 / status со статусом полезной нагрузки запроса или PATCH / groups / 1 с подробной информацией о действии в полезной нагрузке
java_geek
2
Следует также уточнить, что PUT /items/42это также верно для создания ресурса, но только если клиент имеет право именовать ресурс . (
Позволяет
123

Оба используются для передачи данных между клиентом и сервером, но между ними есть тонкие различия:

Введите описание изображения здесь

Аналогия:

  • PUT т.е. возьми и положи, где это было.
  • POST как отправить почту в почтовом отделении.

введите описание изображения здесь

Социальная сеть / Сетевая аналогия:

  • Публикация в социальных сетях: когда мы публикуем сообщение, создается новое сообщение.
  • Поставьте (т.е. отредактируйте) сообщение, которое мы уже опубликовали.
Premraj
источник
21
@MobileMon Нет, методы REST не являются CRUD.
JLR
1
Я бы сказал PUT для UPSERTS
Hola Soy Edu Feliz Navidad
@MobileMon no: POST, когда вы создаете новый ресурс, и вы не знаете конечную конечную точку для его получения. PUT для других случаев.
Portekoi
67

REST - это концепция очень высокого уровня. На самом деле, он даже не упоминает HTTP вообще!

Если у вас есть какие-либо сомнения относительно того, как реализовать REST в HTTP, вы всегда можете взглянуть на спецификацию протокола публикации Atom (AtomPub) . AtomPub - это стандарт для написания веб-сервисов RESTful с HTTP, который был разработан многими светилами HTTP и REST при участии Роя Филдинга, изобретателя REST и (со) изобретателя самого HTTP.

На самом деле, вы даже можете использовать AtomPub напрямую. Хотя он вышел из сообщества блогеров, он никоим образом не ограничен блогами: это общий протокол для RESTful взаимодействия с произвольными (вложенными) коллекциями произвольных ресурсов через HTTP. Если вы можете представить свое приложение как вложенную коллекцию ресурсов, то вы можете просто использовать AtomPub и не беспокоиться о том, использовать ли PUT или POST, какие коды состояния HTTP возвращать и все эти подробности.

Вот что AtomPub говорит о создании ресурса (раздел 9.2):

Чтобы добавить участников в коллекцию, клиенты отправляют запросы POST на URI коллекции.

Йорг Миттаг
источник
8
Нет ничего плохого в том, чтобы позволить PUT создавать ресурсы. Просто знайте, что это означает, что клиент предоставляет URL.
Джулиан Решке
5
Что-то не так с разрешением PUT создавать ресурсы: клиент предоставляет URL. Это работа сервера!
Джошкодес
@Joshcodes Не всегда дело в том, что работа сервера заключается в создании идентификаторов клиентов. Я все чаще вижу проекты, которые позволяют клиентам генерировать некий UUID в качестве идентификатора ресурса. Эта конструкция особенно подходит для увеличения масштаба.
Джастин
@JustinOhms Я согласен с вашей точкой зрения относительно идентификаторов, сгенерированных клиентом (примечание: все системы, разработанные мной начиная с 2008 года, требуют, чтобы клиент создавал идентификатор как UUID / Guid). Это не означает, что клиент должен указать URL.
Джошкодес
1
Да, если ресурс уже существует, используйте PUT. Однако почти во всех случаях ресурсы должны создаваться с помощью POST, а клиент не должен предоставлять URL-адрес. Рой Филдинг согласен с этим утверждением FWIW: roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
Джошкодес
61

Решение о том, использовать ли PUT или POST для создания ресурса на сервере с HTTP + REST API, зависит от того, кому принадлежит структура URL. Знание или участие клиента в определении структуры URL является ненужной связью, сродни нежелательным связям, возникающим из SOA. Экранирование типов муфт является причиной популярности REST. Поэтому правильным методом для использования является POST. Есть исключения из этого правила, и они возникают, когда клиент желает сохранить контроль над структурой расположения ресурсов, которые он развертывает. Это редко и, вероятно, означает, что что-то еще не так.

На этом этапе некоторые люди утверждают, что если используются RESTful-URL , клиент знает URL-адрес ресурса, и поэтому PUT является приемлемым. В конце концов, именно поэтому важны канонические, нормализованные URL-адреса Ruby on Rails, Django, посмотрите на API Twitter… бла-бла-бла. Эти люди должны понимать , что Restful-URL не существует, и что сам Рой Филдинг утверждает, что :

API REST не должен определять фиксированные имена ресурсов или иерархии (очевидная связь клиента и сервера). Серверы должны иметь свободу управления своим собственным пространством имен. Вместо этого позвольте серверам инструктировать клиентов о том, как создавать соответствующие URI, например, в HTML-формах и шаблонах URI, определяя эти инструкции в типах мультимедиа и ссылочных отношениях. [Ошибка здесь подразумевает, что клиенты принимают структуру ресурса из-за внеполосной информации, такой как специфичный для области стандарт, который является ориентированным на данные эквивалентом функциональной связи RPC].

http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven

Идея RESTful-URL на самом деле является нарушением REST, так как сервер отвечает за структуру URL и должен иметь возможность самостоятельно решать, как его использовать, чтобы избежать связывания. Если это вас смущает, читайте о значении самопознания для разработки API.

Использование POST для создания ресурсов требует разработки, поскольку POST не идемпотентен. Это означает, что повторение POST несколько раз не гарантирует одно и то же поведение каждый раз. Это пугает людей использовать PUT для создания ресурсов, когда они не должны. Они знают, что это неправильно (POST предназначен для CREATE), но они все равно делают это, потому что не знают, как решить эту проблему. Эта проблема проявляется в следующей ситуации:

  1. Клиент POST новый ресурс на сервере.
  2. Сервер обрабатывает запрос и отправляет ответ.
  3. Клиент никогда не получает ответ.
  4. Сервер не знает, клиент не получил ответ.
  5. У клиента нет URL для ресурса (поэтому PUT не является опцией) и повторяется POST.
  6. ПОСТ не идемпотент, а сервер…

На шестом этапе люди обычно не понимают, что делать. Тем не менее, нет никаких причин для создания этой проблемы. Вместо этого можно использовать HTTP, как указано в RFC 2616, и сервер отвечает:

10.4.10 409 Конфликт

Запрос не может быть выполнен из-за конфликта с текущим состоянием ресурса. Этот код разрешен только в ситуациях, когда ожидается, что пользователь сможет разрешить конфликт и повторно отправить запрос. Тело ответа ДОЛЖНО включать достаточно

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

Конфликты чаще всего возникают в ответ на запрос PUT. Например, если использовалось управление версиями, а объект PUT включал изменения в ресурсе, которые конфликтуют с ресурсами, сделанными ранее (сторонним) запросом, сервер может использовать ответ 409, чтобы указать, что он не может выполнить запрос , В этом случае объект ответа, скорее всего, будет содержать список различий между двумя версиями в формате, определяемом типом содержимого ответа.

Ответ с кодом состояния 409 Конфликт является правильным решением, потому что :

  • Выполнение POST данных с идентификатором, который соответствует ресурсу, уже находящемуся в системе, является «конфликтом с текущим состоянием ресурса».
  • Поскольку важная часть для клиента, чтобы понять, сервер имеет ресурс и принять соответствующие меры. Это «ситуация (ситуации), когда ожидается, что пользователь сможет разрешить конфликт и повторно отправить запрос».
  • Ответ, который содержит URL-адрес ресурса с конфликтующим идентификатором и соответствующие предварительные условия для ресурса, предоставит «достаточную информацию для пользователя или пользовательского агента для решения проблемы», что является идеальным случаем в соответствии с RFC 2616.

Обновление на основе выпуска RFC 7231 для замены 2616

RFC 7231 предназначен для замены 2616 и в разделе 4.3.3 описывается возможный ответ для POST.

Если результат обработки POST будет эквивалентен представлению существующего ресурса, сервер происхождения МОЖЕТ перенаправить пользовательский агент на этот ресурс, отправив ответ 303 (см. Другие) с идентификатором существующего ресурса в поле Location. Это дает преимущества предоставления пользовательскому агенту идентификатора ресурса и передачи представления с помощью метода, более подходящего для общего кэширования, хотя за счет дополнительного запроса, если пользовательский агент еще не имеет кэшированного представления.

Теперь может возникнуть соблазн просто вернуть 303 в случае повторения POST. Однако обратное верно. Возврат 303 имеет смысл, только если несколько запросов на создание (создание разных ресурсов) возвращают один и тот же контент. В качестве примера можно привести «спасибо за отправку сообщения с запросом», которое клиент не должен повторно загружать каждый раз. RFC 7231 по-прежнему утверждает в разделе 4.2.2, что POST не должен быть идемпотентным, и продолжает утверждать, что POST должен использоваться для создания.

Для получения дополнительной информации об этом, прочитайте эту статью .

Joshcodes
источник
Будет ли ответ 409 Conflict подходящим кодом для чего-то вроде попытки создать новую учетную запись с уже существующим именем пользователя? Я использовал 409 специально для конфликтов версий, но после прочтения вашего ответа, мне интересно, не следует ли использовать его для каких-либо «дублирующих» запросов.
Эрик Б.
@EricB. Да, в описанной вами ситуации «из-за конфликта с текущим состоянием ресурса» операция завершается неудачно. Кроме того, разумно ожидать, что пользователь может разрешить конфликт, и тело сообщения должно только информировать пользователя о том, что имя пользователя уже существует.
Джошкодес
@Joshcodes Можете ли вы сказать больше о процессе разрешения конфликта? В этом случае, если имя пользователя уже существует, ожидается ли, что клиент запросит у конечного пользователя другое имя пользователя? Что если клиент на самом деле пытается использовать POST для изменения имени пользователя? Должны ли запросы PUT все еще использоваться для обновления параметров, в то время как POST используется для создания объектов, будь то по одному или несколько? Спасибо.
BFar
@ BFar2, если имя пользователя уже существует, клиент должен запросить пользователя. Чтобы изменить имя пользователя, предполагая, что имя пользователя является частью уже созданного ресурса, который необходимо изменить, будет использоваться PUT, потому что вы правы, POST используется для создания, всегда и PUT для обновлений.
Joshcodes
объяснение вещей с использованием короткого и эффективного языка также является желательным навыком
Junchen Liu
53

Мне нравится этот совет из определения PUT в RFC 2616 :

Принципиальное различие между запросами POST и PUT отражается в различном значении Request-URI. URI в запросе POST идентифицирует ресурс, который будет обрабатывать вложенный объект. Этот ресурс может быть процессом приема данных, шлюзом к другому протоколу или отдельным объектом, принимающим аннотации. Напротив, URI в запросе PUT идентифицирует объект, заключенный в запросе - пользовательский агент знает, для чего предназначен URI, и сервер НЕ ДОЛЖЕН пытаться применить запрос к какому-либо другому ресурсу.

Это согласуется с другим советом, заключающимся в том, что PUT лучше всего применять к ресурсам, которые уже имеют имя, а POST хорош для создания нового объекта под существующим ресурсом (и позволяет серверу присвоить ему имя).

Я интерпретирую это и требования идемпотентности к PUT, чтобы означать, что:

  • POST хорош для создания новых объектов в коллекции (и для создания не нужно быть идемпотентом)
  • PUT хорош для обновления существующих объектов (и обновление должно быть идемпотентным)
  • POST также может использоваться для неидемпотентных обновлений существующих объектов (особенно для изменения части объекта без указания всей цели - если вы об этом думаете, создание нового члена коллекции на самом деле является частным случаем такого рода обновление, с точки зрения коллекции)
  • PUT также можно использовать для создания, если и только если вы позволите клиенту назвать ресурс. Но поскольку REST-клиенты не должны делать предположения о структуре URL, это не так, как задумано.
metamatt
источник
3
«POST также может использоваться для неидемпотентных обновлений существующих объектов (особенно для изменения части объекта без указания всей цели». Вот для чего предназначен PATCH
Snuggs
48

Короче говоря:

PUT является идемпотентным, где состояние ресурса будет одинаковым, если одна и та же операция выполняется один или несколько раз.

POST не идемпотентен, где состояние ресурса может стать другим, если операция выполняется несколько раз по сравнению с выполнением одного раза.

Аналогия с запросом к базе данных

PUT Можно подумать, что-то похожее на «ОБНОВЛЕНИЕ СТУДЕНТА SET address =« abc », где id =« 123 »;

POST Вы можете придумать что-то вроде «INSERT INTO STUDENT (имя, адрес) VALUES (« abc »,« xyzzz »);

Идентификатор студента генерируется автоматически.

С PUT, если один и тот же запрос выполняется несколько раз или один раз, состояние таблицы STUDENT остается неизменным.

В случае POST, если один и тот же запрос выполняется несколько раз, в базе данных создается несколько записей Стьюдента, и состояние базы данных изменяется при каждом выполнении запроса INSERT.

ПРИМЕЧАНИЕ. PUT требуется местоположение ресурса (уже ресурс), для которого должно происходить обновление, тогда как POST этого не требует. Поэтому интуитивно POST предназначен для создания нового ресурса, а PUT необходим для обновления уже существующего ресурса.

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

bharatj
источник
6
для PUT аналогичен запросу INSERT или UPDATE
Евгений Коньков
1
на самом деле PUT Вы можете подумать о том, что «UPDATE STUDENT SET address =" abc », где id =« 123 »; будет оператором для PATCH.« UPDATE STUDENT SET address = «abc», name = «newname», где id = » 123 "было бы правильной аналогией для PUT
mko
Положить также можно использовать для вставки. Например, если ваш сервер обнаружит, что вы пытаетесь загрузить один и тот же файл несколько раз, это сделает ваш запрос идемпотентным. (Загрузка новых файлов не производится).
kiwicomb123
43

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

С POST, вы отправляете на адрес очереди или коллекции. С PUT вы кладете по адресу ПУНКТ.

PUT идемпотентен. Вы можете отправить запрос 100 раз, и это не будет иметь значения. ПОСТ не идемпотент. Если вы отправите запрос 100 раз, в вашем почтовом ящике вы получите 100 писем или 100 писем.

Общее правило: если вы знаете идентификатор или название предмета, используйте PUT. Если вы хотите, чтобы идентификатор или имя элемента были назначены принимающей стороной, используйте POST.

POST против PUT

Homer6
источник
1
Нет, PUT подразумевает, что вы знаете URL. Если вы знаете только идентификатор, отправьте POST с этим идентификатором, чтобы получить URL.
Джошкодес
6
Идентификатор является частью URL, поэтому да, используйте PUT, если вы знаете URL (который включает в себя идентификатор).
Homer6
Нет, URL-адрес определяется сервером, и идентификатор не обязательно является частью URL-адреса. Рой Филдинг скажет вам то же самое, или вы можете просто прочитать его диссертацию .
Джошкодес
@ Joshcodes, это предполагает REST? В архитектуре RESTful идентификатор элемента наиболее определенно является частью URL, как в: / people / 123. Мне нравится этот сайт для ОТДЫХА: microformats.org/wiki/rest/urls
Beez
1
@Beez ссылка mircoformats предлагает хороший способ для серверов структурировать свои URL, но сервер определяет URL. Клиент почти никогда не делает. Смотрите мой ответ или связанную статью, если вы этого не понимаете.
Джошкодес
39

Новый ответ (теперь, когда я лучше понимаю REST):

PUT - это просто заявление о том, какой контент сервис должен использовать для представления представлений ресурса, идентифицированных клиентом; POST - это заявление о том, какой контент должен содержать сервис (возможно, дублированный), но сервер должен определить, как этот контент.

PUT x(если xидентифицирует ресурс ): «Заменить содержимое ресурса, на которое указывает xмой контент».

PUT x(если xне идентифицирует ресурс): «Создайте новый ресурс, содержащий мой контент, и используйте его xдля идентификации».

POST x: «Храните мой контент и дайте мне идентификатор, который я могу использовать для идентификации ресурса (старого или нового), содержащего указанный контент (возможно, смешанный с другим контентом). Указанный ресурс должен быть идентичным или подчиненным тому, который xидентифицирует». « Ресурс y подчиняется ресурсу x », как правило, но не обязательно реализуется путем превращения y в подпуть x (например, x = /fooи y = /foo/bar) и изменения представления (й) ресурса x , чтобы отразить существование нового ресурса, например с гиперссылкой на yРесурс и некоторые метаданные. Только последний действительно важен для хорошего дизайна, так как URL-адреса непрозрачны в REST - вы должны использовать гипермедиа вместо создания URL-адреса на стороне клиента для обхода сервиса в любом случае.

В REST нет такого понятия, как ресурс, содержащий «контент». Я называю «контентом» данные, которые служба использует для последовательной визуализации представлений. Обычно он состоит из нескольких связанных строк в базе данных или файле (например, в файле изображения). Служба должна преобразовывать контент пользователя во что-то, что может использовать служба, например, преобразовывать полезную нагрузку JSON в операторы SQL.

Оригинальный ответ (может быть легче читать) :

PUT /something(если /somethingуже существует): «Возьми то, что у тебя есть, /somethingи замени это тем, что я тебе даю».

PUT /something(если его /somethingеще нет): «Возьми то, что я тебе даю, и положи на него /something».

POST /something: «Возьми то, что я тебе даю, и положи в любое место, где захочешь, /somethingесли только ты дашь мне его URL».

Иордания
источник
Но как вы можете использовать PUT для создания нового ресурса, если он не существует, в то время как ваш метод генерации идентификатора находится на автоматическом увеличении? Обычно ORM автоматически генерирует для вас идентификатор, например, так, как вы хотите, чтобы он был в POST, например. Означает ли это, что если вы хотите правильно реализовать PUT, вам нужно изменить автогенерацию идентификатора? Это неловко, если ответ - да.
Рони Аксельрад
1
@RoniAxelrad: PUT похож на оператор «INSERT OR UPATE» базы данных, где вы включаете ключ в оператор, поэтому применимо только в том случае, если вы не можете избежать коллизий. например. ваш домен имеет «естественный ключ» или вы используете guid. POST похож на вставку в таблицу с автоинкрементным ключом. База данных должна сообщить вам, какой идентификатор он получил после того, как был вставлен. Обратите внимание, что «INSERT OR UPATE» заменит все предыдущие данные, если они существовали.
Найджел Торн
@NigelThorne Спасибо за ваш ответ. Так что, если, например, я пытаюсь поставить идентификатор книги 10 с URI: PUT books / 10. Если идентификатор книги 10 не существует, я должен создать книгу с идентификатором 10 правильно? но я не могу контролировать числитель идентификатора создания, потому что он автоматически увеличивается. что мне делать в такой ситуации?
Рони Аксельрад
1
@RoniAxelrad REST PUT для несуществующего идентификатора - это запрос к серверу на создание ресурса. Сервер все еще должен решить, хочет ли он это разрешить. Сервер отвечает. Он может ответить «Нет, я не собираюсь этого делать». Вы уже делаете это, если у пользователя недостаточно прав ... и т. Д. Сервер может сказать «нет». REST - это соглашение, которое позволяет нам определять значение различных типов запросов ... ваш сервер решает, что делать с этими запросами, основываясь на вашей бизнес-логике :) Даже если он говорит «нет», он все равно следует REST :)
Найджел Торн
38

Короткий ответ:

Простое правило: используйте POST для создания, PUT для обновления.

Длинный ответ:

ПОСЛЕ:

  • POST используется для отправки данных на сервер.
  • Полезно, когда URL ресурса неизвестен

ПОЛОЖИЛ:

  • PUT используется для передачи состояния на сервер
  • Полезно, когда URL ресурса известен

Более длинный ответ:

Чтобы понять это, нам нужно спросить, зачем требовался PUT, какие проблемы пытался решить PUT, чего не мог POST.

С точки зрения архитектуры REST нет ничего, что имело бы значение. Мы могли бы жить и без PUT. Но с точки зрения разработчика клиента это значительно упростило его жизнь.

До PUT клиенты не могли напрямую знать URL-адрес, сгенерированный сервером, или все, что он сгенерировал, или обновлены ли данные для отправки на сервер или нет. PUT избавил разработчика от всех этих головных болей. PUT является идемпотентом, PUT обрабатывает условия гонки, а PUT позволяет клиенту выбирать URL.

ishandutta2007
источник
3
Ваш короткий ответ может быть ОЧЕНЬ неправильным. HTTP PUT можно повторять через HTTP прокси. И поэтому, если PUT фактически выполняет SQL INSERT, он может завершиться с ошибкой во второй раз, что означает, что он вернет другой результат, и поэтому он не будет IDEMPOTENT (в чем разница между PUT и POST)
Камил Томшик
36

Ruby on Rails 4.0 будет использовать метод PATCH вместо PUT для частичного обновления.

RFC 5789 говорит о PATCH (с 1995 года):

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

« Edge Rails: PATCH - это новый основной метод HTTP для обновлений », объясняет это.

germanlinux
источник
27

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

Если вы не можете полностью доверять клиенту, чтобы он поступал правильно, было бы более целесообразно использовать POST для создания нового элемента, а затем отправлять URL-адрес клиенту в ответе.

сковорода-вор
источник
2
Я немного опоздал на это - но кто-то, говоря что-то похожее на другом сайте, заставил меня щелкнуть по нему. Если вы создаете ресурс и используете автоинкрементный идентификатор в качестве его «идентификатора» вместо имени, назначенного пользователем, это должен быть POST.
Ixmatus
2
Это не совсем верно - PUT все еще может создавать ресурс, ссылаясь на него с неканоническим именем, если в ответе сервер возвращает Locationзаголовок, который содержит каноническое имя ресурса.
Эфир
1
@Joshcodes не забывайте, что вы можете иметь много URI, ссылающихся на один и тот же базовый ресурс. Так что, по словам Эфира, это разумный совет: клиент может поместить PUT на URL (который может быть более семантическим, например PUT /X-files/series/4/episodes/max), а сервер отвечает URI, который предоставляет короткую каноническую уникальную ссылку на этот новый ресурс (то есть /X-Ffiles/episodes/91)
thecoshman
@thecoshman проблема в том, что структура URL не принадлежит клиенту. Чтение о самопознании (также часть REST) ​​может помочь понять это.
Джошкодес
@Joshcodes, по этой логике, клиент никогда не должен использовать PUT для создания, поскольку он не должен заботиться о предоставлении URL. Хорошо ... если только сервер не предоставил URL для PUT, если клиент хочет поместить его ... что-то вроде "PUT / comments / new" и сервер может ответить "204 / comments / 234532", но это выглядит немного RPC для меня, клиент должен просто отправить в / комментарии ...
thecoshman
24

Очень просто, я беру пример с временной шкалы Facebook.

Случай 1: Когда вы публикуете что-то на своей временной шкале, это новая новая запись. Так что в этом случае они используют метод POST, потому что метод POST не идемпотентен.

Случай 2: Если ваш друг прокомментирует ваш пост в первый раз, это также создаст новую запись в базе данных, поэтому будет использоваться метод POST.

Случай 3: если ваш друг редактирует свой комментарий, в этом случае у него есть идентификатор комментария, поэтому он обновит существующий комментарий вместо создания новой записи в базе данных. Поэтому для этого типа операции используйте метод PUT, потому что он идемпотентен. *

В одной строке используйте POST для добавления новой записи в базу данных и PUT для обновления чего-либо в базе данных.

UniCoder
источник
4
Если комментарий является объектом со свойством, таким как идентификатор пользователя, дата создания, комментарий-сообщение и т. Д., И во время редактирования обновляется только комментарий-сообщение, то здесь следует выполнить PATCH?
Habeeb Perwad
PUT используется FB для обновления комментария, потому что обновляется существующий ресурс, и именно это делает PUT (обновляет ресурс). PUT оказывается идемпотентом, в отличие от POST. Глагол HTTP, являющийся идемпотентом, влияет на обработку ошибок, но не диктует использование. См. Мой ответ для более подробного объяснения: stackoverflow.com/questions/630453/put-vs-post-in-rest/…
Joshcodes
22

Наиболее важным фактором является надежность . Если сообщение POST теряется, состояние системы не определено. Автоматическое восстановление невозможно. Для сообщений PUT состояние не определено только до первой успешной попытки.

Например, может быть плохой идеей создавать транзакции по кредитным картам с помощью POST.

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

Некоторые другие соображения:

  • POST делает недействительными кэшированные копии всего содержащего ресурса (лучшая согласованность)
  • Ответы PUT не кэшируются, в то время как ответы POST (требуют Content-Location и expiration)
  • PUT менее поддерживается, например, Java ME, старыми браузерами, брандмауэрами
Ханс Малербе
источник
Это неверно Для POST состояние также не определено только до первой успешной попытки. Затем либо сервер принимает POST (сообщение так и не пришло), выдает конфликт 409 для дублированного идентификатора (сообщение получено, ответ потерян) или любой другой действительный ответ.
Джошкодес
Как правило, пользовательский агент не может безопасно повторить операцию POST, поскольку операция POST не дает такой гарантии, что две операции будут иметь тот же эффект, что и одна. Термин «идентификатор» не имеет ничего общего с HTTP. URI идентифицирует ресурс.
Ганс Малербе
Пользовательский агент может «безопасно» повторить операцию POST столько раз, сколько он хочет. Он просто получит ошибку дублированного идентификатора (при условии, что у ресурса есть идентификатор) или ошибку дублированных данных (при условии, что это проблема, а у ресурса нет идентификаторов).
Джошкодес
Стучит головой о стену. HTTP не имеет решения проблемы надежности, и это не очень хорошо понято, мало обсуждается и просто не учитывается в подавляющем большинстве веб-приложений. @ Joshcodes У меня есть ответ на этот вопрос. Я по сути согласен с Гансом. Есть проблема.
bbsimonbb
@bbsimonbb, HTTP имеет надежный и хорошо документированный набор ответов об ошибках. Мой ответ на этот вопрос ( stackoverflow.com/questions/630453/put-vs-post-in-rest/… ) описывает, как использовать http в соответствии со спецификацией для достижения согласованности.
Джошкодес
17

Читатели, плохо знакомые с этой темой, будут поражены бесконечным обсуждением того, что вы должны делать, и относительным отсутствием уроков из опыта. Тот факт, что REST является «более предпочтительным», чем SOAP, является, я полагаю, высокоуровневым обучением на основе опыта, но, боже, чего мы добились? Это 2016 год. Диссертация Роя была в 2000 году. Что мы разработали? Это было весело? Было ли легко интегрироваться? Поддерживать? Будет ли он справляться с ростом смартфонов и нестабильной мобильной связи?

По словам ME, реальные сети ненадежны. Запрашивает тайм-аут. Соединения сбрасываются. Сети отключаются на часы или дни за раз. Поезда идут в туннели с мобильными пользователями на борту. Для любого данного запроса (как иногда признается во всем этом обсуждении) запрос может упасть в воду на своем пути, или ответ может упасть в воде на обратном пути. В этих условиях выдача запросов PUT, POST и DELETE непосредственно на основные ресурсы всегда казалась мне немного жестокой и наивной.

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

Или вы можете сделать это : рассматривайте ваши небезопасные запросы как эфемерные однопользовательские ресурсы (назовем их действиями). Клиенты запрашивают новое «действие» на основном ресурсе с пустым POST к ресурсу. POST будет использоваться только для этого. Как только надежно завладеет URI только что созданного действия, клиент помещает небезопасный запрос в URI действия, а не в целевой ресурс . Разрешить действие и обновить «реальный» ресурс - это правильно работа вашего API, и здесь он отделен от ненадежной сети.

Сервер выполняет бизнес, возвращает ответ и сохраняет его в соответствии с согласованным URI действия . Если что-то идет не так, клиент повторяет запрос (естественное поведение!), А если сервер его уже видел, он повторяет сохраненный ответ и больше ничего не делает .

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

Лучше всего то, что мы даем отправляющим и принимающим приложениям возможность связать уникально идентифицированное действие с уникальностью в соответствующих средах. И мы можем начать требовать и обеспечивать !, ответственное поведение клиентов: повторять ваши запросы сколько угодно, но не начинайте генерировать новое действие, пока не получите окончательный результат от уже существующего.

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

Последовательные запросы на удаление могут видеть и обрабатывать исходное подтверждение, не вызывая ошибку 404. Если все происходит дольше, чем ожидалось, мы можем ответить временно, и у нас есть место, где клиент может проверить окончательный результат. Самая приятная часть этого паттерна - это его свойство кунг-фу (панда). Мы берем слабость, склонность клиентов повторять запрос каждый раз, когда они не понимают ответ, и превращаем его в силу :-)

Прежде чем сказать мне, что это не RESTful, рассмотрите многочисленные способы соблюдения принципов REST. Клиенты не создают URL. API остается доступным для обнаружения, хотя и с небольшим изменением семантики. HTTP-глаголы используются надлежащим образом. Если вы думаете, что это огромное изменение для реализации, я могу вам сказать по опыту, что это не так.

Если вы думаете, что у вас будет огромное количество данных для хранения, давайте поговорим об объемах: типичное подтверждение обновления составляет доли килобайта. HTTP в настоящее время дает вам одну или две минуты, чтобы ответить окончательно. Даже если вы храните действия только в течение недели, клиенты имеют достаточно шансов наверстать упущенное. Если у вас очень большие объемы, вам может потребоваться выделенное хранилище значений ключей, совместимое с кислотой, или решение в памяти.

bbsimonbb
источник
1
Не будет хранить ответ как поддержание сеанса? Что может вызвать проблемы (горизонтального) масштабирования.
Саураб Харванд
17

В дополнение к различиям, предложенным другими, я хочу добавить еще один.

В методе POST вы можете отправлять параметры тела вform-data

В методе PUT вы должны отправить параметры тела вx-www-form-urlencoded

заголовок Content-Type:application/x-www-form-urlencoded

В соответствии с этим вы не можете отправлять файлы или составные данные методом PUT

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

Тип содержимого «application / x-www-form-urlencoded» неэффективен для отправки больших объемов двоичных данных или текста, содержащего символы не ASCII. Тип контента "multipart / form-data" должен использоваться для отправки форм, которые содержат файлы, данные не ASCII и двоичные данные.

Что означает, если вы должны представить

файлы, данные не в формате ASCII и двоичные данные

Вы должны использовать метод POST

Рохит Дхиман
источник
3
Почему за это не проголосовали? Если это правда, это критическое различие, не так ли?
Iofacture
2
Я сталкивался с этим при реализации API для обновления профиля, который включает загрузку изображения профиля пользователя. Затем я протестировал его с почтальоном, Ajax, PHP curl и laravel 5.6 в качестве бэкэнда.
Рохит Дхиман
14

Кажется, всегда есть некоторая путаница относительно того, когда использовать HTTP POST по сравнению с методом HTTP PUT для служб REST. Большинство разработчиков будут пытаться связать операции CRUD напрямую с методами HTTP. Я буду утверждать, что это не правильно, и нельзя просто связать концепции CRUD с методами HTTP. Это:

Create => HTTP PUT
Retrieve => HTTP GET
Update => HTTP POST
Delete => HTTP DELETE

Это правда, что R (etrieve) и D (elete) операций CRUD могут быть сопоставлены напрямую с HTTP-методами GET и DELETE соответственно. Однако путаница заключается в операциях C (reate) и U (update). В некоторых случаях можно использовать PUT для создания, в то время как в других случаях потребуется POST. Неоднозначность заключается в определении метода HTTP PUT по сравнению с методом HTTP POST.

Согласно спецификации HTTP 1.1 методы GET, HEAD, DELETE и PUT должны быть идемпотентными, а метод POST не идемпотентными. То есть операция является идемпотентной, если она может быть выполнена на ресурсе один или несколько раз и всегда возвращать одно и то же состояние этого ресурса. Тогда как неидемпотентная операция может возвращать измененное состояние ресурса из одного запроса в другой. Следовательно, в неидемпотентной операции нет гарантии, что человек получит такое же состояние ресурса.

Основываясь на приведенном выше идемпотентном определении, я использую метод HTTP PUT по сравнению с методом HTTP POST для служб REST: Используйте метод HTTP PUT, когда:

The client includes all aspect of the resource including the unique identifier to uniquely identify the resource. Example: creating a new employee.
The client provides all the information for a resource to be able to modify that resource.This implies that the server side does not update any aspect of the resource (such as an update date).

В обоих случаях эти операции могут выполняться несколько раз с одинаковыми результатами. То есть ресурс не будет изменен путем запроса операции более одного раза. Следовательно, истинная идемпотентная операция. Используйте метод HTTP POST, когда:

The server will provide some information concerning the newly created resource. For example, take a logging system. A new entry in the log will most likely have a numbering scheme which is determined on the server side. Upon creating a new log entry, the new sequence number will be determined by the server and not by the client.
On a modification of a resource, the server will provide such information as a resource state or an update date. Again in this case not all information was provided by the client and the resource will be changing from one modification request to the next. Hence a non idempotent operation.

Вывод

Не коррелируйте напрямую и не сопоставляйте операции CRUD с методами HTTP для служб REST. Использование метода HTTP PUT по сравнению с методом HTTP POST должно основываться на идемпотентном аспекте этой операции. То есть, если операция идемпотентна, используйте метод HTTP PUT. Если операция не идемпотентна, используйте метод HTTP POST.

Бурхан
источник
2
Обновление => HTTP POST: POST не для обновления
Premraj
@premraj Вы сделали предположение, что Бурхан говорит вам не делать; а именно, вы объединяете CRUD, REST и HTTP. Если вы прочитаете RFC 7231, где эти вещи определены, вы обнаружите, что в протоколе HTTP определение POST, безусловно, позволяет обновлять. Только ограничения REST говорят об обратном.
IAM_AL_X
13

исходный сервер может создать ресурс с этим URI

Таким образом, вы используете POST и, возможно, но не PUT для создания ресурса. Вам не нужно поддерживать оба. Для меня POST вполне достаточно. Так что это дизайнерское решение.

Как упоминалось в вашей цитате, вы используете PUT для создания ресурса, которому IRI не назначен, и вы все равно хотите создать ресурс. Например, PUT /users/123/passwordобычно заменяет старый пароль новым, но вы можете использовать его для создания пароля, если он еще не существует (например, недавно зарегистрированными пользователями или восстановлением заблокированных пользователей).

inf3rno
источник
Я думаю, что вам удалось привести один из немногих хороших примеров того, как использовать PUT, молодец.
Thecoshman
12

Я собираюсь приземлиться со следующим:

PUT относится к ресурсу, идентифицированному URI. В этом случае вы обновляете его. Это часть трех глаголов, относящихся к ресурсам - удали и получи два других.

POST - это в основном сообщение в свободной форме, его значение определяется как «out of band». Если сообщение можно интерпретировать как добавление ресурса в каталог, это будет нормально, но в основном вам нужно понимать сообщение, которое вы отправляете (публикуете), чтобы знать, что произойдет с ресурсом.


Поскольку PUT, GET и DELETE относятся к ресурсу, они также по определению идемпотентны.

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

PUT не должен быть творением; служба может выдать ошибку, если ресурс еще не создан, но в противном случае обновите его. Или наоборот - он может создавать ресурс, но не разрешать обновления. Единственное, что требуется от PUT - это то, что он указывает на конкретный ресурс, а его полезная нагрузка - это представление этого ресурса. Успешный PUT означает (исключая помехи), что GET будет получать тот же ресурс.


Изменить: еще одна вещь - PUT может создавать, но если это так, то идентификатор должен быть естественным ID - AKA адрес электронной почты. Таким образом, когда вы PUT дважды, второй пут - это обновление первого. Это делает его идемпотентным .

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

Жерар ОНЕЙЛ
источник
9

Предполагается, что семантика различна, в том смысле, что «PUT», как и «GET», считается идемпотентным - это означает, что один и тот же точный запрос PUT можно выполнить несколько раз, и в результате вы получите такой же результат, как если бы вы выполнили его только один раз.

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

Когда вы помещаете ресурс по определенному URL-адресу, происходит следующее: он должен сохраняться по этому URL-адресу или что-то в этом роде.

Когда вы размещаете POST на ресурсе по определенному URL-адресу, вы часто публикуете связанную информацию на этот URL-адрес. Это означает, что ресурс по URL уже существует.

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

Что касается изменения свойств потока, вы можете сделать это с помощью PUT или POST. В основном, используйте «PUT» только тогда, когда операция идемпотентна, в противном случае используйте POST.

Однако обратите внимание, что не все современные браузеры поддерживают HTTP-глаголы, кроме GET или POST.

Григорий Магаршак
источник
То, что вы описываете POST как на самом деле, как должен вести себя PATCH. Предполагается, что POST означает что-то вроде «добавить», как в «сообщении в список рассылки».
Александр Торстлинг
8

Большую часть времени вы будете использовать их так:

  • POST ресурс в коллекцию
  • PUT ресурс, идентифицируемый коллекцией /: id

Например:

  • ПОСТ / предметы
  • PUT / items / 1234

В обоих случаях тело запроса содержит данные для ресурса, который будет создан или обновлен. Из названий маршрутов должно быть очевидно, что POST не идемпотентен (если вы вызываете его 3 раза, это создаст 3 объекта), но PUT является идемпотентом (если вы вызываете его 3 раза, результат одинаков). PUT часто используется для операции «upsert» (создание или обновление), но вы всегда можете вернуть ошибку 404, если хотите использовать ее только для изменения.

Обратите внимание, что POST «создает» новый элемент в коллекции, а PUT «заменяет» элемент по заданному URL-адресу, но это очень распространенная практика - использовать PUT для частичных изменений, то есть использовать его только для обновления существующих ресурсов и изменять только включенные поля в теле (игнорируя другие поля). Это технически неверно, если вы хотите быть REST-пуристом, PUT должен заменить весь ресурс, и вы должны использовать PATCH для частичного обновления. Лично меня не волнует, насколько поведение ясное и согласованное на всех ваших конечных точках API.

Помните, что REST - это набор соглашений и руководств, упрощающих работу вашего API. Если у вас сложный обходной путь только для того, чтобы установить флажок «RESTfull», значит, вы победили цель;)

tothemario
источник
7

Хотя, вероятно, существует агностический способ их описания, он, похоже, вступает в противоречие с различными утверждениями из ответов на веб-сайты.

Давайте будем очень ясными и прямыми. Если вы - разработчик .NET, работающий с Web API, факты (из документации Microsoft API), http://www.asp.net/web-api/overview/creating-web-apis/creating-a-web -api-that-поддерживает-crud-операции :

1. PUT = UPDATE (/api/products/id)
2. MCSD Exams 2014 -  UPDATE = PUT, there are **NO** multiple answers for that question period.

Конечно, вы «можете» использовать «POST» для обновления, но просто следуйте соглашениям, изложенным для вас с вашей данной платформой. В моем случае это .NET / Web API, поэтому PUT для ОБНОВЛЕНИЯ не обсуждается.

Я надеюсь, что это поможет любым разработчикам Microsoft, которые читают все комментарии со ссылками на сайты Amazon и Sun / Java.

Том Стиккель
источник
7

Вот простое правило:

PUT to URL должен использоваться для обновления или создания ресурса, который может быть расположен по этому URL.

POST для URL-адреса должен использоваться для обновления или создания ресурса, который расположен по какому-то другому («подчиненному») URL-адресу или не может быть обнаружен через HTTP.

Адам Гриффитс
источник
1
PUT не для обновления, это для замены, обратите внимание, что для создания вы ничего не заменяете чем-то. POST абсолютно не для обновления в любой форме.
Thecoshman
2
В спецификации http это сказано? Или вы основываете свой комментарий на чем-то еще?
Адам Гриффитс
Это просто здравый смысл, как вы обновляете что-то, когда не знаете, что именно вы обновляете? POST для создания нового ресурса.
thecoshman
2
thecoshman - здесь вы злоупотребляете семантикой - замена может быть обновлением, если это тот же ресурс с небольшими отличиями. Замена действительна только для пут, если замена используется для изменения того же ресурса. Замена новым и другим ресурсом недопустима (удалить старый и добавить новый?), Особенно если у «нового» ресурса нет естественного идентификатора. POST, OTOH, это то, что может создавать, обновлять, заменять и удалять - использование сообщения зависит от того, есть ли сообщение для интерпретации, например, «применить скидку», которое может изменить или не изменить ресурс в зависимости от логика.
Джерард ONeill
Что касается вашего второго комментария - как насчет того, чтобы «получить» ресурс, изменить необходимые поля и затем вернуть его обратно? Или как насчет того, если ресурс исходит из другого источника, но использует естественный идентификатор (внешний идентификатор) - команда put естественным образом обновит ресурс по URL-адресу при изменении исходных данных.
Джерард ONeill
6

Если вы знакомы с операциями с базами данных, есть

  1. Выбрать
  2. Вставить
  3. Обновить
  4. удалять
  5. Объединить (обновить, если уже существует, еще вставить)

Я использую PUTдля слияния и обновления как операции и использовать POSTдля вставок.

Rajan
источник
5

На практике POST хорошо работает для создания ресурсов. URL вновь созданного ресурса должен быть возвращен в заголовке ответа Location. PUT следует использовать для полного обновления ресурса. Пожалуйста, поймите, что это лучшие практики при разработке RESTful API. Спецификация HTTP как таковая не ограничивает использование PUT / POST с некоторыми ограничениями для создания / обновления ресурсов. Взгляните на http://techoctave.com/c7/posts/71-twitter-rest-api-dissected , где обобщены лучшие практики.

java_geek
источник
По большей части, прочитав весь этот шум, вы, кажется, на высоте. Я бы сказал, что мы должны ссылаться на PUT как на метод замены, а не на создание / обновление. Я думаю, что лучше описать в одном, что он делает.
Thecoshman