Создайте запрос с помощью POST с кодами ответа 200 или 201 и содержимым

125

Предположим, я пишу службу REST, целью которой является добавление нового элемента данных в систему.

Я планирую отправить сообщение на

http://myhost/serviceX/someResources

Предположим, это сработает, какой код ответа мне следует использовать? И какой контент я могу вернуть.

Я смотрю определения кодов ответа HTTP и вижу следующие возможности:

200: вернуть объект, описывающий или содержащий результат действия;

201: что означает СОЗДАН. Значение * Запрос был выполнен, и в результате был создан новый ресурс. На вновь созданный ресурс можно ссылаться с помощью URI, возвращаемого в сущности ответа, с наиболее конкретным URI для ресурса, заданным полем заголовка Location. В ответ СЛЕДУЕТ включать объект, содержащий список характеристик ресурса и местоположения, из которых пользователь или пользовательский агент может выбрать наиболее подходящий. Формат объекта определяется типом носителя, указанным в поле заголовка Content-Type. *

Последний звучит больше в соответствии со спецификацией Http, но я не совсем понимаю, что

Ответ ДОЛЖЕН включать объект, содержащий список характеристик ресурса и местоположения (а).

средства.

Рекомендации? Интерпретации?

DJNA
источник

Ответы:

78

Идея состоит в том, что тело ответа дает вам страницу, которая связывает вас с этим:

201 Создано

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

Это означает, что вы должны включить Locationв заголовок ответа, который дает URL-адрес, где вы можете найти только что созданный объект :

HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://stackoverflow.com/a/36373586/12597

Тело ответа

Затем они упоминают, что вы должны включить в тело ответа :

Полезные данные ответа 201 обычно описывают созданные ресурсы и ссылаются на них.

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

HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://stackoverflow.com/a/36373586/12597
Content-Type: text/html

Your answer has been saved! 
Click <A href="https://stackoverflow.com/a/36373586/12597">here</A> to view it.

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

HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://stackoverflow.com/a/36373586/12597
Content-Type: application/xml

<createdResources>
   <questionID>1860645</questionID>
   <answerID>36373586</answerID>
   <primary>/a/36373586/12597</primary>
   <additional>
      <resource>http://stackoverflow.com/questions/1860645/create-request-with-post-which-response-codes-200-or-201-and-content/36373586#36373586</resource>
      <resource>http://stackoverflow.com/a/1962757/12597</resource>
   </additional>
</createdResource>

Или, если хотите:

HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://stackoverflow.com/a/36373586/12597
Content-Type: application/json

{ 
   "questionID": 1860645, 
   "answerID": 36373586,
   "primary": "/a/36373586/12597",
   "additional": [
      "http://stackoverflow.com/questions/1860645/create-request-with-post-which-response-codes-200-or-201-and-content/36373586#36373586",
      "http://stackoverflow.com/a/36373586/12597"
   ]
}

Ответ полностью зависит от вас; это произвольно, что вы хотите.

Дружественный к кешу

Наконец, есть оптимизация: я могу предварительно кэшировать созданный ресурс (потому что у меня уже есть контент; я только что загрузил его). Сервер может вернуть дату или ETag, которые я могу сохранить вместе с только что загруженным мной контентом:

См. Раздел 7.2 для обсуждения значения и назначения полей заголовка валидатора, таких как ETag и Last-Modified, в ответе 201.

HTTP/1.1 201 Created
Date: Sat, 02 Apr 2016 12:22:40 GMT
Location: http://stackoverflow.com/a/23704283/12597
Content-Type: text/html
ETag: JF2CA53BOMQGU5LTOQQGC3RAMV4GC3LQNRSS4
Last-Modified: Sat, 02 Apr 2016 12:22:39 GMT 

Your answer has been saved! 
Click <A href="https://stackoverflow.com/a/36373586/12597">here</A> to view it.

И ETags - чисто произвольные значения. Главное, чтобы они были разными при изменении ресурса (и необходимости обновления кешей). ETag обычно представляет собой хэш (например, SHA2). Но это может быть база данных rowversionили увеличивающийся номер ревизии. Все, что изменится, когда что- то изменится.

Ян Бойд
источник
Пока что ваш ответ кажется наиболее разумным. Меня немного беспокоит онтология ответа, но в остальном это кажется наиболее зрелой интерпретацией спецификации. Мне любопытно, есть ли какой-нибудь легкий «отзывчивый» способ обработки вывода человека / машины. но больше всего меня заинтриговало ваше предложение «кэшировать собственный ввод». Большинство известных мне веб-приложений не будут создавать версию ресурса 1: 1. Даже если это что-то тривиальное, например, нормализация заглавных букв в строке. Разве это не хитрость - рассматривать вашу отправленную версию как версию, для которой был создан etag?
Энтони
1
@Anthony, кеширование: это может быть своего рода приложение для хранения файлов 1: 1. Сравните, например, WebDAV PUT & POST. Огромные файлы, которые нужно обрабатывать.
kxr
@Anthony. Вам решать, хотите ли вы вернуть ETag обратно клиенту. Если только что загруженный клиентом контент не является тем, что вы сохранили, не возвращайте ETag. Это ваша гибкость и ваш выбор.
Ян Бойд
Почему в ваших ответах отсутствует Content-Length?
Винни Фалько
1
@VinnieFalco Это ответ о коде ответа 201. Content-Length опущен для пояснительных целей.
Ян Бойд
91

Я думаю, что REST API-интерфейс atompub - отличный пример спокойного сервиса. См. Нижеприведенный фрагмент из спецификации atompub:

POST /edit/ HTTP/1.1
Host: example.org
User-Agent: Thingio/1.0
Authorization: Basic ZGFmZnk6c2VjZXJldA==
Content-Type: application/atom+xml;type=entry
Content-Length: nnn
Slug: First Post

<?xml version="1.0"?>
<entry xmlns="http://www.w3.org/2005/Atom">
  <title>Atom-Powered Robots Run Amok</title>
  <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
  <updated>2003-12-13T18:30:02Z</updated>
  <author><name>John Doe</name></author>
  <content>Some text.</content>
</entry>

Сервер сигнализирует об успешном создании с кодом состояния 201. Ответ включает в себя заголовок Location, указывающий URI записи члена записи Atom, и представление этой записи в теле ответа.

HTTP/1.1 201 Created
Date: Fri, 7 Oct 2005 17:17:11 GMT
Content-Length: nnn
Content-Type: application/atom+xml;type=entry;charset="utf-8"
Location: http://example.org/edit/first-post.atom
ETag: "c180de84f991g8"  

<?xml version="1.0"?>
<entry xmlns="http://www.w3.org/2005/Atom">
  <title>Atom-Powered Robots Run Amok</title>
  <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
  <updated>2003-12-13T18:30:02Z</updated>
  <author><name>John Doe</name></author>
  <content>Some text.</content>
  <link rel="edit"
      href="http://example.org/edit/first-post.atom"/>
</entry>

Запись, созданная и возвращенная коллекцией, может не соответствовать записи, отправленной клиентом. Сервер МОЖЕТ изменить значения различных элементов в записи, таких как значения atom: id, atom: updated и atom: author, и МОЖЕТ выбрать удаление или добавление других элементов и атрибутов или изменение содержимого элемента и значений атрибутов.

Чандра Патни
источник
9
Возврат созданного ресурса может быть слишком большим, если размер ресурса составляет гигабайты ...
Тор Валамо
10
Согласовано! Это оптимизация по необходимости, но вы не хотите делать это преждевременно. Важно разрабатывать в Restful spirits и делать исключения только тогда, когда они необходимы.
Чандра Патни
3
@ChandraPatni, Атом мертв . Нужны примеры получше.
Pacerier
16
Атом может быть мертв, но дух примера все еще актуален.
Ashimema
2
Моя первоначальная интерпретация ответа 201 была больше похожа на «эй, вы хотели создать ресурс, но, исходя из контекста, вы либо не были заинтересованы в конечном результате, либо у вас есть доступ для записи, но нет доступа для чтения к этому ресурсу. В этом случае все, что вам нужно, прежде чем вернуться в основную коллекцию, - это URL-адрес созданного ресурса. В качестве доказательства, что он был создан ". Все, что выходит за рамки этого, по сути, похоже на ответ 200. Если только RFC не имел в виду что-то еще.
Энтони
50

В нескольких словах:

  • 200 при создании объекта и возвращается
  • 201, когда объект создается, но возвращается только его ссылка (например, идентификатор или ссылка)
Стефан Брукерт
источник
Источник для этого?
sudo soul
3
Это то, что я понял из w3.org/Protocols/rfc2616/rfc2616-sec10.html и httpstatuses.com/201
Стефан Брукерт
3
Прочитав tools.ietf.org/html/rfc7231#section-6.3.1 , я согласен с этим пониманием - я полагаю, я больше спрашивал, как вы к этому пришли. Но теперь в моем понимании ... 200 = ресурс создан и возвращен | 201 = ресурс создан и возвращается ссылка | 204 = ресурс создан, полезная нагрузка не возвращена
sudo soul
34

Ознакомьтесь с HTTP: Определения методов: POST .

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

Если ресурс был создан на исходном сервере, ответ ДОЛЖЕН быть 201 (Создан) и содержать объект, который описывает статус запроса и относится к новому ресурсу, и заголовок Location (см. Раздел 14.30).

ma11hew28
источник
18

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19

Это просто пара "ключ-значение", разделенная двоеточиями.

ETag: "xyzzy"

Это могут быть текстовые данные любого типа - я обычно включаю строку JSON с идентификатором созданного элемента. Сама по себе простота тестирования делает его целесообразным.

ETag: "{ id: 1234, uri: 'http://domain.com/comments/1234', type: 'comment' }"

В этом примере идентификатор, uri и тип созданного элемента являются «характеристиками ресурса и местоположением».

tempire
источник
3
Вы говорите, что ETag соответствует объекту, содержащему список характеристик ресурсов и местоположений . Я вижу, что ваше предложение хорошее, полностью согласен с вашим мнением о тестировании. Однако я не понимаю, как это согласуется со «списком характеристик ресурсов и местоположений».
djna
«Список характеристик и местоположений ресурсов» будет содержанием любой предоставленной структуры данных. Более строгая реализация была бы для структуры JSON, чтобы включить uri ресурса и, возможно, тип ресурса, который был создан. Я откорректирую ответ как таковой.
tempire
7
Укажите проблемы, чтобы люди могли узнать. В противном случае комментарий просто машет рукой.
tempire
@SimonGibbs Какие проблемы?
MEMark 04
2
Хотя это строго соответствует спецификации, он рекомендует весьма необычный вариант реализации. Кроме того, он фактически не отвечает на вопрос вверху страницы (или делает это путем смешивания слов ETag и entity). Ответ с 43 голосами, вероятно, лучше.
Саймон Гиббс
1

Вывод фактически зависит от запрашиваемого типа содержимого. Однако, как минимум, вы должны поместить созданный ресурс в Location. Точно так же, как шаблон Post-Redirect-Get.

В моем случае я оставляю это поле пустым, пока не будет запрошено иное. Поскольку это поведение JAX-RS при использовании Response.created ().

Однако просто обратите внимание, что браузеры и фреймворки, такие как Angular, не следуют 201 автоматически. Я заметил поведение в http://www.trajano.net/2013/05/201-created-with-angular-resource/

Архимед Траяно
источник
-2

Другой ответ, который я хотел бы получить, - это использовать прагматический подход и упростить ваш контракт REST API . В моем случае я реорганизовал свой REST API, чтобы сделать его более тестируемым, не прибегая к JavaScript или XHR, просто к простым HTML-формам и ссылкам.

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

Одно замечание: в моем отредактированном контракте API ответы POST не должны содержать никаких кешируемых данных, поскольку POST на самом деле не кэшируются, поэтому ограничьте его идентификаторами, которые можно запросить и кэшировать с помощью запроса GET.

Архимед Траяно
источник