2011-04-16 8 views
21

Aby być bardziej precyzyjnym:należy użyć metody PUT do aktualizacji, jeśli ja również aktualizować atrybutu datownika

Według stylu spoczynkowej, to generalnie assummed że POST, GET, PUT i DELETE metod HTTP powinien być stosowany CREATE, READ, UPDATE i DELETE (CRUD) operacji.

W rzeczywistości, jeśli będziemy trzymać się definicji metod HTTP rzecz może nie być tak oczywiste

W this article to wyjaśnił, że:

W skrócie: użycie PUT wtedy i tylko wtedy, gdy wiesz, zarówno adres URL, w którym zasób będzie żyć, jak i całość zawartości zasobu. W przeciwnym razie użyj POST.

Głównie dlatego

PUT jest znacznie bardziej restrykcyjne czasownik. Zajmuje kompletny zasób i przechowuje go pod podanym adresem URL. Jeśli wcześniej był tam zasób, jest on zastępowany; jeśli nie, tworzony jest nowy. Te właściwości obsługują idempotencję, której nie można naiwnie utworzyć ani zaktualizować. Podejrzewam, że to może być powód, dla którego PUT jest zdefiniowane tak, jak jest; jest to idempotentna operacja, która umożliwia klientowi wysyłanie informacji do serwera.

W moim przypadku zazwyczaj wydać aktualizacje przechodzących wszystkich danych zasobów, więc mogę użyć PUT do aktualizacji, ale za każdym razem wystawia aktualizacji zapisać kolumnę LastUser i LastUpdate, z identyfikatorem użytkownika, który dokonał modyfikacji i czas operacji.

Chciałbym więc poznać Pana zdanie, ponieważ ściśle mówiąc te dwie kolumny nie są częścią zasobu, ale uniemożliwiają operację bez idepotencji.

saludos

sas

+0

W jaki sposób reprezentujesz 'LastUser' i' LastUpdate' - czy są one częścią reprezentacji zasobów (np. Węzły w XML)? – MicE

+0

Nie, one nawet nie istnieją, gdy issiung aktualizacji, ale zwracam je, gdy kwerendy z get .... tak zrobię PUT, a następnie GET, i otrzymuję czas ostatniej aktualizacji, wydaje ponownie to samo PUT, i inny GET daje inny lastUpdate ... – opensas

+0

OK, dziękuję za potwierdzenie - zobacz moją odpowiedź poniżej dla alternatywnego podejścia do problemu. – MicE

Odpowiedz

20

Ignorowanie komentarza dotyczącego stylu REST odwzorowującego CRUD na metody HTTP, jest to doskonałe pytanie.

Odpowiedź na twoje pytanie brzmi: tak, możesz korzystać z PUT w tym scenariuszu, mimo że niektóre elementy tego zasobu są aktualizowane przez serwer w sposób nie idepotentny. Niestety, uzasadnienie odpowiedzi jest dość ogólnikowe. Ważne jest, aby zrozumieć, co było celem zlecenia klienta. Klient zamierzał całkowicie zastąpić zawartość zasobu wartościami przekazanymi.Klient nie jest odpowiedzialny za wykonywanie przez serwer innych operacji, dlatego też semantyka metody HTTP nie jest naruszona.

Jest to uzasadnienie umożliwiające serwerowi aktualizację licznika stron podczas wykonywania operacji GET. Klient nie prosił o aktualizację, dlatego GET jest bezpieczny, mimo że serwer zdecydował się na aktualizację.

Całość, kompletne źródło kontra debaty zasobów częściowego została wreszcie podkreślono w aktualizacji do HTTP spec

Serwer pochodzenia powinna odrzucić wszelkie PUT wniosek, który zawiera pole nagłówka Content-klasy, ponieważ to może być błędnie interpretowane jako częściowa treść (lub może być częściową treścią , która jest omyłkowo PUT jako pełna reprezentacja ). Częściowe zawartość aktualizacje są możliwe kierowania oddzielnie zidentyfikować zasób stanu, który zachodzi na część większa zasobów, lub za pomocą inny sposób, który został konkretnie zdefiniowane przy częściowym aktualizacji (na przykład, plaster metoda zdefiniowana w [RFC5789]).

To, co powinniśmy zrobić, jest teraz jasne. Nie jest tak jasne, dlaczego istnieje takie ograniczenie, gdy tylko można wysyłać pełne odpowiedzi. To pytanie zostało zadane, a IMHO pozostaje bez odpowiedzi w tym wątku na temat dyskusji na temat odpoczynku.

+0

Tak, zgadzam się. Mimo to używanie nagłówków PATCH lub Content-Range nie jest właściwe, ponieważ klient próbuje zmodyfikować cały zasób (przynajmniej te części, które klient może modyfikować). Zobacz moją odpowiedź na inne podejście do problemu.Wiem, że to także nie jest kuloodporne, ale próbuje przynajmniej trochę zaradzić problemowi. – MicE

+2

http://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/ - Nie nazywam nikogo idiotą, ale jest to przydatna lektura. – backdesk

3

wadą stosowania oddany do tworzenia zasobów jest to, że klient musi dostarczyć unikatowy identyfikator, który reprezentuje obiekt to tworzenie. Chociaż zwykle możliwe jest wygenerowanie tego unikalnego identyfikatora przez klienta, większość projektantów aplikacji woli, aby ich serwery (zwykle za pośrednictwem baz danych) tworzyły ten identyfikator. W większości przypadków chcemy, aby nasz serwer kontrolował generowanie identyfikatorów zasobów. Więc co robimy? Możemy przełączyć się na używanie POST zamiast PUT.

Więc:

Put = UPDATE

post = INSERT

Mamy nadzieję, że pomoże to dla konkretnego przypadku.

+0

cóż, mówię o aktualizacji, w której znam id ... Nie mówię o wstawce ... – opensas

6

Nie można modyfikować ich przez klienta, więc usunęłbym je z reprezentacji zasobów w ogóle. Pozwól mi wyjaśnić moje rozumowanie na przykładzie.

Powiedzmy, że nasz typowy przykład API zwróci następującą reprezentację do klienta, gdy poproszony o podanie jednego zasobu:

GET /example/123 

<?xml version="1.0" encoding="UTF-8" ?> 
<example> 
    <id>123</id> 
    <lorem>ipsum</lorem> 
    <dolor>sit amet</dolor> 
    <lastUser uri="/user/321">321</lastUser> 
    <lastUpdate>2011-04-16 20:00:00 GMT</lastUpdate> 
</example> 

Jeśli klient chce zmodyfikować zasób, że będzie przypuszczalnie podjąć całą reprezentację i odeślij ją do interfejsu API.

PUT /example/123 

<?xml version="1.0" encoding="UTF-8" ?> 
<example> 
    <id>123</id> 
    <lorem>foobar</lorem> 
    <dolor>foobaz</dolor> 
    <lastUser>322</lastUser> 
    <lastUpdate>2011-04-16 20:46:15 GMT+2</lastUpdate> 
</example> 

Ponieważ API generuje wartości lastUser i lastUpdate automatycznie i nie może zaakceptować danych dostarczonych przez klienta, najwłaściwszą odpowiedzią byłoby 400 Bad Request lub 403 Forbidden (ponieważ klient nie może modyfikować te wartości).

Jeśli chcemy zachować zgodność z usługą REST i wysłać pełną reprezentację zasobu podczas wykonywania żądania PUT, musimy usunąć lastUser i z reprezentacji zasobu. Pozwoli to klientom na wysyłanie pełną jednostkę poprzez PUT:

PUT /example/123 

<?xml version="1.0" encoding="UTF-8" ?> 
<example> 
    <id>123</id> 
    <lorem>foobar</lorem> 
    <dolor>foobaz</dolor> 
</example> 

Serwer zaakceptuje pełną reprezentację teraz, że nie zawierają lastUpdate i lastUser.

Pozostaje pytanie, w jaki sposób zapewnić klientom dostęp do lastUpdate i lastUser. Jeśli go nie potrzebują (i te pola są wymagane tylko wewnętrznie przez API), wszystko jest w porządku, a nasze rozwiązanie jest doskonale URUCHOMIONE.Jeśli jednak klienci potrzebują dostępu do tych danych, najczystsze podejście byłoby użyć nagłówków HTTP:

GET /example/123 

... 
Last-Modified: Sat, 16 Apr 2011 18:46:15 GMT 
X-Last-User: /user/322 
... 

<?xml version="1.0" encoding="UTF-8" ?> 
<example> 
    <id>123</id> 
    <lorem>foobar</lorem> 
    <dolor>foobaz</dolor> 
</example> 

Korzystanie z niestandardowy nagłówek HTTP nie jest idealny, ponieważ aplikacje klienckie muszą się nauczyć, w jaki sposób ją przeczytać. Jeśli chcemy zapewnić klientom łatwiejszy dostęp do tych samych danych, jedyne, co możemy zrobić, to umieścić dane w reprezentacji i mamy taki sam problem, jak w pierwotnym pytaniu. Przynajmniej spróbuję jakoś go złagodzić. Jeżeli typ zawartości używane przez API XML, możemy umieścić dane do węzła atrybutów zamiast wystawiając je bezpośrednio jako wartości węzłów, tj:

GET /example/123 

... 
Last-Modified: Sat, 16 Apr 2011 18:46:15 GMT 
... 

<?xml version="1.0" encoding="UTF-8" ?> 
<example last-update="2011-04-16 18:46:15 GMT" last-user="/user/322"> 
    <id>123</id> 
    <lorem>foobar</lorem> 
    <dolor>foobaz</dolor> 
</example> 

ten sposób będziemy przynajmniej uniknąć problemu Jeżeli klient spróbuje przesłać wszystkie węzły XML w kolejnym zapytaniu PUT. To nie zadziała z JSON-em, a rozwiązanie wciąż jest na granicy idempotencji (ponieważ API musiałoby zignorować atrybuty XML podczas przetwarzania żądania).

Jeszcze lepiej, jak Jonah zauważył w komentarzach, jeśli klienci potrzebują dostępu do lastUser i lastUpdate, mogą być narażeni jako nowy zasób powiązany z oryginału jeden przykład w następujący sposób:

GET /example/123 

<?xml version="1.0" encoding="UTF-8" ?> 
<example> 
    <id>123</id> 
    <lorem>foobar</lorem> 
    <dolor>foobaz</dolor> 
    <lastUpdateUri>/example/123/last-update</lastUpdateUri> 
</example> 

... a potem:

GET /example/123/last-update 

<?xml version="1.0" encoding="UTF-8" ?> 
<lastUpdate> 
    <resourceUri>/example/123</resourceUri> 
    <updatedBy uri="/user/321">321</updatedBy> 
    <updatedAt>2011-04-16 20:00:00 GMT</updatedAt> 
</lastUpdate> 

(. Powyższe można również ładnie rozbudowany, aby zapewnić pełną dziennika kontroli z poszczególnych zmian, zapewniając changelog zasób jest dostępny)

Uwaga:
zgadzam się z Darrel Miller „s take on the question, ale chciałem pro zobaczyć na nim inne podejście. Zauważ, że takie podejście nie jest wspierane przez żadne standardy/RFC/etc, to tylko inne podejście do problemu.

+0

Dzięki za alternatywne podejście. Używanie nagłówka "ostatniej modyfikacji" wydaje się być całkiem dobrym pomysłem - zwłaszcza jeśli chodzi o buforowanie lub odrzucanie nieaktualnych aktualizacji (tj. Zasobów, które zostały edytowane przez kogoś innego, podczas gdy byłeś zajęty ich zmienianiem). Myśli? – backdesk

+1

dlaczego nie wystawiać go na nowy punkt końcowy? w końcu, jak przekonywująco argumentowałeś, jest to oddzielna koncepcja od oryginalnego zasobu: 'example/123/lastUpdate' – Jonah

+0

@Jonah - to doskonały punkt, dziękuję. Zaktualizowałem odpowiedź, aby ją uwzględnić. (Zauważ, że np. Identyfikatory URI w edycji mogą być bardziej odpowiednie jako atrybuty węzła XML, bieżący format powinien umożliwiać bardziej bezpośrednie tłumaczenie na JSON w razie potrzeby). – MicE

0

Metody HTTP POST i PUT nie są odpowiednikiem HTTP tworzenia i aktualizacji CRUD. Obaj służą innym celom. Jest całkiem możliwe, ważne, a nawet preferowane w niektórych sytuacjach użycie PUT do tworzenia zasobów lub użycie POST do aktualizacji zasobów.

Użyj PUT, gdy możesz całkowicie zaktualizować zasób za pomocą określonego zasobu. Na przykład, jeśli wiesz, że artykuł znajduje się pod adresem http://example.org/article/1234, możesz umieścić nową reprezentację zasobów tego artykułu bezpośrednio przez PUT na tym adresie URL.

Jeśli nie znasz rzeczywistej lokalizacji zasobu, na przykład, po dodaniu nowego artykułu, ale nie masz pojęcia, gdzie go przechowywać, możesz POST umieścić go pod adresem URL i pozwolić serwerowi URL.

Powiązane problemy