PUT vs. POST в REST


5371

Відповідно до специфікації 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.
Маріус Бутук

37
Мені здається, що ця дискусія виникла із загальної практики надпрощення REST шляхом опису методів HTTP з точки зору операцій CRUD.
Ступорман

5
На жаль, перші відповіді неправдиві щодо POST. Перевірте моя відповідь для кращого пояснення відмінностей: stackoverflow.com/a/18243587/2458234
7hi4g0

23
PUT і POST - небезпечні методи. Однак PUT є безсильним, тоді як POST - ні. - Див. Більше на: restcookbook.com/HTTP%20Methods/put-vs-post/…
Дінеш

Відповіді:


4237

Загалом:

І PUT, і POST можуть використовуватися для створення.

Ви повинні запитати "для чого ви виконуєте дію?" щоб розрізнити, що вам слід використовувати. Припустимо, ви розробляєте API для задавання питань. Якщо ви хочете використовувати POST, ви зробите це до списку питань. Якщо ви хочете використовувати PUT, тоді ви зробите це для певного питання.

Чудові обидва можуть бути використані, тож який я повинен використовувати в своєму RESTful дизайні:

Вам не потрібно підтримувати і PUT, і POST.

Що використовується, залишається за вами. Але просто не забудьте скористатися правильним залежно від того, на який об’єкт ви посилаєтесь у запиті.

Деякі міркування:

  • Ви називаєте об’єкти URL-адрес, які ви створюєте явно, або дозволяєте серверу вирішити? Якщо ви їх назвали, то використовуйте PUT. Якщо ви дозволите серверу вирішити, використовуйте POST.
  • PUT ідентичний, тому якщо ви двічі PUT об'єкт, це не має ефекту. Це хороша властивість, тому я б використовував PUT, коли це можливо.
  • Ви можете оновити або створити ресурс за допомогою PUT з тією ж URL-адресою об’єкта
  • З POST ви можете мати два запити, що надходять одночасно, вносячи зміни до URL-адреси, і вони можуть оновлювати різні частини об’єкта.

Приклад:

Я написав наступне як частину іншої відповіді на ТА щодо цього :

POST:

Використовується для зміни та оновлення ресурсу

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> Спершу слід PUT ресурс на сервері.

Ви можете хоч щось подібне зробити для створення ресурсів за допомогою POST:

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

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

PUT:

Використовується для створення ресурсу або його замінення. Поки ви вказуєте на ресурсах нову URL-адресу.

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

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

Щоб замінити існуючий ресурс:

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

Крім того, і трохи більш стисло, RFC 7231, розділ 4.3.4, стан PUT (наголос додано),

4.3.4. ПУТ

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


1026
Я думаю, що не можна достатньо наголосити на тому, що PUT ідентичний: якщо мережа перебуває в підключенні, і клієнт не впевнений, чи отримав його запит, він може просто надіслати його другий (або 100-й) раз, і це гарантується HTTP специфікує, що це має точно такий же ефект, як надсилання одного разу.
Йорг W Міттаг

77
@ Йорг W Міттаг: Не потрібно. Другий раз може повернути 409 конфлікт чи щось, якщо запит був змінений тим часом (іншим користувачем або самим першим запитом, який пройшов через).
Мітар

632
Якщо я не помиляюся, наголосити на тому, що PUT визначається як ідентичний. Ви все ще повинні записати свій сервер таким чином, щоб PUT поводився правильно, так? Можливо, краще сказати: "PUT змушує транспорт приймати ідентифікацію, що може вплинути на поведінку транспорту, наприклад кешування".
Ян Ні-Льюїс

150
@ JörgWMittag Ідентифікаційна грамота? Як щодо "Надіслати та надіслати та надіслати мого друга, це зрештою не має ніякого значення".
Джеймс Бенінгер

39
Думає про них як: PUT = вставити або оновити; POST = вставити. Отже, коли ви робите два PUT - ви отримуєте один новий запис, коли ви робите два POST - ви отримуєте два нові записи.
Євген Конков

2218

Ви можете знайти твердження в Інтернеті, які говорять

Жоден з них не зовсім правильний.


Краще вибирати між PUT та POST на основі ідентичності потенціалу.

PUT передбачає розміщення ресурсу - повністю замінюючи все, що доступне за вказаною URL-адресою, іншим. За визначенням, PUT є безсильним. Робіть це скільки завгодно разів, і результат той самий. x=5є безсильним. Ви можете надіслати ресурс, чи він раніше існував, чи ні (наприклад, для створення або оновлення)!

POST оновлює ресурс, додає допоміжний ресурс або викликає зміни. POST не є ідентичним, таким чином, який x++не є ідентичним.


За цим аргументом PUT призначений для створення, коли ви знаєте URL-адресу речі, яку ви створите. POST можна використовувати для створення, коли вам відома URL-адреса "фабрики" або менеджера для тієї категорії речей, які ви хочете створити.

тому:

POST /expense-report

або:

PUT  /expense-report/10929

72
Я погоджуюсь, де б це не стосувалося імепотенції, вона повинна перешкоджати будь-яким іншим проблемам, оскільки помилка може спричинити багато несподіваних помилок.
Джош

16
Якщо POST може оновити ресурс, то як це не ідентично? Якщо я міняю вік учнів за допомогою PUT і роблю це в 10 разів більше, ніж вік учнів, це те саме, як я робив це один раз.
Джек Уклея

28
@Schneider, у цьому випадку ваш сервер докладає додаткових зусиль, щоб гарантувати ідентифікацію, але він не рекламує це. Браузери все ще попереджають користувача, якщо він спробує перезавантажити такий POST-запит.
Тобу

47
@Schneider POST може створити допоміжний ресурс; отже, ви можете POST для збору, як POST / cost-report, і це створило б стільки сутностей (звітів про витрати) на вашому сервері, скільки кількість відправлених запитів, навіть якщо вони повністю схожі. Подумайте про те, як вставити той самий рядок у таблицю БД (/ витрати-звіти) з автоматичним збільшенням первинного ключа. Дані залишаються тими ж, ключ (URI в даному випадку) генерується сервером і відрізняється від усіх інших вставок (запиту). Отже, POST-ефект може бути безсильним, але також не може . Отже, POST не є безсильним.
Snifff

11
Скажімо, у нас є сутності, які можуть мати дві властивості - nameі date. Якщо у нас є об'єкт із існуючим nameі date, але потім надсилаємо до нього запити із зазначенням лише a name, правильна поведінка PUT полягала б у видаленні dateоб'єкта, тоді як POST може оновлювати лише вказані властивості, залишаючи невизначені властивості такими, якими вони були до того, як було зроблено запит. Це звучить правильно / розумно, чи це неправильне використання PUT (я побачив посилання на PATCH , що, здається, було б більш доречним, але ще не існує)?
Jon z

707
  • POST на URL-адресу створює дочірній ресурс у визначеній сервером URL-адресі.
  • PUT до URL-адреси створює / замінює ресурс у повному обсязі за визначеною клієнтом URL-адресою.
  • PATCH до 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 without PUT” ідея полягає в тому, що споживачі змушені розміщувати нові “неоновлені” ресурси запиту. Як було обговорено раніше, зміна поштової адреси клієнта є POST на новий ресурс "ChangeOfAddress", а не PUT ресурсу "Клієнт" з іншим значенням поля поштової адреси.

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

Це змушує API уникати проблем з переходом стану, коли декілька клієнтів оновлюють один ресурс, а також краще узгоджуються з пошуком подій та CQRS. Коли робота виконується асинхронно, відмінювання перетворення та очікування його застосування здається доцільним.


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 

POST

Створює А новий ресурс під / ресурсів URI, або колекції . Зазвичай ідентифікатор повертається сервером.

POST /resources HTTP/1.1

Оновлення:

Можна виконати лише PUT таким чином:

ПУТ

Оновляє ресурс наявнимResourceId як ідентифікатором, під / ресурсом URI або колекцією .

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

він буде працювати, але ви семантично заявляєте, що хочете додати ресурс до колекції John під колекцією користувачів .

Після використання PUT ви переходите до ресурсу або одного елемента, можливо всередині колекції . Отже, коли ви кажете:

PUT /users/john HTTP/1.1

ви повідомляєте оновлення сервера або створюєте, якщо його не існує, ресурс john в колекції користувачів .

Специфікація:

Дозвольте мені виділити деякі важливі частини специфікації:

POST

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

Отже, створюється новий ресурс на колекції .

ПУТ

Метод PUT вимагає, щоб вкладений об'єкт зберігався під наданим URI-запитом. Якщо Request-URI посилається на вже існуючий ресурс, додане об'єкт ДОЛЖНЕ розглядатися як модифікована версія тієї, що знаходиться на сервері походження. Якщо запит-URI не вказує на існуючий ресурс і URI може бути визначений новим ресурсом запитуючим агентом користувача, сервер-джерело може створити ресурс із цим URI. "

Отже, створюйте або оновлюйте на основі існування ресурсу .

Довідка:


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, оскільки ви хочете замінити / створити конкретного користувача.

Двічі розміщення з однаковими даними означає створення двох однакових користувачів з різними ідентифікаторами. Двічі PUTING з одними і тими ж даними створює користувача першим і вдруге оновлює його в одному стані (без змін). Оскільки ви потрапляєте в один і той же стан після PUTтого, скільки разів ви його виконуєте, кожен раз, як кажуть, він "однаково потужний". Це корисно для автоматичного повторного повторного запиту. Більше "чи ви впевнені, що хочете повторно", коли натискаєте кнопку "назад" у веб-переглядачі.

Загальна порада - використовувати, POSTколи вам потрібно, щоб сервер мав контроль над URLгенеруванням своїх ресурсів. Використовуйте PUTінакше. Віддавайте перевагу PUT над POST.


12
Неслухняність може призвести до того, що зазвичай вчать, що вам потрібні лише два дієслова: GET і POST. ЗАРАЗ отримати, POST змінити. Навіть PUT і DELETE виконувались за допомогою POST. Запитуючи, що PUT насправді означає на 25 років пізніше, можливо, знак ми спочатку навчилися неправильно. Популярність REST привернула людей до тих основ, де тепер ми повинні вивчити минулі погані помилки. Пост був надмірно використаний, і тепер його зазвичай навчають неправильно. Найкраща частина: "Розміщення двічі з однаковими даними означає створення двох однакових [ресурсів]". Чудова точка!
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, коли ви знаєте "id", за допомогою якого можна отримати об'єкт, який ви зберігаєте. Використання 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

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також діє для створення ресурсу, але лише в тому випадку, якщо клієнт має привілей називати ресурс . (Чи дозволяє Rails клієнту цю привілей іменування?)
DavidRR

123

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

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

Аналогія:

  • ПУТ, тобто візьміть і покладіть, де було.
  • POST як відправлення пошти у поштовому відділенні.

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

Аналогія соціальних медіа / мереж:

  • Публікація в соціальних мережах: коли ми публікуємо повідомлення, це створює нову публікацію.
  • Помістіть (тобто редагуйте) повідомлення, яке ми вже опублікували.

21
@MobileMon Ні, методи REST не є CRUD.
jlr

1
Я б сказав PUT для UPSERTS
Hola Soy Edu Feliz Navidad

@MobileMon ні: POST, коли ви створюєте новий ресурс, і ви не знаєте кінцевої кінцевої точки, щоб отримати його. ПУТ для інших випадків.
Портекой

67

REST - це концепція дуже високого рівня. Насправді він взагалі навіть не згадує HTTP!

Якщо у вас є сумніви щодо того, як реалізувати REST в HTTP, ви завжди можете ознайомитися зі специфікацією протоколу публікації Atom (AtomPub) . AtomPub - це стандарт для написання RESTful веб-сервісів з HTTP, який був розроблений багатьма світликами HTTP та REST, з деяким вкладом від Роя Філдінга, винахідника REST та самого співавтора HTTP.

Насправді, можливо, ви навіть зможете використовувати AtomPub безпосередньо. Незважаючи на те, що він вийшов із спільноти блогів, він жодним чином не обмежується веденням блогу: це загальний протокол для REST-взаємодії з довільними (вкладеними) колекціями довільних ресурсів через HTTP. Якщо ви можете представити свою програму як вкладену колекцію ресурсів, ви можете просто використовувати AtomPub і не турбуватися про те, чи використовувати PUT або POST, які коди статусу HTTP повернути та всі ці деталі.

Це те, що AtomPub має сказати про створення ресурсів (розділ 9.2):

Щоб додати членів до колекції, клієнти надсилають POST-запити до URI колекції.


8
Немає нічого поганого в тому, щоб дозволити PUT створювати ресурси. Просто пам’ятайте, що це означає, що клієнт надає URL-адресу.
Джуліян Решке

5
Щось дуже не так у дозволі PUT створювати ресурси: клієнт надає URL-адресу. Оце робота сервера!
Joshcodes

@Joshcodes Не завжди буває так, що завданням сервера є створення ідентифікаторів клієнта. Я все частіше бачу конструкції, які дозволяють клієнтам генерувати якийсь UUID як ідентифікатор ресурсу. Така конструкція, зокрема, піддається збільшенню масштабів.
Джастін Омс

@JustinOhms Я погоджуюся з вашою думкою щодо ідентифікаторів, згенерованих клієнтом (бічна примітка: всі системи, розроблені мною з приблизно 2008 року, вимагають від клієнта створити ідентифікатор як UUID / Guid). Це не означає, що клієнт повинен вказати URL-адресу.
Joshcodes

1
Так, якщо ресурс вже існує, використовуйте PUT. Однак майже у всіх випадках ресурси слід створювати за допомогою POST, а клієнт не повинен надавати URL-адресу. Рой Філдінг погоджується з цим твердженням FWIW: roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
Joshcodes

61

Рішення про використання PUT або POST для створення ресурсу на сервері з HTTP + REST API ґрунтується на тому, кому належить структура URL. Маючи клієнта знати або брати участь у визначенні, структура URL - це непотрібне з'єднання, подібне до небажаних з’єднань, що виникли з SOA. Утеплення типів муфт - причина REST настільки популярна. Тому правильним методом використання є POST. З цього правила є винятки, і вони трапляються, коли клієнт бажає зберегти контроль над структурою розташування ресурсів, які він використовує. Це рідко і, ймовірно, означає, що щось інше не так.

У цей момент деякі люди будуть стверджувати, що якщо використовується RESTful-URL , клієнт дійсно знає URL-адресу ресурсу, і тому PUT є прийнятним. Зрештою, саме тому канонічні, нормалізовані, URL-адреси Ruby on Rails, Django є важливими, подивіться на API Twitter ... бла-бла-бла. Тим людям потрібно зрозуміти, що немає такого поняття, як 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. Клієнт розміщує новий ресурс на сервері.
  2. Сервер обробляє запит і надсилає відповідь.
  3. Клієнт ніколи не отримує відповіді.
  4. Сервер не знає, клієнт не отримав відповіді.
  5. У клієнта немає URL-адреси ресурсу (тому PUT не є варіантом) і повторює POST.
  6. POST не ідентичний, і сервер ...

Крок 6 - це, коли люди зазвичай плутаються, що робити. Однак немає підстав створювати хитрощі для вирішення цього питання. Натомість HTTP можна використовувати, як зазначено в RFC 2616, а сервер відповідає:

10.4.10 409 Конфлікт

Запит не вдалося виконати через конфлікт із поточним станом ресурсу. Цей код дозволений лише в тих випадках, коли очікується, що користувач зможе вирішити конфлікт і подати повторно запит. Орган відповіді ДОЛЖЕН би включити достатньо

інформація для користувача, щоб визнати джерело конфлікту. В ідеалі суб'єкт відповіді повинен містити достатню кількість інформації для користувача або агента користувача, щоб вирішити проблему; однак це може бути неможливим і не потрібно.

Конфлікти, швидше за все, виникають у відповідь на запит PUT. Наприклад, якщо використовувались версії і сутність PUT включала зміни до ресурсу, які суперечать тим, які були зроблені попереднім (стороннім) запитом, сервер може використовувати відповідь 409, щоб вказати, що він не може виконати запит . У цьому випадку суб'єкт відповіді, ймовірно, міститиме список відмінностей між двома версіями у форматі, визначеному відповіддю Content-Type.

Відповідь за допомогою коду статусу 409 Conflict - це правильний спосіб звернення, оскільки :

  • Виконання 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 слід використовувати для створення.

Детальніше про це читайте в цій статті .


Чи відповідь 409 Conflict буде відповідним кодом для чогось подібного до спроби створити новий обліковий запис із ім’ям користувача, яке вже існує? Я спеціально використовував 409 для конфліктування версій, але, прочитавши вашу відповідь, мені цікаво, чи не слід його використовувати для будь-яких "дублюючих" запитів.
Ерік Б.

@EricB. Так, у ситуації, яку ви описуєте, "через конфлікт із поточним станом ресурсу" операція не вдається. Крім того, доцільно розраховувати, що користувач може вирішити конфлікт, і тілу повідомлення потрібно лише повідомити користувача про те, що ім'я користувача вже існує.
Joshcodes

@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-адрес, це менше в плані речей.

3
"POST також може використовуватися для неідентичних оновлень існуючих об'єктів (особливо, зміни частини об'єкта без уточнення всієї речі". Для цього призначено PATCH
Snuggs

48

Коротко:

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

POST - неідентичний, де стан ресурсу може стати різним, якщо операція виконується кілька разів порівняно з виконанням одного разу.

Аналогія з запитом до бази даних

PUT Ви можете придумати подібне до "UPDATE STUDENT SET address =" abc ", де id =" 123 ";

POST Ви можете придумати щось на кшталт "ВСТАВКА В СТУДЕНТ (ім'я, адреса) VALUES (" abc "," xyzzz ");

Ідентифікатор студента створюється автоматично.

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

У випадку POST, якщо один і той же запит виконується кілька разів, тоді в базі даних створюються кілька записів Student, а стан бази даних змінюється при кожному виконанні запиту "INSERT".

ПРИМІТКА: PUT потребує розташування ресурсу (вже-ресурс), для якого має відбуватися оновлення, тоді як POST цього не потребує. Тому інтуїтивно POST призначений для створення нового ресурсу, тоді як PUT необхідний для оновлення вже наявного ресурсу.

Деякі можуть придумати, що оновлення можна виконувати за допомогою POST. Не існує жодного жорсткого правила, яке з них використовувати для оновлень або яке для створення. Знову це конвенції, і я інтуїтивно схиляюсь до вищезгаданих міркувань і дотримуюсь цього.


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

Put також може використовуватися для INSERT. Наприклад, якщо сервер виявив, що ви намагалися завантажити один і той самий файл кілька разів, це зробить ваш запит ідентичним. (Не завантажуються нові файли).
kiwicomb123

43

POST - це як надсилати лист до поштової скриньки або розміщувати повідомлення в черзі електронної пошти. PUT - це як коли ви кладете предмет в отвір для кубиків або місце на полиці (він має відому адресу).

З POST ви надсилаєте повідомлення на адресу ЧАСТИНИ або КОЛЕКЦІЇ. За допомогою PUT ви ставите на адресу ITEM.

PUT ідентичний. Ви можете відправити запит 100 разів, і це не матиме значення. POST не є ідентичним. Якщо Ви надсилаєте запит 100 разів, Ви отримаєте 100 листів або 100 листів у Вашій поштовій скриньці.

Загальне правило: якщо ви знаєте ідентифікатор або назву елемента, використовуйте PUT. Якщо ви хочете, щоб ідентифікатор або ім'я предмета було призначено стороною, що приймає, використовуйте POST.

POST порівняно з PUT


1
Ні, PUT означає, що ви знаєте URL-адресу. Якщо ви знаєте лише ідентифікатор, тоді розміщуйте POST з цим ідентифікатором, щоб отримати URL-адресу.
Joshcodes

6
Ідентифікатор є частиною URL-адреси, тому так, використовуйте PUT, якщо вам відома URL-адреса (яка включає ідентифікатор).
Гомер6

Ні, URL-адреса визначається сервером, ідентифікатор не обов'язково є частиною URL-адреси. Рой Філдінг сказав би вам те саме, або ви могли просто прочитати його тезу .
Joshcodes

@Joshcodes, це припускаючи REST? У архітектурі RESTful ідентифікатор елемента, безумовно, є частиною URL-адреси, як у: / people / 123. Мені подобається цей сайт для відпочинку: microformats.org/wiki/rest/urls
Біз

1
@Beez посилання mircoformats пропонує хороший спосіб структурувати свої URL-адреси серверам, але сервер визначає URL-адресу. Клієнт, який ніколи не робить. Дивіться мою відповідь або пов’язану статтю, якщо ви цього не розумієте.
Joshcodes

39

Нова відповідь (тепер, коли я краще розумію REST):

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

PUT x(якщо xідентифікує ресурс ): "Замініть вміст ресурсу, ідентифікований xмоїм вмістом."

PUT x(якщо xне визначає ресурс): "Створіть новий ресурс, що містить мій вміст, і використовуйте xдля його ідентифікації."

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

У REST немає такого поняття, як ресурс, що містить "контент". Я називаю "вмістом" дані, які сервіс використовує для послідовного представлення представлень. Зазвичай складається з деяких суміжних рядків у базі даних чи файлах (наприклад, файл зображення). Служба залежить від перетворення вмісту користувача у те, що служба може використовувати, наприклад, перетворення корисного навантаження JSON в оператори SQL.

Оригінальна відповідь (може бути простішою для читання) :

PUT /something(якщо /somethingвже існує): "Візьміть все, що у вас є, /somethingі замініть його тим, що я вам даю".

PUT /something(якщо її /somethingвже немає): "Візьміть те, що я вам даю, і покладіть на це /something".

POST /something: "Візьміть те, що я вам даю, і поставте його куди завгодно /something, доки ви не дасте мені його URL-адреси, коли закінчите."


Але як ви можете використовувати PUT для створення нового ресурсу, якщо його не існує, тоді як ваш метод створення ідентифікатора знаходиться на автоматичному збільшенні? Зазвичай ORM автоматично генерує ідентифікатор для вас, як, наприклад, ви хочете, щоб він був у POST, наприклад. Чи означає це, що якщо ви хочете реалізувати PUT правильним способом, вам доведеться змінити свій автоматичний генератор id? Це незручно, якщо відповідь - так.
Роні Аксельрад

1
@RoniAxelrad: PUT - це як вислів бази даних "ВСТАВИТЬ АБО ОНОВЛЕНО", де ви включаєте ключ у виписку, тому застосовується лише там, де ви не можете зіткнутись із зіткненнями. напр. у вашому домені є "природний ключ" або ви використовуєте настанови. POST - це як вставляти в таблицю ключ з автоматичним збільшенням. Вам потрібно повідомити базі даних, який ідентифікатор він отримав після його вставки. Зверніть увагу, що "ВСТАВИТЕ АБО ОНОВЛЕНО" замінить будь-які попередні дані, якщо вони існували.
Найджел Торн

@NigelThorne Дякую за вашу відповідь. Так, наприклад, якщо я намагаюся ввести UID книги 10 за допомогою URI: PUT books / 10. Якщо ідентифікатор книги 10 не існує, я повинен створити книгу з ідентифікатором 10 правильно? але я не можу контролювати чисельник ідентифікатора створення, тому що це автоматичне збільшення. що мені робити в цій ситуації?
Роні Аксельрад

1
@ RoniAxelrad REST PUT до ідентифікатора, який не існує, - це запит до сервера про створення ресурсу. Сервер все ще вирішує, чи хоче він це дозволити. Сервер відповідає. Це може відповісти "Ні. Я цього не збираюся робити". Ви вже робите це, якщо у користувача недостатньо дозволів ... тощо. Це нормально, щоб сервер сказав "Ні". REST - це умова, яка дозволяє нам визначати значення різних типів запиту ... ваш сервер вирішує, що робити з тими запитами, виходячи з вашої ділової логіки :) Навіть якщо він каже "ні", це все-таки слід REST :)
Найджел Торн

38

Коротка відповідь:

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

Довга відповідь:

POST:

  • POST використовується для надсилання даних на сервер.
  • Корисно, коли URL-адреса ресурсу невідома

PUT:

  • PUT використовується для передачі стану на сервер
  • Корисно, коли відома URL-адреса ресурсу

Більш довгий відповідь:

Щоб зрозуміти це, нам потрібно запитати, чому потрібен PUT, які проблеми PUT намагався вирішити, що POST не зміг.

З точки зору архітектури REST немає жодного значення. Ми могли б жити і без PUT. Але з точки зору розробника клієнта це зробило його життя набагато простішим.

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


3
Ваша коротка відповідь може бути ДУЖЕ неправильною. HTTP PUT може повторюватися проксі-серверами HTTP. І так, якщо PUT насправді робить SQL INSERT, він може вийти з ладу вдруге, а це означає, що він поверне інший результат, і тому це не буде IDEMPOTENT (що є різницею між PUT та POST)
Kamil Tomšík

36

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

RFC 5789 говорить про PATCH (з 1995 року):

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

" Edge Rails: PATCH - це новий основний метод HTTP для оновлень ", пояснює це.


27

Загрожуючи перезавантажити все, що вже було сказано, важливо пам’ятати, що PUT означає, що клієнт контролює те, що буде в результаті URL-адреси під час створення ресурсу. Таким чином, частина вибору між PUT та POST буде полягати в тому, наскільки ви можете довірити клієнту надання правильної, нормалізованої URL-адреси, яка є узгодженою з будь-якою схемою URL-адрес.

Якщо ви не можете повністю довіритись клієнту зробити все правильно, було б доцільніше використовувати POST для створення нового елемента, а потім відправити URL-адресу назад клієнту у відповідь.


2
Я трохи запізнююся на це - але хтось, що щось інше говорить на іншому веб-сайті, змусив мене натиснути. Якщо ви створюєте ресурс і використовуєте автоматично збільшений ідентифікатор як його "ідентифікатор" замість призначеного користувачем імені, це повинен бути POST.
Іксмат

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

@Joshcodes тоді за такою логікою клієнт ніколи не повинен використовувати PUT для створення, оскільки вони не повинні мати справу з наданням URL-адреси. Ну ... якщо сервер не надав URL-адресу PUT, якщо клієнт хоче поставити на нього ... щось на кшталт "PUT / comments / new", і сервер може відповісти "204 / comments / 234532", але це здається трохи RPC для мене, клієнт повинен просто POST на / коментарі ...
thecoshman

24

Дуже простим способом я беру приклад часової шкали Facebook.

Випадок 1: Коли ви щось публікуєте на часовій шкалі, це новий запис. Тож у цьому випадку вони використовують метод POST, оскільки метод POST не є ідентичним.

Випадок 2: Якщо ваш друг коментує вашу публікацію вперше, це також створить новий запис у базі даних, щоб застосовувався метод POST.

Випадок 3: Якщо ваш друг редагує свій коментар, у цьому випадку у нього був ідентифікатор коментаря, тож він оновить існуючий коментар замість створення нового запису в базі даних. Тому для цього типу операцій використовуйте метод PUT, оскільки він ідентичний. *

В одному рядку використовуйте POST, щоб додати новий запис у базу даних, а PUT - щоб оновити щось у базі даних.


4
Якщо коментар - це об'єкт із властивістю, такою як ідентифікатор користувача, дата створення, коментар-повідомлення тощо, і на момент редагування лише оновлення повідомлення коментаря оновлюється, PATCH слід зробити тут?
Хабіб Первад

FB використовує PUT для оновлення коментаря, оскільки існуючий ресурс оновлюється, і саме так PUT робить (оновлення ресурсу). PUT буває ідентичним, на відміну від POST. Дієслово HTTP idempotent впливає на обробку помилок, але не диктує використання. Дивіться моя відповідь для більш детального пояснення: stackoverflow.com/questions/630453/put-vs-post-in-rest / ...
Joshcodes

21

Найголовніше врахування - надійність . Якщо повідомлення POST втрачається, стан системи не визначено. Автоматичне відновлення неможливо. Для повідомлень PUT стан не визначено лише до першого успішного спроби.

Наприклад, це може бути не дуже хорошою ідеєю створювати транзакції з кредитної картки за допомогою POST.

Якщо на вашому ресурсі у вас є автоматично створені URI, ви все одно можете використовувати PUT, передаючи згенерований URI (вказуючи на порожній ресурс) клієнту.

Деякі інші міркування:

  • POST скасовує кешовані копії всього ресурсу, що містить вміст (краща послідовність)
  • Відповіді PUT не підлягають кешуванню, а POST - (Потрібно розташування вмісту та термін дії)
  • PUT менш підтримується, наприклад, Java ME, старими браузерами, брандмауерами

Це неправильно. Для POST стан також не визначено лише до першого успішного спроби. Тоді або сервер приймає POST (повідомлення ніколи не надходило), викидає конфлікт 409 для дублюючого ідентифікатора (повідомлення надійшло, відповідь втрачено) або будь-який інший дійсний відповідь.
Joshcodes

Загалом, користувальницький засіб не зможе безпечно повторити операцію POST, оскільки операція POST не дає такої гарантії, що дві операції матимуть такий же ефект, як одна. Термін "ID" не має нічого спільного з HTTP. URI ідентифікує ресурс.
Ганс Малхербе

Корисний засіб може "безпечно" повторити операцію POST стільки разів, скільки захоче. Він просто отримає дублюючу помилку ідентифікатора (якщо припустимо, що у ресурсу є ідентифікатор) або помилка дублікату даних (якщо припустити, що це проблема, а ресурс не має ідентифікаторів).
Joshcodes

Чубчик головою об стіну. HTTP не вирішує проблеми надійності, і це недостатньо зрозуміло, не дуже обговорюється і просто не обслуговується в переважній більшості веб-додатків. @Joshcodes У мене є відповідь на це питання. Я по суті згоден з Гансом. Є проблема.
bbsimonbb

@bbsimonbb, HTTP має надійний і добре підтверджений набір відповідей на помилки. Моя відповідь на це питання ( stackoverflow.com/questions/630453/put-vs-post-in-rest/… ) стосується того, як використовувати http відповідно до специфікації для досягнення послідовності.
Joshcodes

17

Читачів, що знайшли цю тему, вразить нескінченна дискусія про те, що вам слід робити, та відносна відсутність уроків із досвіду. Те, що REST «віддають перевагу» над SOAP - це, напевно, навчання на високому рівні з досвіду, але добро, що ми повинні були прогресувати звідти? Це 2016. Дисертація Роя була в 2000 році. Що ми розробили? Було весело? З вами було легко інтегруватися? Підтримувати? Чи впорається він із підйомом смартфонів та нестійкими мобільними зв’язками?

За словами МО, мережі реального життя є ненадійними. Запрошує тайм-аут. З'єднання скидаються. Мережі опускаються годинами або днями. Потяги прямують в тунелі з мобільними користувачами на борту. Для будь-якого запиту (як це іноді визнається у всій цій дискусії) запит може впасти у воду на своєму шляху, або відповідь може впасти у воду на зворотному шляху. В цих умовах видача запитів PUT, POST та DELETE безпосередньо проти матеріальних ресурсів завжди вважала мене трохи жорстоким і наївним.

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

Або ви можете це зробити : розгляньте свої небезпечні запити як ефемерні ресурси одного користувача (назвемо їх діями). Клієнти вимагають нової "дії" на основний ресурс із порожнім POST на ресурс. POST використовуватиметься лише для цього. Отримавши безпечне володіння URI свіжовичавленої дії, клієнт виводить небезпечний запит на URI дії, а не на цільовий ресурс . Розв’язання дії та оновлення "реального" ресурсу належним чином є завданням вашого API, і тут він відокремлюється від ненадійної мережі.

Сервер займається бізнесом, повертає відповідь і зберігає його проти узгодженого URI дії . Якщо щось піде не так, клієнт повторює запит (природна поведінка!), І якщо сервер його вже бачив, він повторює збережену відповідь і більше нічого не робить .

Ви швидко помітите схожість з обіцянками: ми створюємо та повертаємо заповнювач для результату, перш ніж робити щось. Також, як і обіцянка, дія може бути успішною або невдалою одноразово, але її результат можна отримати неодноразово.

Найкраще, що ми надаємо можливість надсилання та прийому додатків пов'язувати унікально визначені дії з унікальністю у відповідних середовищах. І ми можемо почати вимагати і виконувати! Відповідальної поведінки від клієнтів: повторюйте свої запити скільки завгодно, але не продовжуйте створювати нові дії, поки не будете мати остаточного результату від існуючого.

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

Послідовні запити на видалення можуть бачити та обробляти оригінальне підтвердження, не потрапляючи на помилку 404. Якщо все займе більше часу, ніж очікувалося, ми можемо відповісти тимчасово, і у нас є місце, де клієнт може перевірити на остаточний результат. Найприємнішою частиною цієї картини є її властивість Кунг-Фу (Панда). Ми враховуємо слабкість, схильність клієнтів повторювати запит будь-коли, коли вони не розуміють відповідь, і перетворюємо це на міцність :-)

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

Якщо ви думаєте, що у вас буде величезна кількість даних для зберігання, давайте поговоримо про обсяги: типовим підтвердженням оновлення є частка кілобайт. На даний момент HTTP дає вам хвилину чи дві, щоб остаточно відповісти. Навіть якщо ви зберігаєте акції лише тиждень, клієнти мають достатньо шансів наздогнати. Якщо у вас дуже великі обсяги, вам може знадобитися спеціалізований запас цін, що відповідає кислоті, або рішення в пам'яті.


1
Не можете відповісти на зберігання відповідати підтримці сеансу? Що може спричинити (горизонтальне) масштабування.
Саураб Харванде

17

Окрім відмінностей, запропонованих іншими, я хочу додати ще одну.

У методі POST можна надсилати парами тіла вform-data

У методі PUT вам потрібно надіслати парами тілаx-www-form-urlencoded

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

Згідно з цим, ви не можете надсилати файли або багатопоточні дані методом PUT

EDIT

Тип вмісту "application / x-www-form-urlencoded" неефективний для надсилання великої кількості бінарних даних або тексту, що містить символи, що не містять ASCII. Тип вмісту "багаточастинні / форми-дані" слід використовувати для подання форм, що містять файли, не-ASCII дані та двійкові дані.

Що означає, якщо вам доведеться подати

файли, не ASCII дані та двійкові дані

ви повинні використовувати метод POST


3
Чому це не було схвалено? Якщо це правда, це критична відмінність, чи не так?
Іофактура

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 - не ідентичним. Тобто операція є ідентичною, якщо її можна виконувати на ресурсі один раз або багато разів і завжди повертати той самий стан цього ресурсу. В той час, як операція, що не входить в силу, може повернути змінений стан ресурсу з одного запиту на інший. Отже, при операції, що не має сили, немає гарантії, що людина отримає той самий стан ресурсу.

Виходячи з вищезазначеного визначення idempotent, я прийму використання методу 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зазвичай замінюють старий пароль новим, але ви можете використовувати його для створення пароля, якщо він вже не існує (наприклад, щойно зареєстровані користувачі або відновлення заборонених користувачів).


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

12

Я збираюся приземлитися з наступним:

PUT посилається на ресурс, ідентифікований URI. У цьому випадку ви її оновлюєте. Це частина трьох дієслів, що стосуються ресурсів - видалити та стати двома іншими.

POST - це повідомлення у вільній формі, його значення визначається як "поза діапазоном". Якщо повідомлення можна інтерпретувати як додавання ресурсу до каталогу, це було б добре, але в основному вам потрібно зрозуміти повідомлення, яке ви надсилаєте (розміщуєте), щоб знати, що буде з ресурсом.


Оскільки PUT і GET і DELETE посилаються на ресурс, вони також є визначенням idempotent.

POST може виконувати інші три функції, але тоді семантика запиту буде втрачена на посередниках, таких як кеші та проксі. Це також стосується забезпечення безпеки на ресурсі, оскільки URI в публікації не обов'язково вказує ресурс, до якого він застосовується (він може хоч).

PUT не потрібно створювати; служба може помилитися, якщо ресурс ще не створений, але в іншому випадку оновіть його. Або навпаки - це може створювати ресурс, але не допускати оновлень. Єдине, що потрібно про PUT, це те, що він вказує на певний ресурс, а його корисний навантаження - це представлення цього ресурсу. Вдалий PUT означає (заборона перешкод), що GET отримає той самий ресурс.


Редагувати: Ще одне - PUT може створити, але якщо це буде, то ID повинен бути природним ідентифікатором - AKA електронною адресою. Таким чином, коли ви PUT двічі, друге місце є оновлення першого. Це робить це безсильним .

Якщо ідентифікується ідентифікатор (наприклад, новий ідентифікатор співробітника), то другий PUT з тією ж URL-адресою створить нову запис, що порушує правило ідентифікатора. У цьому випадку дієсловом буде POST, а повідомлення (не ресурс) - це створити ресурс, використовуючи значення, визначені в цьому повідомленні.


9

Семантика повинна бути різною, тому що "PUT", як "GET", повинен бути однозначним - це означає, що ви можете запитувати один і той же точний PUT кілька разів, і результат буде таким, як якщо ви виконали його лише один раз.

Я опишу умови, які, на мою думку, є найбільш широко використовуваними та найкориснішими:

Коли ви PUT ресурс за певною URL-адресою, це відбувається, щоб його зберегти за цією URL-адресою, або щось у цьому рядку.

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

Наприклад, коли ви хочете створити новий потік, ви можете передати його на якусь URL-адресу. Але коли ви хочете розмістити повідомлення в поточному потоці, ви відправляєте його на його URL.

Що стосується зміни властивостей потоку, ви можете зробити це за допомогою PUT або POST. В основному, використовуйте "PUT" лише тоді, коли операція ідентична, інакше використовуйте POST.

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


Те, що ви описуєте як POST, - це насправді, як PATCH повинен поводитися. POST повинен означати щось більш схоже на "додавання", як у "дописі до списку розсилки".
Олександр Торстлінг

8

Більшу частину часу ви будете використовувати їх так:

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

Наприклад:

  • POST / items
  • ПУТ / предмети / 1234

В обох випадках орган запиту містить дані для створеного або оновленого ресурсу. З імен маршрутів повинно бути очевидно, що POST не є ідентичним (якщо ви будете називати його 3 рази, він створить 3 об'єкти), але PUT є idempotent (якщо ви будете називати його 3 рази, результат однаковий). PUT часто використовується для "upsert" операції (створення або оновлення), але ви завжди можете повернути помилку 404, якщо ви хочете використовувати її лише для зміни.

Зауважте, що POST "створює" новий елемент у колекції, а PUT "замінює" елемент за вказаною URL-адресою, але дуже часто зустрічається практика використовувати PUT для часткових модифікацій, тобто використовувати його лише для оновлення наявних ресурсів і змінювати лише включені поля в тілі (ігноруючи інші поля). Це технічно неправильно, якщо ви хочете бути REST-пуристом, PUT повинен замінити весь ресурс, і ви повинні використовувати PATCH для часткового оновлення. Мене особисто не хвилює, наскільки поведінка чітка та послідовна у всіх ваших кінцевих точках API.

Пам'ятайте, що REST - це набір конвенцій та вказівок, щоб зробити ваш API простим. Якщо ви закінчите складну обробку, щоб просто встановити прапорець "RESTfull", тоді ви перемагаєте мету;)


7

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

Будьмо тут дуже чіткими та прямими. Якщо ви розробник .NET, який працює з Web API, факти (з документації Microsoft API), http://www.asp.net/web-api/overview/creating-web-apis/creating-a-web -api-що-підтримує-грубі операції :

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 призначений для UPDATE , дебатів немає.

Сподіваюся, це допоможе будь-яким розробникам Microsoft, які читають усі коментарі із посиланнями на веб-сайт Amazon та Sun / Java.


7

Ось просте правило:

PUT до URL-адреси слід використовувати для оновлення або створення ресурсу, який може бути розташований за цією URL-адресою.

POST до URL-адреси слід використовувати для оновлення або створення ресурсу, який знаходиться за якоюсь іншою ("підпорядкованою") URL-адресою або не є локальним через HTTP.


1
PUT не для оновлення, це для заміни, зауважте, що для створення ви нічого не замінюєте. POST абсолютно не для оновлення в будь-якій формі.
thecoshman

2
Чи говорить те специфікація http? Або ви базуєте свій коментар на чомусь іншому?
Адам Гріффітс

Це просто здоровий глузд, як ти щось оновлюєш, коли не знаєш, що це ти оновлюєш? POST призначений для створення нового ресурсу.
thecoshman

2
thecoshman - ви тут зловживаєте семантикою - заміною може бути оновлення, якщо це той самий ресурс з кількома відмінностями. Заміна справедлива для put, лише якщо заміна використовується для зміни того ж ресурсу. Заміна на новий і інший ресурс недійсна (видалити старий і додати новий?), Особливо якщо "новий" ресурс не має природного ідентифікатора. POST, OTOH - це те, що може створювати, оновлювати, замінювати та видаляти - використання публікації залежить від того, чи є повідомлення для інтерпретації, наприклад "застосувати знижку", яке може або не може змінити ресурс залежно від логіка.
Джерард ONeill

Що стосується вашого другого коментаря - як щодо того, щоб ви «дістали» ресурс, змінювали потрібні поля, а потім повертали його назад? А як щодо того, якщо ресурс походить з іншого джерела, але використовує природний ідентифікатор (зовнішній ідентифікатор) - припущення природно оновить ресурс за URL-адресою, коли вихідні дані змінилися.
Джерард ONeill

6

Якщо ви знайомі з операціями з базою даних, вони є

  1. Виберіть
  2. Вставити
  3. Оновлення
  4. Видалити
  5. Об’єднати (оновити, якщо воно вже є, інше вставити)

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


5

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


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