Pytanie PUT vs. POST w REST


Zgodnie ze specyfikacją HTTP / 1.1:

The POST Metoda służy do żądania, aby serwer pochodzenia zaakceptował encję zawartą w żądaniu jako nowy podrzędny zasobu zidentyfikowanego przez Request-URI w Request-Line

Innymi słowy, POST jest używany do Stwórz.

The PUT Metoda żąda, aby zamknięty obiekt był przechowywany pod dostarczonym Request-URI. Jeśli Request-URI odnosi się do już istniejącego zasobu, zamknięta jednostka POWINNA być uznana za zmodyfikowaną wersję tej, która znajduje się na serwerze źródłowym. Jeśli Request-URI nie wskazuje istniejącego zasobu, a identyfikator URI może być zdefiniowany jako nowy zasób przez agenta użytkownika żądającego, serwer źródłowy może utworzyć zasób z tym identyfikatorem URI. "

To jest, PUT jest używany do utwórz lub zaktualizuj.

Który z nich należy wykorzystać do stworzenia zasobu? Lub trzeba wspierać oba?


4468
2018-03-10 14:25


pochodzenie


Pomocne może być użycie definicji w HTTPbis - Roy poświęcił sporo pracy na ich wyjaśnienie. Widzieć: tools.ietf.org/html/... - Mark Nottingham
Aby przynieść komentarz @ MarkNottingham do najnowszej wersji, oto STANOWISKO i POŁOŻYĆ, jak zdefiniowano w HTTPbis. - Marius Butuc
Wydaje mi się, że debata ta powstała w wyniku powszechnej praktyki upraszczającego REST, opisując metody HTTP w kategoriach operacji CRUD. - Stuporman
Niefortunnie pierwsze odpowiedzi są błędne w przypadku testu POST. Sprawdź moją odpowiedź, aby lepiej wyjaśnić różnice: stackoverflow.com/a/18243587/2458234 - 7hi4g0
PUT i POST to obie niebezpieczne metody. Jednak PUT jest idempotent, a POST nie jest. - Zobacz więcej na: restcookbook.com/HTTP%20Methods/put-vs-post/... - Dinesh Saini


Odpowiedzi:


Ogólnie: 

Zarówno PUT, jak i POST można używać do tworzenia.

Musisz zapytać "do czego chcesz wykonać akcję?" aby odróżnić, czego powinieneś używać. Załóżmy, że projektujesz interfejs API do zadawania pytań. Jeśli chcesz użyć POST, możesz to zrobić na liście pytań. Jeśli chcesz użyć PUT, możesz to zrobić na konkretne pytanie.

Świetne oba mogą być używane, więc których należy użyć w moim projekcie RESTful:

Nie musisz obsługiwać zarówno PUT, jak i POST.

Który jest używany zależy od Ciebie. Ale pamiętaj, aby użyć właściwego, w zależności od tego, do którego obiektu odwołujesz się w żądaniu.

Niektóre uwagi:

  • Czy wyraźnie określasz swoje obiekty URL, które tworzysz, lub pozwalasz serwerowi zdecydować? Jeśli je nazwiesz, użyj PUT. Jeśli pozwolisz serwerowi zdecydować, użyj POST.
  • PUT jest idempotentne, więc jeśli postawisz obiekt dwukrotnie, nie ma on żadnego efektu. Jest to dobra właściwość, więc użyłbym PUT, jeśli to możliwe.
  • Możesz zaktualizować lub utworzyć zasób za pomocą PUT z tym samym adresem URL obiektu
  • Za pomocą POST można uzyskać 2 żądania przychodzące jednocześnie modyfikując adres URL i mogą one aktualizować różne części obiektu.

Przykład:

Napisałem to w ramach innej odpowiedzi na SO dotyczącej tego:

STANOWISKO:

Służy do modyfikowania i aktualizowania zasobu

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

Zauważ, że następujące jest błąd:

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

Jeśli URL nie jest jeszcze utworzony, ty   nie powinno używać POST do jego utworzenia   podczas określania nazwy. To powinno   powoduje błąd "nie znaleziono zasobu"   bo <new_question> nie istnieje   jeszcze. Powinieneś PUT the <new_question>   najpierw zasób na serwerze.

Możesz jednak zrobić coś takiego   to, aby utworzyć zasoby za pomocą testu POST:

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

Zauważ, że w tym przypadku zasób   nazwa nie jest określona, ​​nowe obiekty   Ścieżka adresu URL zostanie zwrócona do Ciebie.

POŁOŻYĆ: 

Służy do tworzenia zasobu, lub   Przepisz to. Podczas określania   Zasoby nowy adres URL.

Dla nowego zasobu:

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

Aby zastąpić istniejący zasób:

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

3487
2018-03-10 14:29



Myślę, że nie można wystarczająco podkreślić faktu, że PUT jest idempotentem: jeśli sieć zostanie spartaczona, a klient nie jest pewien, czy jego żądanie się udało, może po prostu wysłać ją na sekundę (lub 100), i jest to gwarantowane przez Specyfikacja HTTP, która ma dokładnie taki sam efekt jak wysyłanie raz. - Jörg W Mittag
@ Jörg W Mittag: Nie konieczne. Drugi raz może zwrócić 409 Konflikt lub coś takiego, jeśli żądanie zostało zmodyfikowane w międzyczasie (przez innego użytkownika lub pierwsze żądanie, które przeszło). - Mitar
Jeśli się nie mylę, powinniśmy podkreślić, że PUT jest określone być idempotentem. Nadal musisz napisać swój serwer w taki sposób, aby PUT działał poprawnie, tak? Być może lepiej powiedzieć "PUT powoduje, że transport przyjmuje idempotencję, która może wpływać na zachowanie transportu, np. Buforowanie". - Ian Ni-Lewis
@ JörgWMittag Idempotence catchphrase? Co powiesz na "Wyślij i wyślij i wyślij mojego przyjaciela, to nie ma znaczenia w końcu." - James Beninger
Myśli o nich jako: PUT = wstaw lub zaktualizuj; POST = wstaw. Kiedy więc wykonasz dwa PUT - otrzymasz jeden nowy rekord, kiedy wykonasz dwa POST-y - otrzymasz dwa nowe rekordy. - Eugen Konkov


W Internecie można znaleźć stwierdzenia

Żadne z nich nie jest w porządku.


Lepiej wybrać na podstawie PUT i POST idempotencja akcji.

POŁOŻYĆ oznacza umieszczenie zasobu - całkowicie zastępującego wszystko, co jest dostępne pod danym adresem URL, czymś innym. Z definicji PUT jest idempotentne. Zrób to tyle razy, ile chcesz, a wynik jest taki sam. x=5 jest idempotent. Możesz PST zasób, czy wcześniej istniał, czy nie (np. Do tworzenia lub do aktualizacji)!

STANOWISKO aktualizuje zasób, dodaje zasób pomocniczy lub powoduje zmianę. POST nie jest idempotentny w taki sposób x++ nie jest idempotentny.


Poprzez ten argument, PUT służy do tworzenia, gdy znasz adres URL rzeczy, którą utworzysz. POST może być użyty do utworzenia, gdy znasz adres URL "fabryki" lub menedżera dla kategorii rzeczy, które chcesz utworzyć.

więc:

POST /expense-report

lub:

PUT  /expense-report/10929

1882
2018-04-22 14:55



Zgadzam się, że wszędzie tam, gdzie idempotencja jest ważna, powinna zająć wszelkie inne obawy, ponieważ uzyskanie tego błędu może spowodować wiele nieoczekiwanych błędów. - Josh
Jeśli POST może zaktualizować zasób, to jak to nie jest idempotent? Jeśli zmienię wiek uczniów za pomocą PUT i zrobię to 10 razy, wiek uczniów jest taki sam, jeśli zrobiłem to raz. - Schneider
@Schneider, w tym przypadku twój serwer robi dodatkowy wysiłek, aby zagwarantować idempotencję, ale nie reklamuje go. Przeglądarki nadal będą ostrzegać użytkownika, jeśli spróbują ponownie załadować takie żądanie POST. - Tobu
@Schneider POST może utworzyć zasób pomocniczy; stąd możesz POST, by zbierać, jak POST / raporty wydatków i tworzyłoby tyle jednostek (raportów wydatków) na serwerze, ile wysłanych przez ciebie żądań, nawet jeśli są one całkowicie podobne. Pomyśl o tym, wstawiając ten sam wiersz w tabeli DB (/ raporty wydatków) z automatycznie zwiększanym kluczem podstawowym. Dane pozostają niezmienione, klucz (w tym przypadku URI) jest generowany przez serwer i jest różny dla każdej innej wstawki (żądania). Tak, efekt POST mogą być idempotentem, ale także może nie. W związku z tym POST jest nie idempotent. - Snifff
Załóżmy, że mamy obiekty, które mogą mieć dwie właściwości - name i date. Jeśli mamy podmiot z istniejącym name i date, ale następnie wysyłaj do niego żądania określając tylko a name, prawidłowe zachowanie POŁOŻYĆ byłoby zniszczyć date podmiotu, natomiast STANOWISKO może aktualizować tylko określone właściwości, pozostawiając nieokreślone właściwości tak, jak przed wysłaniem żądania. Czy brzmi to poprawnie / rozsądnie, czy jest to niewłaściwe użycie? POŁOŻYĆ (Widziałem odniesienia do ŁATA, które wydaje się bardziej odpowiednie, ale jeszcze nie istnieje)? - Jon z


  • STANOWISKO do adresu URL tworzy zasób potomny na zdefiniowany serwer URL.
  • POŁOŻYĆ do adresu URL tworzy / zamienia zasób w całości w zdefiniowany klient URL.
  • ŁATA do adresu URL aktualizacje część zasobu pod tym zdefiniowanym adresem URL klienta.

Odpowiednia specyfikacja dla PUT i POST jest RFC 2616 §9.5ff.

POST tworzy zasób potomny, więc POST na /items tworzy zasoby, które znajdują się pod domeną /items ratunek. Na przykład. /items/1. Dwukrotne wysłanie tego samego pakietu pocztowego spowoduje utworzenie dwóch zasobów.

POŁOŻYĆ służy do tworzenia lub zamiany zasobu na URL znany przez klienta.

W związku z tym: POŁOŻYĆ jest kandydatem do CREATE tylko wtedy, gdy klient zna już adres URL przed utworzeniem zasobu. Na przykład. /blogs/nigel/entry/when_to_use_post_vs_put ponieważ tytuł jest używany jako klucz zasobów

POŁOŻYĆ zamienia zasób pod znanym adresem URL, jeśli już istnieje, więc dwukrotne wysłanie tego samego żądania nie ma żadnego efektu. Innymi słowy, połączenia do PUT są idempotentne.

RFC czyta się tak:

Podstawowa różnica między żądaniami POST i PUT znajduje odzwierciedlenie w różnym znaczeniu Request-URI. Identyfikator URI w żądaniu POST identyfikuje zasób, który będzie obsługiwał zamknięty obiekt. Ten zasób może być procesem akceptującym dane, bramą do innego protokołu lub osobną jednostką akceptującą adnotacje. Natomiast URI w żądaniu PUT identyfikuje encję dołączoną do żądania - agent użytkownika wie, co to jest URI, a serwer NIE MOŻE próbować zastosować żądania do innego zasobu. Jeśli serwer życzy sobie, aby żądanie zostało zastosowane do innego identyfikatora URI,

Uwaga: PUT został w większości wykorzystany do aktualizacji zasobów (poprzez zastąpienie ich w całości), ale ostatnio następuje ruch w kierunku użycia PATCH do aktualizacji istniejących zasobów, ponieważ PUT określa, że ​​zastępuje cały zasób. RFC 5789.


563
2018-04-07 05:52



Lub z drugiej strony ogrodzenia: PUT, jeśli klient określa adres zasobu wynikowego, POST, jeśli serwer to robi. - DanMan
Myślę, że ta odpowiedź powinna zostać zredagowana, aby było bardziej zrozumiałe, co @DanMan wskazał w bardzo prosty sposób. To, co uważam za najcenniejsze, to uwaga na końcu, stwierdzająca, że ​​PUT powinno się używać tylko do zastąpienia całego zasobu. - Hermes
PATCH nie jest realistyczną opcją przez co najmniej kilka lat, ale zgadzam się z ideologią. - crush
Próbuję to zrozumieć, ale użycie PUT do stworzenia czegoś miałoby sens tylko wtedy, gdy klient wie na pewno, że zasób jeszcze nie istnieje, prawda? Podążając za przykładem bloga, powiedz, że w ciągu kilku lat stworzyłeś setki postów na blogu, a następnie przypadkowo wybierz ten sam tytuł, co w poście sprzed dwóch lat. Teraz odszedłeś i zastąpiłeś ten post, który nie był zamierzony. Zatem użycie PUT do stworzenia wymagałoby od klienta śledzenia tego, co jest zrobione, a co nie, i może prowadzić do wypadków i niezamierzonych efektów ubocznych, jak również posiadania tras, które robią dwie całkowicie różne rzeczy? - galaxyAbstractor
Masz rację. Ustanowienie wpisu w blogu pod tym samym adresem URL, co istniejący, spowoduje aktualizację istniejącego wpisu (chociaż oczywiście można sprawdzić najpierw za pomocą GET). Wskazuje to, dlaczego niewłaściwym pomysłem byłoby użycie tytułu jako adresu URL. To działałoby jednak wszędzie tam, gdzie istniałby naturalny klucz w danych ... które z mojego doświadczenia jest rzadkie. Lub jeśli użyłeś identyfikatorów GUID - Nigel Thorne


Podsumowanie:

Stwórz:

Można go wykonać za pomocą PUT lub POST w następujący sposób:

POŁOŻYĆ

Tworzy THE nowy zasób z newResourceId jako identyfikator pod identyfikatorem URI / resources, lub kolekcja.

PUT /resources/<newResourceId> HTTP/1.1 

STANOWISKO

Tworzy ZA nowy zasób pod identyfikatorem URI / resources, lub kolekcja. Zazwyczaj identyfikator jest zwracany przez serwer.

POST /resources HTTP/1.1

Aktualizacja:

Mogą tylko wykonać za pomocą PUT w następujący sposób:

POŁOŻYĆ

Aktualizuje zasób za pomocą existingResourceId jako identyfikator pod identyfikatorem URI / resources, lub kolekcja.

PUT /resources/<existingResourceId> HTTP/1.1

Wyjaśnienie:

W przypadku REST i URI jako ogólne, masz ogólny na lewo i konkretny na dobrze. The generics zazwyczaj są wywoływane kolekcje i więcej konkretny przedmioty można wywoływać ratunek. Zauważ, że a ratunek może zawierać kolekcja.

Przykłady:

<- rodzajowy - specyficzny ->

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

Kiedy używasz POST, jesteś zawsze odnosząc się do a kolekcja, więc za każdym razem, gdy mówisz:

POST /users HTTP/1.1

publikujesz nowego użytkownika do użytkowników  kolekcja.

Jeśli kontynuujesz i wypróbujesz coś takiego:

POST /users/john HTTP/1.1

to zadziała, ale semantycznie mówisz, że chcesz dodać zasób do Jan  kolekcja pod użytkowników  kolekcja.

Kiedy używasz PUT, odwołujesz się do a ratunek lub pojedynczy przedmiot, prawdopodobnie wewnątrz kolekcja. Więc kiedy mówisz:

PUT /users/john HTTP/1.1

mówisz do aktualizacji serwera, lub utworzyć, jeśli nie istnieje, Jan  ratunek pod użytkowników  kolekcja.

Spec:

Pozwól, że podświetlę niektóre ważne części specyfikacji:

STANOWISKO

The STANOWISKO Metoda służy do żądania serwera pochodzenia zaakceptować podmiot dołączony do wniosku jako Nowy podrzędny zasobu zidentyfikowanego przez Request-URI w wierszu żądania

Stąd tworzy nowy ratunek na kolekcja.

POŁOŻYĆ

The POŁOŻYĆ Metoda żąda, aby dołączony podmiot był przechowywane pod dostarczonym identyfikatorem URI żądania. Jeśli Request-URI odnosi się do już istniejący zasobów, zamknięta jednostka powinna być traktowana jako zmodyfikowana wersja tego, który znajduje się na serwerze źródłowym. Jeśli działa Identyfikator URI żądania nie wskazuje na istniejący zasób i ten URI jest zdolny bycia definiowanym jako Nowy ratunek przez agenta użytkownika z żądaniem, serwer źródłowy może Stwórz zasób z tym URI. "

Stąd tworzenie lub aktualizowanie w oparciu o istnienie ratunek.

Odniesienie:


164
2017-08-14 22:47



Ten post był mi pomocny w zrozumieniu, że POST dodaje "coś" jako dziecko do danej kolekcji (URI), podczas gdy PUT jawnie definiuje "coś" w danej lokalizacji URI. - kwah
To jest najlepsza odpowiedź, tutaj, myślę: żadna z tych "POST nie może zaktualizować zasobu" nonsens. Podoba mi się twoje stwierdzenie: "Aktualizacja może być wykonana tylko przy pomocy PUT". - Thomas
Nie, PUT nie służy do aktualizacji ani tworzenia. To jest do wymiany. Zauważ, że nic nie możesz zastąpić czymś na efekt tworzenia. - thecoshman
@ 7hi4g0 PUT służy do aktualizacji z pełną wymianą, innymi słowy, zastępuje. Nic nie zastąpisz, lub coś zupełnie nowego. PUT nie służy do wprowadzania drobnych zmian (chyba że klient wprowadzi drobną zmianę i dostarczy całą nową wersję, nawet to, co pozostanie bez zmian). W przypadku częściowej modyfikacji PATCH jest metodą z wyboru. - thecoshman
@thecoshman Mógłbyś, ale nie było zbyt jasne, że tworzenie jest również tam uwzględnione. W tym przypadku lepiej jest być wyraźnym. - 7hi4g0


Chciałbym dodać moją "pragmatyczną" radę. Użyj PUT, gdy znasz "id", przy pomocy którego można zapisać obiekt, który zapisujesz. Używanie PUT nie zadziała zbyt dobrze, jeśli potrzebujesz, powiedzmy, wygenerowanego przez bazę danych identyfikatora, który ma zostać zwrócony, abyś mógł dokonać przyszłych wyszukiwań lub aktualizacji.

Tak: Aby zapisać istniejącego użytkownika lub taki, w którym klient generuje identyfikator i zweryfikowano, że identyfikator jest unikalny:

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

W przeciwnym razie użyj POST do początkowego utworzenia obiektu, a PUT do aktualizacji obiektu:

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

155
2018-01-15 19:59



Właściwie tak powinno być POST /users. (Zauważ, że /users jest liczbą mnogą). Ma to wpływ na tworzenie nowego użytkownika i uczynienie go zasobem potomnym /users kolekcja. - DavidRR
@DavidRR uczciwie, jak poradzić sobie z grupami to zupełnie inna debata. GET /users ma sens, czyta się tak, jak chcesz, ale byłbym w porządku GET /user/<id> lub POST /user (z ładunkiem dla wspomnianego nowego użytkownika), ponieważ poprawnie czyta "mnie użytkownicy 5" jest dziwne, ale "pobierz mi użytkownika 5" jest bardziej naturalny. Prawdopodobnie jeszcze upadłbym na stronę pluralizacji :) - thecoshman


POST oznacza "utwórz nowy", jak w "Tutaj jest dane do stworzenia użytkownika, utwórz go dla mnie".

PUT oznacza "wstaw, wymień, jeśli już istnieje", jak w "Tutaj są dane dla użytkownika 5".

POST do example.com/users, ponieważ nie znasz jeszcze adresu URL użytkownika, który chcesz, aby serwer go utworzył.

PUT do example.com/users/id, ponieważ chcesz zastąpić / utworzyć konkretny użytkownik.

POSTOWANIE dwa razy tymi samymi danymi oznacza utworzenie dwóch identycznych użytkowników o różnych identyfikatorach. Dwukrotne użycie tych samych danych tworzy pierwszego użytkownika i aktualizuje go do tego samego stanu po raz drugi (bez zmian). Ponieważ kończysz z tym samym stanem po PUT bez względu na to ile razy go wykonujesz, mówi się, że jest "równie silny" za każdym razem - idempotent. Jest to przydatne do automatycznego ponawiania żądań. Koniec "jesteś pewien, że chcesz ponownie wysłać" po naciśnięciu przycisku Wstecz w przeglądarce.

Ogólna rada polega na użyciu POST, gdy potrzebujesz, aby serwer kontrolował generowanie twoich zasobów przez URL. Użyj PUT inaczej. Wolisz PUT ponad POST.


144
2017-10-23 14:27



Niechlujstwo może spowodować, że będzie powszechnie nauczane, że potrzebujesz tylko dwóch czasowników: GET i POST. POBIERZ, aby uzyskać, POST, aby zmienić. Nawet PUT i DELETE zostały wykonane przy użyciu testu POST. Pytanie o to, co PUT naprawdę znaczy 25 lat później, może znak, że na początku się myliłem. Popularność REST spowodowała, że ​​ludzie powrócili do podstaw, w których musimy oduczyć się po błędach. POST był nadużywany i obecnie powszechnie nauczany niepoprawnie. Najlepsza część: "ZATWIERDZENIE dwa razy tymi samymi danymi oznacza utworzenie dwóch identycznych [zasobów]". Świetny punkt! - maxpolk
W jaki sposób można użyć PUT do utworzenia rekordu według identyfikatora, tak jak w twoim przykładzie user 5 jeśli jeszcze nie istnieje? Nie masz na myśli update, replace if already exists? lub coś - Luke
@Coulton: Miałem na myśli to, co napisałem. Wstawiasz użytkownika 5, jeśli umieścisz PUT na / users / 5, a numer 5 jeszcze nie istnieje. - Alexander Torstling
@Coulton: And PUT można również użyć do zastąpić wartość istniejący zasoby w całości. - DavidRR
"Wolisz PUT ponad POST" ... czy to usprawiedliwia? - thecoshman


Użyj POST do utworzenia i PUT do aktualizacji. Tak czy owak robi to Ruby on Rails.

PUT    /items/1      #=> update
POST   /items        #=> create

104
2018-03-10 14:28



POST /items dodaje nowy element do już zdefiniowanego zasobu ("pozycja"). Nie ma, jak mówi odpowiedź, "tworzyć grupy". Nie rozumiem, dlaczego ma to 12 głosów. - David J.
Po wyjęciu z pudełka, Rails nie obsługuje "tworzenia grupy" poprzez REST. Aby "utworzyć grupę", przez co mam na myśli "Utwórz zasób", musisz to zrobić za pomocą kodu źródłowego. - David J.
Jest to uczciwa wytyczna, ale nadmierne uproszczenie. Jak wspominają inne odpowiedzi, obie metody można zastosować zarówno do tworzenia, jak i aktualizacji. - Brad Koch
Zgadzam się z odpowiedzią z niewielką modyfikacją. Użyj POST do utworzenia i PUT, aby całkowicie zaktualizować zasób. W przypadku częściowych aktualizacji możemy użyć PUT lub PATCH. Powiedzmy, że chcemy zaktualizować status grupy. Możemy użyć PUT / groups / 1 / status ze statusem jest ładunek żądania lub PATCH / groups / 1 ze szczegółami o akcji w polu danych - java_geek
Należy również wyjaśnić, że PUT /items/42 jest również ważny dla tworzenie zasób, ale tylko wtedy, gdy klient ma przywilej nazywania zasobu. (Czy Railsy zezwalają klientowi na takie uprawnienie do nazewnictwa?) - DavidRR


REST to bardzo koncepcja wysokiego poziomu. W rzeczywistości nawet nie wspomina o HTTP!

Jeśli masz jakiekolwiek wątpliwości dotyczące wdrażania REST w HTTP, zawsze możesz rzucić okiem na Atom Publication Protocol (AtomPub) specyfikacja. AtomPub jest standardem do pisania RESTful webservices z HTTP, który został opracowany przez wiele reflektorów HTTP i REST, z pewnym wkładem od Roya Fieldinga, wynalazcy REST i (współ) wynalazcy samego HTTP.

W rzeczywistości możesz nawet używać AtomPub bezpośrednio. Choć pochodzi ze społeczności blogerów, nie jest w żaden sposób ograniczony do blogowania: jest to ogólny protokół do RESTfully interakcji z dowolnymi (zagnieżdżonymi) kolekcjami arbitralnych zasobów za pośrednictwem HTTP. Jeśli możesz reprezentować swoją aplikację jako zagnieżdżoną kolekcję zasobów, możesz po prostu użyć AtomPub i nie martwić się, czy użyć PUT lub POST, jakie kody statusu HTTP powrócić i wszystkie te szczegóły.

Oto, co AtomPub ma do powiedzenia na temat tworzenia zasobów (sekcja 9.2):

Aby dodać członków do kolekcji, klienci wysyłają żądania POST do identyfikatora URI kolekcji.


57
2018-03-10 15:27



Nie ma nic złego w zezwalaniu PP na tworzenie zasobów. Należy pamiętać, że oznacza to, że klient podaje adres URL. - Julian Reschke
Jest coś bardzo nie w porządku z zezwoleniem PUT na tworzenie zasobów: klient podaje adres URL. To jest zadanie serwera! - Joshcodes
@Joshcodes Nie zawsze jest tak, że zadaniem serwera jest tworzenie identyfikatorów klienta. Coraz częściej widzę projekty, które umożliwiają klientom generowanie pewnego rodzaju identyfikatora UUID jako identyfikatora zasobu. Ten wzór nadaje się w szczególności do zwiększenia skali. - Justin Ohms
@JustinOhms Zgadzam się z twoją uwagą na temat generowanych przez klienta identyfikatorów (uwaga na marginesie: wszystkie systemy zaprojektowane przeze mnie od około 2008 r. Wymagają, aby klient utworzył identyfikator jako UUID / Guid). To nie znaczy, że klient powinien podać adres URL. - Joshcodes
@ Joshcodes Jest kwestia oddzielenia obaw. Miejsce, w którym generowany jest URL, nie ma większego znaczenia. Tak, serwer jest odpowiedzialny za dostarczanie treści z prawidłowego adresu URL, ale to nie ogranicza serwera do odpowiedzi na żądanie pod nieprawidłowym adresem URL. Prawidłowa odpowiedź z serwera w tym przypadku to 308. Właściwy klient następnie ponowi próbkę PUT na poprawnym adresie URL. Innym przykładem jest system rozproszony, w którym nie wszystkie węzły znają wszystkie zasoby dostarczane przez klientów. Tutaj PUT do utworzenia byłby całkowicie poprawny, ponieważ dla tego węzła serwera zasób nie istnieje. - Justin Ohms


Decyzja, czy użyć PUT lub POST do utworzenia zasobu na serwerze z interfejsem API HTTP + REST, zależy od tego, kto jest właścicielem struktury adresu URL. Znając klienta lub uczestnicząc w jego definiowaniu, struktura URL jest niepotrzebnym sprzężeniem podobnym do niepożądanych sprzężeń, które powstały z architektury SOA. Uciekające typy sprzężeń powodują, że REST jest tak popularny. W związku z tym, właściwą metodą użycia jest POST. Istnieją wyjątki od tej reguły i występują, gdy klient chce zachować kontrolę nad strukturą rozmieszczenia rozmieszczanych zasobów. Jest to rzadkie i prawdopodobnie oznacza, że ​​coś jest nie tak.

W tym momencie niektórzy ludzie będą argumentować, że jeśli RESTful-URL są używane, klient zna adres URL zasobu, a zatem PUT jest akceptowalny. W końcu dlatego kanoniczne, znormalizowane, Ruby on Rails, adresy URL Django są ważne, spójrz na Twitter API ... bla bla bla. Ci ludzie muszą zrozumieć nie ma czegoś takiego jak Restful-URL i to Sam Roy Fielding stwierdza to:

Interfejs API usług REST nie może definiować nazw ani hierarchii zasobów (np   oczywiste połączenie klienta i serwera). Serwery muszą mieć swobodę   kontrolować ich własną przestrzeń nazw. Zamiast tego pozwól serwerom instruować   klientów, jak budować odpowiednie identyfikatory URI, na przykład w HTML   formularze i szablony URI, definiując te instrukcje na nośniku   rodzaje i relacje między linkami. [Niepowodzenie tutaj oznacza, że ​​klienci są   przy założeniu struktury zasobów ze względu na informacje pozapasmowe, takie jak   standardem specyficznym dla domeny, który jest odpowiednikiem danych   Łączenie funkcjonalne RPC].

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

Idea a RESTful-URL jest faktycznie naruszeniem REST, ponieważ serwer odpowiada za strukturę URL i powinien mieć swobodę decydowania, jak go użyć, aby uniknąć sprzężenia. Jeśli to zawiedzie, przeczytasz o znaczeniu samopoznania w projektowaniu interfejsu API.

Używanie POST do tworzenia zasobów wiąże się z uwzględnieniem projektu, ponieważ test POST nie jest idempotentny. Oznacza to, że wielokrotne powtarzanie testu POST nie gwarantuje tego samego zachowania za każdym razem. To odstrasza ludzi do używania PUT do tworzenia zasobów, gdy nie powinni. Wiedzą, że to źle (POST jest dla CREATE), ale robią to tak czy inaczej, ponieważ nie wiedzą, jak rozwiązać ten problem. Obawa ta została wykazana w następującej sytuacji:

  1. Klient POST wysyła nowy zasób do serwera.
  2. Serwer przetwarza żądanie i wysyła odpowiedź.
  3. Klient nigdy nie otrzymuje odpowiedzi.
  4. Serwer nie jest świadomy, że klient nie otrzymał odpowiedzi.
  5. Klient nie ma adresu URL dla zasobu (dlatego PUT nie jest opcją) i powtarza POST.
  6. POST nie jest idempotentem, a serwer ...

Krok 6 to sytuacja, w której ludzie często nie wiedzą, co robić. Jednak nie ma powodu, aby tworzyć kludge, aby rozwiązać ten problem. Zamiast tego można użyć protokołu HTTP, jak określono w RFC 2616 a serwer odpowiada:

10.4.10 409 Konflikt

Nie można ukończyć żądania z powodu konfliktu z bieżącym   stan zasobu. Ten kod jest dozwolony tylko w sytuacjach, w których   oczekuje się, że użytkownik będzie w stanie rozwiązać konflikt i   ponownie prześlij żądanie. Ciało odpowiedzi powinno zawierać wystarczającą ilość

informacje dla użytkownika o rozpoznaniu źródła konfliktu.   Najlepiej byłoby, gdyby jednostka odpowiedzi zawierała wystarczającą ilość informacji   użytkownik lub agent użytkownika, aby rozwiązać problem; jednak może nie być   możliwe i nie jest wymagane.

Konflikty najprawdopodobniej wystąpią w odpowiedzi na żądanie PUT. Dla   na przykład, jeśli używano wersjonowania, a encja była PUT   włączone zmiany w zasobie, które są sprzeczne z tymi, które zostały wprowadzone przez   wcześniejsze (zewnętrzne) żądanie, serwer może użyć odpowiedzi 409   aby wskazać, że nie może ukończyć żądania. W tym przypadku   Jednostka odpowiedzi prawdopodobnie zawierałaby listę różnic między   dwie wersje w formacie określonym przez typ zawartości odpowiedzi.

Odpowiadanie z kodem statusu 409 Konflikt jest prawidłowym regresem, ponieważ:

  • Wykonywanie POST danych, które mają identyfikator pasujący do zasobu już znajdującego się w systemie, to "konflikt z bieżącym stanem zasobu".
  • Ponieważ ważną częścią jest zrozumienie przez klienta, że ​​serwer ma zasoby i podjęcie odpowiednich działań. Jest to "sytuacja", w której oczekuje się, że użytkownik może rozwiązać konflikt i ponownie przesłać żądanie. "
  • Odpowiedź, która zawiera adres URL zasobu ze sprzecznym identyfikatorem i odpowiednimi warunkami wstępnymi dla zasobu, zapewnia "wystarczającą ilość informacji dla użytkownika lub klienta użytkownika, aby rozwiązać problem", co jest idealnym przypadkiem na dokument RFC 2616.

Aktualizacja oparta na wydaniu RFC 7231 na Replace 2616

RFC 7231 jest przeznaczony do zastąpienia 2616 i w Punkt 4.3.3 opisuje następującą możliwą odpowiedź dla POST

Jeśli wynik przetwarzania POST byłby równoważny z   reprezentacja istniejącego zasobu, serwer inicjujący MOŻE przekierować   agent użytkownika do tego zasobu, wysyłając odpowiedź 303 (Zobacz inne)   z istniejącym identyfikatorem zasobu w polu Lokalizacja. To   ma zalety zapewniania agentowi użytkownika identyfikatora zasobu   i przeniesienie reprezentacji za pomocą metody bardziej podatnej na   wspólne buforowanie, ale kosztem dodatkowego żądania, jeśli użytkownik   agent nie ma już buforowanej reprezentacji.

Może teraz kusić, aby po prostu zwrócić 303 w przypadku powtórzenia testu POST. Jednak jest odwrotnie. Zwrot 303 ma sens tylko wtedy, gdy wiele żądań tworzenia (tworząc różne zasoby) zwróci tę samą treść. Przykładem może być "dziękuję za przesłanie wiadomości z prośbą", że klient nie musi ponownie pobierać za każdym razem. RFC 7231 nadal utrzymuje w sekcji 4.2.2, że POST nie ma być idempotentny i nadal utrzymuje, że test POST powinien być używany do tworzenia.

Aby uzyskać więcej informacji na ten temat, przeczytaj to artykuł.


53
2017-10-29 23:00



Czy reakcja Konflikt 409 byłaby odpowiednim kodem dla czegoś w rodzaju próby utworzenia nowego konta z nazwą użytkownika, która już istnieje? Używałem 409 do rozwiązywania konfliktów dotyczących wersji, ale po przeczytaniu odpowiedzi zastanawiam się, czy nie powinno się jej używać do żadnych "duplikatów" żądań. - Eric B.
@EricB. Tak, w sytuacji, którą opisujesz "z powodu konfliktu z bieżącym stanem zasobu" operacja kończy się niepowodzeniem. Ponadto uzasadnione jest oczekiwanie, że użytkownik może rozwiązać konflikt, a organ komunikacyjny musi tylko poinformować użytkownika, że ​​nazwa użytkownika już istnieje. - Joshcodes
@ Joshcodes możesz powiedzieć więcej o procesie rozwiązywania konfliktów? W takim przypadku, jeśli nazwa użytkownika już istnieje, klient oczekuje, że poprosi użytkownika końcowego o inną nazwę użytkownika? Co się stanie, jeśli klient rzeczywiście próbuje użyć POST, aby zmienić nazwę użytkownika? Czy żądania PUT powinny być nadal używane do aktualizacji parametrów, podczas gdy POST służy do tworzenia obiektów, czy to pojedynczo, czy kilka? Dzięki. - BFar
@ BFar2, jeśli nazwa użytkownika już istnieje, wówczas klient powinien zapytać użytkownika. Aby zmienić nazwę użytkownika, zakładając, że nazwa użytkownika jest częścią już utworzonego zasobu, który wymaga modyfikacji, użyty zostanie PUT, ponieważ masz rację, POST jest używany do tworzenia, zawsze i PUT do aktualizacji. - Joshcodes
wyjaśnienie rzeczy za pomocą krótkiego i skutecznego języka jest również pożądaną umiejętnością - Junchen Liu


Podoba mi się ta rada, od Definicja PUT w RFC 2616:

Podstawowa różnica między żądaniami POST i PUT znajduje odzwierciedlenie w różnym znaczeniu Request-URI. Identyfikator URI w żądaniu POST identyfikuje zasób, który będzie obsługiwał zamknięty obiekt. Ten zasób może być procesem akceptującym dane, bramą do innego protokołu lub osobną jednostką akceptującą adnotacje. Natomiast URI w żądaniu PUT identyfikuje encję dołączoną do żądania - agent użytkownika wie, co to jest URI, a serwer NIE MOŻE próbować zastosować żądania do innego zasobu.

To sugeruje tutaj, że PUT najlepiej nadaje się do zasobów, które mają już nazwę, a POST jest dobre do tworzenia nowego obiektu w ramach istniejącego zasobu (i pozwalania serwerowi na jego nazwę).

Interpretuję to i wymagania idempotencji w PUT, aby:

  • POST jest dobry do tworzenia nowych obiektów w kolekcji (a tworzenie nie musi być idempotentne)
  • PUT jest dobre do aktualizacji istniejących obiektów (i aktualizacji musi być idempotent)
  • POST może być również użyty do niekarmotentowych aktualizacji istniejących obiektów (w szczególności do zmiany części obiektu bez określania całej rzeczy - jeśli o tym pomyśleć, utworzenie nowego członka kolekcji jest w rzeczywistości specjalnym przypadkiem tego rodzaju aktualizacja, z punktu widzenia kolekcji)
  • PUT można również użyć do utworzenia wtedy i tylko wtedy, gdy pozwalasz klientowi na nazwanie zasobu. Ale ponieważ klienci REST nie powinni przyjmować założeń dotyczących struktury adresów URL, jest to mniej zgodne z zamierzonym duchem.

48
2018-01-11 17:18



"POST może być również użyty do niekarmotentowych aktualizacji istniejących obiektów (w szczególności do zmiany części obiektu bez określania całej rzeczy" To właśnie jest PATCH dla - Snuggs