Utwórz żądanie z POST, które zawiera kody odpowiedzi 200 lub 201 i zawartość

125

Przypuśćmy, że piszę usługę REST, której celem jest dodanie nowego elementu danych do systemu.

Planuję POST do

http://myhost/serviceX/someResources

Załóżmy, że to zadziała, jakiego kodu odpowiedzi powinienem użyć? I jaką treść mogę wrócić.

Patrzę na definicje kodów odpowiedzi HTTP i widzę te możliwości:

200: Zwróć jednostkę opisującą lub zawierającą wynik działania;

201: co oznacza STWORZONO. Znaczenie * Żądanie zostało spełnione i spowodowało utworzenie nowego zasobu. Do nowo utworzonego zasobu można się odwoływać za pomocą identyfikatorów URI zwróconych w jednostce odpowiedzi, z najbardziej szczegółowym identyfikatorem URI dla zasobu podanym w polu nagłówka lokalizacji. Odpowiedź POWINNA zawierać encję zawierającą listę charakterystyk zasobów i lokalizacji, z których użytkownik lub agent użytkownika może wybrać najbardziej odpowiednią. Format jednostki jest określony przez typ nośnika podany w polu nagłówka Content-Type. *

Ta ostatnia brzmi bardziej zgodnie ze specyfikacją HTTP, ale nie mam pojęcia, co

Odpowiedź POWINNA zawierać encję zawierającą listę charakterystyk zasobów i lokalizacji

znaczy.

Zalecenia? Interpretacje?

djna
źródło

Odpowiedzi:

78

Chodzi o to, że treść odpowiedzi podaje stronę, która prowadzi do danej rzeczy:

201 Utworzono

Kod stanu 201 (Utworzono) wskazuje, że żądanie zostało spełnione i spowodowało utworzenie co najmniej jednego nowego zasobu. Podstawowy zasób utworzony przez żądanie jest identyfikowany przez pole nagłówka lokalizacji w odpowiedzi lub, jeśli nie otrzymano pola lokalizacji, przez efektywny identyfikator URI żądania.

Oznacza to, że Locationw nagłówku odpowiedzi umieścisz a, który zawiera adres URL miejsca, w którym możesz znaleźć nowo utworzoną rzecz :

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

Treść odpowiedzi

Następnie wspominają, co należy zawrzeć w treści odpowiedzi :

Ładunek odpowiedzi 201 zwykle opisuje i łączy do utworzonych zasobów.

Człowiek korzystający z przeglądarki daje mu coś, na co może spojrzeć i kliknąć, aby dostać się do nowo utworzonego zasobu:

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.

Jeśli strona będzie używana tylko przez robota, sensowne jest, aby odpowiedź była czytelna dla komputera:

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>

Lub, jeśli wolisz:

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"
   ]
}

Odpowiedź zależy wyłącznie od Ciebie; to arbitralnie to, co chcesz.

Przyjazny dla pamięci podręcznej

Wreszcie jest optymalizacja, dzięki której mogę wstępnie buforować utworzony zasób (ponieważ mam już zawartość; właśnie ją przesłałem). Serwer może zwrócić datę lub ETag, które mogę przechowywać z treścią, którą właśnie załadowałem:

W sekcji 7.2 omówiono znaczenie i cel pól nagłówka walidatora, takich jak ETag i Last-Modified w odpowiedzi 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.

A ETagsą to wartości czysto arbitralne. Ważne jest, aby były inne, gdy zmieniają się zasoby (i pamięci podręczne wymagają aktualizacji). ETag jest zwykle hashem (np. SHA2). Ale może to być baza danych rowversionlub rosnący numer wersji. Wszystko, co zmieni się, gdy coś się zmieni.

Ian Boyd
źródło
Jak dotąd twoja odpowiedź wydaje się najbardziej sensowna. Trochę niepokoi mnie ontologia odpowiedzi, ale poza tym wydaje mi się, że jest to najbardziej dojrzała interpretacja specyfikacji. Jestem ciekawy, czy istnieje jakiś lekki, „responsywny” sposób obsługi wyjścia człowiek / maszyna. ale przede wszystkim intryguje mnie twoja sugestia „buforowania własnych danych wejściowych”. Większość znanych mi aplikacji internetowych nie utworzy wersji 1: 1 zasobu. Nawet jeśli jest to coś trywialnego, jak normalizacja wielkości liter w ciągu. Czy nie jest trochę podejrzane traktowanie przesłanej wersji jako wersji, dla której powstał etag?
Anthony
1
@Anthony, buforowanie: może to być rodzaj aplikacji do przechowywania plików 1: 1. Porównaj np. WebDAV PUT & POST. Ogromne pliki do obsługi.
kxr
@Anthony To od Ciebie zależy, czy chcesz zwrócić klientowi ETag. Jeśli zawartość, którą właśnie przesłał klient, nie jest tym, co zapisałeś, nie zwracaj ETag. To Twoja elastyczność i Twój wybór.
Ian Boyd
Dlaczego w Twoich odpowiedziach brakuje długości treści?
Vinnie Falco,
1
@VinnieFalco To jest odpowiedź na temat kodu odpowiedzi 201. Długość treści została usunięta w celach informacyjnych.
Ian Boyd
91

Myślę, że atompub REST API jest doskonałym przykładem uspokajającej usługi. Zobacz poniższy fragment ze specyfikacji 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>

Serwer sygnalizuje pomyślne utworzenie kodem stanu 201. Odpowiedź zawiera nagłówek lokalizacji wskazujący identyfikator URI elementu członkowskiego wpisu atomu oraz reprezentację tego wpisu w treści odpowiedzi.

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>

Wpis utworzony i zwrócony przez kolekcję może nie odpowiadać wpisowi POST przesłanemu przez klienta. Serwer MOŻE zmienić wartości różnych elementów we wpisie, takich jak wartości atom: id, atom: updated i atom: author, i MOŻE zdecydować się na usunięcie lub dodanie innych elementów i atrybutów lub zmianę zawartości elementu i wartości atrybutów.

Chandra Patni
źródło
9
Zwrot utworzonego zasobu może być trochę za duży, jeśli zasób jest wielkości gigabajtów ...
Tor Valamo,
10
Zgoda! To jest optymalizacja z konieczności - ale nie chcesz tego robić przedwcześnie. Ważne jest, aby projektować w Restful Spirits i robić wyjątki tylko wtedy, gdy są konieczne.
Chandra Patni
3
@ChandraPatni, Atom nie żyje . Potrzebujesz lepszych przykładów.
Pacerier
16
Atom może być martwy, ale duch przykładu jest wciąż aktualny.
Ashimema
2
Moja oryginalna interpretacja odpowiedzi 201 była bardziej jak „hej, chciałeś utworzyć zasób, ale na podstawie kontekstu albo nie byłeś zainteresowany końcowym wynikiem albo masz uprawnienia do zapisu, ale nie do odczytu tego zasobu. przypadku, wszystko, czego potrzebujesz przed powrotem do głównej kolekcji, to adres URL utworzonego zasobu. Jako dowód, że został utworzony. " Cokolwiek poza tym wydaje się zasadniczo odpowiedzią 200. Chyba że RFC miał coś innego na myśli.
Anthony
50

W kilku słowach:

  • 200, gdy obiekt jest tworzony i zwracany
  • 201 gdy obiekt jest tworzony, ale zwracane jest tylko jego odwołanie (takie jak identyfikator lub łącze)
Stéphane Bruckert
źródło
Źródło tego?
sudo soul
3
To, co rozumiem z w3.org/Protocols/rfc2616/rfc2616-sec10.html i httpstatuses.com/201
Stéphane Bruckert
3
Po przeczytaniu tools.ietf.org/html/rfc7231#section-6.3.1 zgadzam się z tym zrozumieniem - przypuszczam, że pytałem więcej, jak do tego doszedłeś. Ale teraz w moim rozumieniu ... 200 = zasób utworzony i zwrócony | 201 = utworzono zasób i zwracane jest odwołanie | 204 = utworzono zasób i nie zwrócono ładunku
sudo soul
34

Sprawdź HTTP: definicje metod: POST .

Akcja wykonana przez metodę POST może nie skutkować zasobem, który można zidentyfikować za pomocą identyfikatora URI. W tym przypadku odpowiedni stan odpowiedzi to 200 (OK) lub 204 (Brak treści), w zależności od tego, czy odpowiedź zawiera jednostkę opisującą wynik.

Jeśli zasób został utworzony na serwerze pochodzenia, odpowiedź POWINNA być 201 (utworzona) i zawierać jednostkę opisującą stan żądania i odwołującą się do nowego zasobu oraz nagłówek lokalizacji (patrz sekcja 14.30).

ma11hew28
źródło
18

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

To tylko para klucz-wartość rozdzielana dwukropkami.

ETag: „xyzzy”

Mogą to być dane tekstowe dowolnego typu - generalnie dołączam ciąg JSON z identyfikatorem utworzonego elementu. Już sama łatwość testowania sprawia, że ​​warto to zrobić.

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

W tym przykładzie identyfikator, uri i typ utworzonego elementu to „charakterystyka i lokalizacja zasobu”.

tempire
źródło
3
Mówisz, że ETag odpowiada jednostce zawierającej listę charakterystyk zasobów i lokalizacji . Widzę, że twoja sugestia jest dobra i bardzo zgadza się z twoim punktem dotyczącym testowania. Jednak nie widzę, jak to pasuje do „listy charakterystyk i lokalizacji zasobów”.
djna
„Lista charakterystyk i lokalizacji zasobów” byłaby treścią dowolnej dostarczonej struktury danych. Bardziej rygorystyczna implementacja polegałaby na tym, że struktura JSON zawierałaby identyfikator uri zasobu i być może typ utworzonego zasobu. Dostosuję odpowiedź jako taką.
tempire
7
Określ problemy, aby ludzie mogli się uczyć. W przeciwnym razie komentarz jest po prostu machaniem ręką.
tempire
@SimonGibbs Jakie problemy?
MEMark
2
Chociaż jest to ściśle zgodne ze specyfikacją, zaleca bardzo nietypową opcję implementacji. Również w rzeczywistości nie odpowiada na pytanie u góry strony (lub robi to przez pomieszanie słów ETag i entity). Odpowiedź z 43 głosami jest prawdopodobnie lepsza.
Simon Gibbs
1

Dane wyjściowe są w rzeczywistości zależne od żądanego typu zawartości. Jednak przynajmniej powinieneś umieścić zasób, który został utworzony w lokalizacji. Podobnie jak wzorzec Post-Redirect-Get.

W moim przypadku zostawiam to pole puste, dopóki nie zażądam inaczej. Ponieważ takie jest zachowanie JAX-RS podczas używania Response.created ().

Pamiętaj jednak, że przeglądarki i frameworki, takie jak Angular, nie podążają automatycznie za wersjami 201. Zauważyłem zachowanie w http://www.trajano.net/2013/05/201-created-with-angular-resource/

Archimedes Trajano
źródło
-2

Inną odpowiedzią, jaką miałbym na to, byłoby przyjęcie pragmatycznego podejścia i zachowanie kontraktu REST API uproszczenie . W moim przypadku zrefaktoryzowałem moje REST API, aby uczynić rzeczy bardziej testowalnymi bez uciekania się do JavaScript lub XHR, tylko proste formularze HTML i linki.

Aby być bardziej szczegółowym w powyższym pytaniu, użyłbym po prostu kodu zwrotnego 200 i otrzymam wiadomość w formacie JSON, którą aplikacja może zrozumieć. W zależności od potrzeb może wymagać identyfikatora nowo utworzonego obiektu, aby aplikacja internetowa mogła pobrać dane w innym wywołaniu.

Jedna uwaga, w mojej refaktoryzowanej umowie API odpowiedzi POST nie powinny zawierać żadnych danych, które można zapisać w pamięci podręcznej, ponieważ posty POST nie są tak naprawdę buforowalne, więc ogranicz je do identyfikatorów, które można żądać i buforować za pomocą żądania GET.

Archimedes Trajano
źródło