Wykorzystanie metod PUT vs PATCH w scenariuszach REST API

Po pierwsze, niektóre definicje:

PUT jest zdefiniowany w sekcja 9.6 RFC 2616:

Metoda PUT wymaga, aby zamknięty podmiot był przechowywany pod dostarczonym Request-URI. Jeśli Request-URI odnosi się do już istniejącego zasobu, zamknięty podmiot powinien być traktowany jako zmodyfikowana wersja tego, który znajduje się na serwerze źródłowym. Jeśli Request-URI nie wskazuje na istniejący zasób, a URI może być serwer źródłowy zdefiniowany jako nowy zasób przez żądającego agenta użytkownika może utworzyć zasób z tym URI.

PATCH jest zdefiniowany w RFC 5789:

Metoda PATCH wymaga zbioru zmian opisanych w Jednostka żądania ma zastosowanie do zasobu określonego przez żądanie- URI.

Również zgodnie z RFC 2616 sekcja 9.1.2 PUT jest Idempotentny, podczas gdy PATCH nie jest.

Teraz spójrzmy na prawdziwym przykładzie. Gdy wykonam POST do /users z danymi {username: 'skwee357', email: '[email protected]'} i serwer jest w stanie utworzyć zasób, odpowie 201 i resource location (Załóżmy /users/1) i każde następne wywołanie do GET /users/1 zwróci {id: 1, username: 'skwee357', email: '[email protected]'}.

Teraz powiedzmy, że chcę zmodyfikować mój e-mail. Modyfikacja wiadomości e-mail jest uważana za "zestaw zmian" i dlatego powinienem łatać /users/1 za pomocą "Patch document". W moim przypadku byłby to dokument json: {email: '[email protected]'}. Serwer zwraca wtedy 200 (zakładając zgoda jest ok). To sprowadza mnie do pierwszego pytania:

  • PATCH nie jest idempotentny. Tak było w RFC 2616 i RFC 5789. Jeśli jednak wydam to samo żądanie poprawki (z moim nowym e-mailem), otrzymam ten sam stan zasobów (z moim e-mailem jest modyfikowany do żądanej wartości). Dlaczego PATCH nie jest idempotentny?

PATCH to stosunkowo nowy czasownik (RFC wprowadzony w marcu 2010 roku), który rozwiązuje problem "łatania" lub modyfikowania zbioru pól. Przed łatką wprowadzono, wszyscy używali PUT do aktualizacji zasobów. Ale po wprowadzeniu patcha, Nie wiem, Do czego służy PUT. I to sprowadza mnie do mojego drugiego (i głównego) pytania:

  • Jaka jest prawdziwa różnica między PUT a PATCH? Czytałem gdzieś, że PUT może być używany do zastąpienia całego encji pod określonym zasobem, więc należy wysłać cały encję (zamiast zestawu atrybutów jak w patchu). Jakie jest praktyczne zastosowanie takiego przypadku? Kiedy chcesz zastąpić / zastąpić encję na określonym Uri zasobu i dlaczego taka operacja nie jest uważana za aktualizację / łatanie encji? Jedynym praktycznym przypadkiem użycia, jaki widzę dla PUT, jest wydanie PUT na zbiorze, tzn. /users, aby zastąpić cały zbiór. Wystawianie na konkretny podmiot nie ma sensu po wprowadzeniu patcha. Mylę się?
Author: SunSparc, 2015-02-11

11 answers

Uwaga: kiedy po raz pierwszy czytałam o odpoczynku, idempotencja była mylącą koncepcją, aby spróbować się poprawić. Nadal nie udało mi się to w mojej pierwotnej odpowiedzi, jak pokazały dalsze komentarze (i Jason Hoetger ' s answer). Od jakiegoś czasu oparłem się obszernej aktualizacji tej odpowiedzi, aby uniknąć skutecznego plagiatu, ale edytuję ją teraz, ponieważ, cóż, zostałem poproszony (w komentarzach).

Po przeczytaniu mojej odpowiedzi proponuję również przeczytać doskonała odpowiedź Jasona Hoetgera na to pytanie, i postaram się poprawić moją odpowiedź, Nie okradając Jasona.

Dlaczego PUT idempotent?

Jak zauważyłeś w swoim cytacie RFC 2616, PUT jest uważany za idempotentny. Kiedy umieścisz zasób, te dwa założenia są w grze:

  1. Odnosisz się do podmiotu, a nie do kolekcji.

  2. Encja, którą dostarczasz, jest kompletna (całość entity).

Przyjrzyjmy się jednemu z Twoich przykładów.
{ "username": "skwee357", "email": "[email protected]" }

Jeśli opublikujesz ten dokument do /users, jak sugerujesz, możesz odzyskać podmiot taki jak

## /users/1

{
    "username": "skwee357",
    "email": "[email protected]"
}

Jeśli chcesz później zmodyfikować ten element, Wybierz pomiędzy PUT I PATCH. A PUT może wyglądać tak:

PUT /users/1
{
    "username": "skwee357",
    "email": "[email protected]"       // new email address
}

Możesz osiągnąć to samo używając patcha. To może wyglądać tak:

PATCH /users/1
{
    "email": "[email protected]"       // new email address
}

Od razu zauważysz różnicę między tymi dwoma. W zestawie znalazły się wszystkie parametry tego użytkownika, ale PATCH zawierał tylko ten, który był modyfikowany (email).

Podczas używania PUT zakłada się, że wysyłasz pełną jednostkę, a ta kompletna jednostka zastępuje dowolną istniejącą jednostkę na tym URI. W powyższym przykładzie PUT I PATCH osiągają ten sam cel: obie zmieniają adres e-mail tego użytkownika. Ale PUT obsługuje go, zastępując cały obiekt, podczas gdy PATCH aktualizuje tylko pola, które zostały dostarczone, pozostawiając pozostałe sam.

Ponieważ żądania PUT obejmują całą jednostkę, jeśli wielokrotnie wystawiasz to samo żądanie, zawsze powinno mieć ten sam wynik (dane, które wysłałeś, są teraz całymi danymi jednostki). Dlatego PUT jest idempotentny.

Using PUT wrong

Co się stanie, jeśli użyjesz powyższych danych łaty w zapytaniu PUT?

GET /users/1
{
    "username": "skwee357",
    "email": "[email protected]"
}
PUT /users/1
{
    "email": "[email protected]"       // new email address
}

GET /users/1
{
    "email": "[email protected]"      // new email address... and nothing else!
}

(zakładam na potrzeby tego pytania, że serwer nie ma żadnych konkretnych wymaganych pól, i pozwoli to na zdarza się... w rzeczywistości może tak nie być.)

Ponieważ użyliśmy PUT, ale tylko dostarczaliśmy email, Teraz jest to jedyna rzecz w tej istocie. Spowodowało to utratę danych.

Ten przykład jest tutaj dla celów ilustracyjnych - nigdy tak naprawdę nie rób. To żądanie PUT jest technicznie idempotentne, ale to nie znaczy, że nie jest to straszny, zepsuty pomysł.

Jak PATCH może być idempotentny?

W powyższym przykładzie PATCH był idempotentny. Zrobiłeś zmiana, ale jeśli dokonałeś tej samej zmiany raz po raz, zawsze zwracasz ten sam wynik: zmieniłeś adres e-mail na nową wartość.

GET /users/1
{
    "username": "skwee357",
    "email": "[email protected]"
}
PATCH /users/1
{
    "email": "[email protected]"       // new email address
}

GET /users/1
{
    "username": "skwee357",
    "email": "[email protected]"       // email address was changed
}
PATCH /users/1
{
    "email": "[email protected]"       // new email address... again
}

GET /users/1
{
    "username": "skwee357",
    "email": "[email protected]"       // nothing changed since last GET
}

Mój oryginalny przykład, ustalony dla dokładności

Początkowo miałem przykłady, które myślałem, że pokazują Nie-idempotencję, ale były mylące / błędne. Zachowam przykłady, ale użyję ich do zilustrowania innej rzeczy: że wiele dokumentów łatek przeciwko temu samemu obiektowi, modyfikując różne atrybuty, zrobić nie sprawiają, że łatki nie są idempotentne.

Powiedzmy, że w jakimś czasie, Użytkownik został dodany. To jest stan, od którego zaczynacie.

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "10001"
}

Po patchu masz zmodyfikowany byt:

PATCH /users/1
{"email": "[email protected]"}

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",    // the email changed, yay!
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "10001"
}

Jeśli następnie wielokrotnie zastosujesz łatkę, nadal otrzymasz ten sam wynik: wiadomość e-mail została zmieniona na nową wartość. A wchodzi, a wychodzi, dlatego jest to idempotentne.

Godzinę później, po tym jak pójdziesz zrobić kawę i zrobić sobie przerwę, ktoś inny przychodzi z własnym plastrem. Wygląda na to, że Poczta wprowadza pewne zmiany.

PATCH /users/1
{"zip": "12345"}

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",  // still the new email you set
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "12345"                      // and this change as well
}

Ponieważ ta łatka z poczty Nie dotyczy poczty e-mail, tylko kodu pocztowego, jeśli zostanie wielokrotnie zastosowana, również otrzyma ten sam wynik: kod pocztowy jest ustawiony na nową wartość. A wchodzi, a wychodzi, dlatego jest to również idempotentne.

Następnego dnia decydujesz się na ponowne wysłanie plastra.

PATCH /users/1
{"email": "[email protected]"}

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "12345"
}

Twój plaster ma taki sam efekt miał wczoraj: ustawił adres e-mail. A wszedł, a wyszedł, dlatego też jest to idempotentne.

What I got wrong in my original answer

Chcę narysować ważne rozróżnienie (coś, co pomyliłem w mojej pierwotnej odpowiedzi). Wiele serwerów odpowie na twoje prośby o odpoczynek, wysyłając z powrotem nowy stan jednostki, z Twoimi modyfikacjami (jeśli istnieją). Więc, kiedy otrzymasz odpowiedź z powrotem, to jest inna od tej, którą dostałeś wczoraj, ponieważ kod pocztowy nie jest Tym, który otrzymałeś ostatnim razem. Jednak twoja prośba nie dotyczyła kodu pocztowego, tylko wiadomości e-mail. Tak więc twój dokument patcha jest nadal idempotentny - e-mail, który wysłałeś w patchu, jest teraz adresem e-mail jednostki.

Więc kiedy PATCH nie jest idempotentny?

W celu pełnego potraktowania tego pytania ponownie odsyłam do odpowiedzi Jasona Hoetgera . Po prostu to zostawię, bo szczerze mówiąc Nie wiem, czy potrafię odpowiedzieć na tę część lepiej niż on.

 1060
Author: Dan Lowe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:49

Chociaż doskonała odpowiedź dana Lowe ' a bardzo dokładnie odpowiedziała na pytanie OP o różnicę między PUT i patchem, jego odpowiedź na pytanie, dlaczego PATCH nie jest idempotentny, nie jest całkiem poprawna.

Aby pokazać, dlaczego PATCH nie jest idempotentny, warto zacząć od definicji idempotencji (z Wikipedia):

Termin idempotent jest używany bardziej kompleksowo do opisania operacji, która przyniesie te same wyniki, jeśli zostanie wykonana raz lub wielokrotnie [...] Funkcja idempotentna to taka, która ma właściwość f(f(x)) = f (x) dla dowolnej wartości x.

W bardziej przystępnym języku, idempotentna łata może być zdefiniowana jako: po Załataniu zasobu z dokumentem łaty, wszystkie kolejne wywołania łaty do tego samego zasobu z tym samym dokumentem łaty nie zmienią zasobu.

Odwrotnie, operacja nie-idempotentna jest taka, gdzie f(f (x)) != f (x), które dla patcha można określić jako: po łataniu zasobu w przypadku dokumentu łaty kolejne wywołania łaty do tego samego zasobu z tym samym dokumentem łaty powodują zmianę zasobu.

Aby zilustrować nie idempotentną łatkę, Załóżmy, że istnieje zasób / users i załóżmy, że wywołanie GET /users zwraca listę użytkowników, aktualnie:

[{ "id": 1, "username": "firstuser", "email": "[email protected]" }]

Zamiast patchowania /users/{id}, jak w przykładzie OP, Załóżmy, że serwer zezwala na Patchowanie / users. Wypuśćmy tę prośbę o łatkę:

PATCH /users
[{ "op": "add", "username": "newuser", "email": "[email protected]" }]

Nasz dokument patch instruuje serwer aby dodać nowego Użytkownika o nazwie newuser do listy użytkowników. Po wywołaniu tego po raz pierwszy, GET /users zwróci:

[{ "id": 1, "username": "firstuser", "email": "[email protected]" },
 { "id": 2, "username": "newuser", "email": "[email protected]" }]

Teraz, jeśli wystawimy dokładnie to samo żądanie poprawki jak powyżej, co się stanie? (Dla tego przykładu załóżmy, że zasób / users pozwala na duplikowanie nazw użytkowników.) "Op "to " add", więc nowy użytkownik jest dodawany do listy, a następnie GET /users zwraca:

[{ "id": 1, "username": "firstuser", "email": "[email protected]" },
 { "id": 2, "username": "newuser", "email": "[email protected]" },
 { "id": 3, "username": "newuser", "email": "[email protected]" }]

Zasób /users zmienił się ponownie , mimo że wydaliśmy dokładnie ten sam plaster przeciwko dokładnie ten sam Punkt końcowy. Jeśli nasza łata to f( x), f(f(x)) nie jest tym samym co f(x), a zatem ta konkretna łata nie jest idempotentna.

Chociaż PATCH nie jest gwarantowany jako idempotent, nie ma nic w specyfikacji patcha, aby uniemożliwić wykonanie wszystkich operacji PATCH na danym serwerze idempotent. RFC 5789 przewiduje nawet korzyści płynące z żądań poprawek idempotent:

A Żądanie poprawki może być wydane w taki sposób, aby było idempotentne, co pomaga również zapobiegać złym skutkom kolizji między dwoma Żądania poprawek dotyczące tego samego zasobu w podobnym czasie.

W przykładzie dana, jego operacja łatki jest w rzeczywistości idempotentna. W tym przykładzie, encja /users/1 zmieniła się pomiędzy naszymi żądaniami poprawek, ale nie z powodu naszych żądań poprawek; to w rzeczywistości inny dokument z łatą poczty spowodował, że zip kod do zmiany. Inna łatka Urzędu Pocztowego to inna operacja; jeśli nasza łatka to f (x), to łatka Urzędu Pocztowego to g(x). Idempotencja stwierdza, że f(f(f(x))) = f(x), ale nie gwarantuje o f(g(f(x))).

 369
Author: Jason Hoetger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-10 20:11:21

Byłem również ciekaw tego i znalazłem kilka ciekawych artykułów. Mogę nie odpowiedzieć na twoje pytanie w pełnym zakresie, ale to przynajmniej dostarcza więcej informacji.

Http://restful-api-design.readthedocs.org/en/latest/methods.html

RFC HTTP określa, że PUT musi przyjmować pełny nowy zasób reprezentacja jako podmiot wnioskujący. Oznacza to, że jeśli na przykład podane są tylko pewne atrybuty, które należy usunąć (np. ustawić na null).

Biorąc to pod uwagę, wtedy PUT powinien wysłać cały obiekt. Na przykład,

/users/1
PUT {id: 1, username: 'skwee357', email: '[email protected]'}

To skutecznie zaktualizowałoby e-mail. Powodem może nie być zbyt skuteczne jest to, że tylko naprawdę modyfikowanie jednego pola i tym nazwę użytkownika jest trochę bezużyteczny. Następny przykład pokazuje różnicę.

/users/1
PUT {id: 1, email: '[email protected]'}

Teraz, jeśli PUT został zaprojektowany zgodnie ze specyfikacją, wtedy PUT ustawiłby nazwę Użytkownika na null i otrzymałbyś następujące z powrotem.

{id: 1, username: null, email: '[email protected]'}

Kiedy używasz łatki, aktualizujesz tylko pole, które podałeś, a resztę zostawiasz w spokoju, jak w przykładzie.

Poniższe ujęcie plastra jest nieco inne niż nigdy wcześniej.

Http://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/

Różnica między żądaniami PUT I PATCH znajduje odzwierciedlenie w sposób, w jaki serwer przetwarza zamknięty obiekt w celu modyfikacji zasobu zidentyfikowane przez żądanie-URI. W zapytaniu PUT, zamknięty podmiot jest uważana za zmodyfikowaną wersję zasobu przechowywanego na serwera origin, a klient żąda, aby zapisana wersja była zastąpiony. Z patchem, jednak zamknięty podmiot zawiera zestaw instrukcje opisujące w jaki sposób zasób aktualnie znajduje się na serwer origin powinien zostać zmodyfikowany, aby stworzyć nową wersję. Naszywka metoda wpływa na zasób zidentyfikowany przez Request-URI, a także Mogą mieć skutki uboczne dla innych zasobów, tj., nowe zasoby mogą być utworzone lub istniejące zmodyfikowane przez zastosowanie poprawki.

PATCH /users/123

[
    { "op": "replace", "path": "/email", "value": "[email protected]" }
]

Traktujesz mniej więcej łatkę jako sposób na aktualizację pola. Więc zamiast wysyłać częściowy obiekt, wysyłasz operację. czyli Zastąp wiadomość e-mail wartością.

Artykuł kończy się na tym.

Warto wspomnieć, że PATCH nie jest tak naprawdę przeznaczony do naprawdę odpoczynku APIs, ponieważ praca doktorska Fieldinga nie definiuje żadnego sposobu na częściowo Modyfikuj zasoby. Ale sam Roy Fielding powiedział, że łatka jest coś [On] stworzył dla początkowej propozycji HTTP/1.1, ponieważ częściowe umieszczenie nigdy nie jest spokojne. Na pewno nie przenosisz kompletnego reprezentacyjne, ale odpoczynek nie wymaga reprezentowania i tak kompletne.

Nie wiem, czy szczególnie zgadzam się z artykułem, jak wskazuje wielu komentatorów. Wysłanie częściowej reprezentacji może być łatwo opisem zmian.

Dla mnie, Jestem zmieszany z używaniem patcha. W przeważającej części będę traktował PUT jako łatkę, ponieważ jedyną realną różnicą, jaką do tej pory zauważyłem, jest to, że PUT "should" ustawia brakujące wartości na null. Może nie jest to "najbardziej poprawny" sposób, ale powodzenia w kodowaniu.

 78
Author: Kalel Wade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-05 16:55:38

TLDR-Dumbed Down Version

PUT = > Ustaw wszystkie nowe atrybuty dla istniejącego zasobu.

PATCH = > częściowo zaktualizuje istniejący zasób (nie wszystkie atrybuty wymagane).

 30
Author: Bijan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-17 18:55:17

Różnica między PUT I PATCH jest taka, że:

  1. PUT musi być idempotentny. Aby to osiągnąć, musisz umieścić cały kompletny zasób w treści żądania.
  2. łatka może nie być idempotentna. Co oznacza, że może być również idempotentny w niektórych przypadkach, takich jak opisane przypadki.

PATCH wymaga jakiegoś "języka łatek", aby powiedzieć serwerowi, jak zmodyfikować zasób. Wywołujący i serwer muszą zdefiniować pewne "operacje", takie jak " Dodaj", "replace", "delete". Na przykład:

GET /contacts/1
{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",
  "state": "NY",
  "zip": "10001"
}

PATCH /contacts/1
{
 [{"operation": "add", "field": "address", "value": "123 main street"},
  {"operation": "replace", "field": "email", "value": "[email protected]"},
  {"operation": "delete", "field": "zip"}]
}

GET /contacts/1
{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",
  "state": "NY",
  "address": "123 main street",
}

Zamiast używać jawnych pól "operation", język łaty może uczynić go niejawnym, definiując konwencje takie jak:

W treści żądania poprawki:

  1. istnienie pola oznacza "zastąp" lub "dodaj" to pole.
  2. jeśli wartość pola jest null, oznacza to usunięcie tego pola.

Zgodnie z powyższą konwencją PATCH w przykładzie może mieć następującą postać:

PATCH /contacts/1
{
  "address": "123 main street",
  "email": "[email protected]",
  "zip":
}

Który wygląda bardziej zwięźle i przyjazny dla użytkownika. Ale użytkownicy muszą być świadomi podstawowej konwencji.

Z operacjami, o których wspomniałem powyżej, PATCH jest nadal idempotentny. Ale jeśli zdefiniujesz operacje takie jak:" increment "lub" append", możesz łatwo zobaczyć, że nie będzie już idempotentny.

 20
Author: Bin Ni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-17 19:12:54

Pozwolę sobie zacytować i skomentować dokładniej RFC 7231 sekcja 4.2.2 , cytowana już we wcześniejszych komentarzach:

Metoda żądania jest uważana za "idempotentną", jeśli zamierzony efekt na serwer wielu identycznych żądań z tą metodą jest taki sam jako skutek dla jednego takiego wniosku. Metody żądania zdefiniowane przez niniejszą specyfikację, metody PUT, DELETE i safe request są idempotentne.

(...)

Metody Idempotentne to wyróżnione, ponieważ wniosek może być powtarzane automatycznie w przypadku awarii komunikacji przed klient jest w stanie odczytać odpowiedź serwera. Na przykład, jeśli klient wysyła żądanie PUT, a połączenie bazowe jest zamknięte przed otrzymaniem jakiejkolwiek odpowiedzi klient może ustanowić nową połączenie i Ponów próbę żądania idempotent. Wie, że powtarzanie wniosek będzie miał taki sam zamierzony efekt, nawet jeśli oryginał Prośba się powiodła, choć odpowiedź może się różnić.

Więc, co powinno być "to samo" po wielokrotnym żądaniu metody idempotentnej? Nie stan serwera, ani odpowiedź serwera, ale zamierzony efekt. W szczególności metoda powinna być idempotentna "z punktu widzenia klienta". Myślę, że ten punkt widzenia pokazuje, że ostatni przykład w odpowiedzi dana Lowe ' a , której nie chcę tutaj plagiatować, rzeczywiście pokazuje, że żądanie poprawki może być nie-idempotentne (w bardziej naturalny sposób niż przykład w Jason Hoetger ' s answer ).

Rzeczywiście, uczyńmy przykład nieco bardziej precyzyjnym, czyniąc jawnym jeden możliwy Intent dla pierwszego klienta. Powiedzmy, że ten klient przechodzi przez listę użytkowników z projektem, aby sprawdzić ich e-maile i kody pocztowe. Zaczyna od usera 1, zauważa, że zip jest dobry, ale e-mail jest zły. Postanawia to skorygować żądaniem patcha, które jest w pełni uzasadnione i wysyła tylko

PATCH /users/1
{"email": "[email protected]"}

Ponieważ jest to jedyna korekta. Teraz żądanie nie powiedzie się z powodu problemów z siecią i zostanie automatycznie przesłane kilka godzin później. W międzyczasie inny klient (błędnie) zmodyfikował zip użytkownika 1. Następnie wysłanie tego samego żądania po raz drugi nie osiągnie zamierzonego efektu klienta, ponieważ otrzymujemy nieprawidłowy zip. Stąd metoda nie jest idempotentna w sensie RFC.

Jeśli zamiast tego klient używa Wyślij żądanie poprawienia wiadomości e-mail, wysyłając na serwer wszystkie właściwości użytkownika 1 wraz z e-mailem, jego zamierzony efekt zostanie osiągnięty, nawet jeśli żądanie musi zostać ponownie wysłane później, a użytkownik 1 został zmodyfikowany w międzyczasie - - - ponieważ drugie żądanie PUT nadpisze wszystkie zmiany od pierwszego żądania.

 3
Author: Rolvernew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-15 12:51:27

Moim skromnym zdaniem idempotencja oznacza:

  • Umieścić:

Wysyłam definicję zasobu, więc-Wynikowy stan zasobu jest dokładnie taki, jak zdefiniowano przez PUT params. Za każdym razem aktualizuję zasób z tym samym PUT params - stan wynikowy jest dokładnie taki sam.

  • łatka:

Wysłałem tylko część definicji zasobu, więc może się zdarzyć, że inni użytkownicy aktualizują inne parametry tego zasobu w międzyczasie. W konsekwencji - kolejne łaty o tych samych parametrach i ich wartościach mogą powodować inny stan zasobów. Na przykład:

Załóżmy, że obiekt zdefiniowany jest następująco:

Samochód: - Kolor: Czarny, - Typ: sedan, - miejsc: 5

I patch it with:

{color: 'red'}

Wynikowym obiektem jest:

Samochód: - kolor: czerwony, - Typ: sedan, - miejsc: 5

Potem inni użytkownicy łatają ten samochód:

{Typ: "hatchback"}

Tak więc wynik obiekt to:

Samochód: - kolor: czerwony, - Typ: hatchback, - miejsc: 5

Teraz, jeśli poprawię ten obiekt ponownie za pomocą:

{color: 'red'}

Wynikowym obiektem jest:

Samochód: - kolor: czerwony, - Typ: hatchback, - miejsc: 5

Czym się różni od tego, co mam wcześniej!

Dlatego PATCH nie jest idempotentny, podczas gdy PUT jest idempotentny.

 3
Author: Zbigniew Szczęsny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-19 23:25:02

Aby zakończyć dyskusję na temat idempotencji, powinienem zauważyć, że w pozostałym kontekście można zdefiniować idempotencję na dwa sposoby. Najpierw sformalizujmy kilka rzeczy: {]}

A zasób jest funkcją, której codomain jest klasą łańcuchów. Innymi słowy, zasób jest podzbiorem String × Any, gdzie wszystkie klucze są unikalne. Nazwijmy klasę zasobów Res.

Operacja REST na zasobach, jest funkcją f(x: Res, y: Res): Res. Dwa przykłady operacji odpoczynku są:

  • PUT(x: Res, y: Res): Res = x, oraz
  • PATCH(x: Res, y: Res): Res, który działa jak PATCH({a: 2}, {a: 1, b: 3}) == {a: 2, b: 3}.

(ta definicja jest specjalnie zaprojektowana, aby dyskutować o PUT i POST, i np. nie ma większego sensu na GET i POST, ponieważ nie dba o wytrwałość).

Teraz, ustalając x: Res (informatycznie mówiąc, używając curryingu), PUT(x: Res) i PATCH(x: Res) są jednowymiarowymi funkcjami typu Res → Res.

  1. Funkcję g: Res → Res nazywa się globalnie idempotentną, gdy g ○ g == g, tj. dla każdego y: Res, g(g(y)) = g(y).

  2. Niech x: Res zasób i k = x.keys. Funkcję g = f(x) nazywa się idempotentną , gdy dla każdego y: Res mamy g(g(y))| == g(y)|. Zasadniczo oznacza to, że wynik powinien być taki sam, jeśli spojrzymy na zastosowane klucze.

Więc PATCH(x) nie jest globalnie idempotentna, ale pozostaje idempotentna. A lewa idempotencja jest rzeczą, która się tutaj liczy: jeśli łatamy kilka kluczy zasobu, chcemy, aby te klucze były takie same, jeśli łatamy znowu, a my nie dbamy o resztę zasobów.

A kiedy RFC mówi o tym, że PATCH nie jest idempotentny, mówi o globalnej idempotencji. Dobrze, że nie jest globalnie idempotentna, w przeciwnym razie byłaby to zepsuta operacja.


Teraz, Jason Hoetger ' s answer próbuje udowodnić, że PATCH nie jest nawet pozostawiony idempotentny, ale łamie zbyt wiele rzeczy, aby to zrobić: {]}

  • Po pierwsze, PATCH jest używany na zestawie, chociaż PATCH jest zdefiniowany do pracy na mapach / słownikach / obiektach klucz-wartość.
  • jeśli ktoś naprawdę chce zastosować PATCH do zestawów, to istnieje naturalne tłumaczenie, które powinno być użyte: t: Set<T> → Map<T, Boolean>, zdefiniowane przez x in A iff t(A)(x) == True. Używając tej definicji, łatanie pozostaje idempotentne.
  • W przykładzie tłumaczenie to nie zostało użyte, zamiast tego PATCH działa jak POST. Po pierwsze, dlaczego dla obiektu generowany jest identyfikator? A kiedy jest generowany? Jeśli obiekt jest najpierw porównywany z elementy zestawu, a jeĹ "li nie zostanie znaleziony pasujÄ ... cy obiekt, wtedy zostanie wygenerowany ID, wtedy ponownie program powinien dziaĹ' aÄ ‡ inaczej ({id: 1, email: "[email protected]"} musi pasowaÄ ‡ do {email: "[email protected]"}, w przeciwnym razie program jest zawsze zepsuty i łatka nie moĹźe byÄ ‡ w stanie Ĺ ' atwić). Jeśli identyfikator zostanie wygenerowany przed sprawdzeniem zestawu, program ponownie zostanie uszkodzony.

Można zrobić przykłady, że PUT jest nie-idempotentny z łamaniem połowy rzeczy, które są złamane w tym przykładzie:

  • przykład z wygenerowane dodatkowe funkcje to wersjonowanie. Można prowadzić rejestr liczby zmian na jednym obiekcie. W tym przypadku PUT nie jest idempotentny: PUT /user/12 {email: "[email protected]"} daje {email: "...", version: 1} pierwszy i {email: "...", version: 2} drugi raz.
  • mieszając się z IDs, można generować nowe ID za każdym razem, gdy obiekt jest aktualizowany, co skutkuje nie-idempotentnym PUT.
Wszystkie powyższe przykłady są naturalnymi przykładami, z którymi można się spotkać.

Moja ostatnia uwaga jest taka, że Plaster nie powinien być globalnie idempotent, W przeciwnym razie nie da pożądanego efektu. Chcesz zmienić adres e-mail użytkownika bez dotykania reszty informacji i nie chcesz nadpisywać zmian innej strony uzyskującej dostęp do tego samego zasobu.

 1
Author: Mohammad-Ali A'RÂBI,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-31 13:19:19

Wszyscy inni odpowiedzieli na łatkę PUT vs. Chciałem tylko odpowiedzieć, jaką część tytułu pytania oryginalnego zadaje:"... w REST API realne scenariusze życia". W realnym świecie zdarzyło mi się to z aplikacją internetową, która miała Serwer RESTful i relacyjną bazę danych z "szeroką" tabelą klientów (około 40 kolumn). Omyłkowo użyłem PUT, ale założyłem, że jest to jak polecenie aktualizacji SQL i nie wypełniłem wszystkich kolumn. Problemy: 1) niektóre kolumny były opcjonalne (tak puste była poprawna odpowiedź), 2) wiele kolumn rzadko się zmieniało, 3) niektóre kolumny użytkownik nie mógł zmienić, takie jak znacznik czasu ostatniej daty zakupu, 4) jedna kolumna była wolna forma tekst "Komentarze" kolumna, że użytkownicy pilnie wypełnione pół-strona Obsługi Klienta komentarze, takie jak nazwisko małżonków zapytać o lub zwykłe zamówienie, 5) pracowałem na aplikacji internetowej w czasie i nie było martwić się o rozmiar pakietu.

Wadą PUT jest to, że zmusza do wysłania dużej paczki z info (wszystkie kolumny w tym cała kolumna komentarzy, mimo że tylko kilka rzeczy się zmieniło) i problem wielu użytkowników 2 + edytujących tego samego klienta jednocześnie (więc wygrywa ostatni, który naciśnie Update). Wadą patcha jest to, że musisz śledzić po stronie widoku / ekranu to, co się zmieniło i mieć trochę inteligencji, aby wysłać tylko te części, które się zmieniły. Problem z wieloma użytkownikami patcha ogranicza się do edycji tych samych kolumn tego samego klienta.

 1
Author: Eric Wood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-13 21:39:12

A very nice explanation is here -

Https://blog.segunolalive.com/posts/restful-api-design-%E2%80%94-put-vs-patch/#:~:text=RFC%205789,not%20required%20to%20be%20idempotent.

Zwykły Ładunek - // Dom na działce 1 { adres: "działka 1", właściciel: "segun", "type": "duplex", kolor: 'zielony', pokoje: '5', kuchnie: "1", okna: 20 } PUT for Updated - // Wyślij Zapytanie o aktualizację okien domu na działce 1 { adres: "działka 1", właściciel: "segun", Typ: "duplex", kolor: 'zielony', pokoje: '5', kuchnie: "1", okna: 21 } Uwaga: w powyższym ładunku próbujemy zaktualizować system windows z 20 do 21.

Zobacz ścieżkę- // Patch Prośba o aktualizację Windowsa w domu { okna: 21 }

Ponieważ PATCH nie jest idempotentny, nieudane żądania nie są automatycznie ponownie próbowane w sieci. Ponadto, jeśli prośba o łatkę zostanie wysłana do nieistniejącego adresu url, np. próbując wymienić drzwi wejściowe nieistniejącego budynku, powinno to po prostu zawieść bez tworzenia nowego zasobu w przeciwieństwie do PUT, który tworzyłby Nowy przy użyciu ładunku. Jak o tym pomyślę, to będzie dziwne mieć samotne drzwi pod adresem domu.

 0
Author: harit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-13 07:18:37

Jedną dodatkową informacją, którą dodam jest to, że żądanie poprawki zużywa mniej przepustowości w porównaniu do żądania PUT, ponieważ tylko część danych jest wysyłana, a nie cały obiekt. Więc wystarczy użyć żądania poprawki do aktualizacji określonych rekordów, takich jak (1-3 rekordy), podczas gdy umieścić żądanie aktualizacji większej ilości danych. To jest to, nie myśl za dużo lub martwić się o to zbyt dużo.

 -2
Author: Benjamin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-17 17:27:30