używa PUT z wpływami bocznymi akceptowalnymi (REST)

9

Chcę utworzyć historię cofania za każdym razem, gdy użytkownik aktualizuje formularz. Ponieważ jest to aktualizacja, chcę użyć żądania PUT. Jednak przeczytałem, że PUT nie musi mieć żadnych skutków ubocznych .

Czy dopuszczalne jest użycie PUT tutaj? Czy są lepsze alternatywy?

PUT /person/F02E395A235

{
   time: 1234567,
   fields: {
      name: 'John',
      age: '41'
   }
}

Na serwerze

doPut('person/:personId',
   // create a new person snapshot
)

Edytować:

Historia będzie widoczna dla użytkownika, wielokrotne wywołanie spowodowałoby powstanie wielu wersji.

Rozwiązaniem było sprawdzenie, czy wersja była unikalna przed jej utworzeniem.

roo2
źródło

Odpowiedzi:

11

Ludzie opracowujący HTTP / 2 byli o wiele bardziej otwarci na temat tego, co powinien zrobić HTTP, zachowując stare znaczenie. Zobaczmy, co ma do powiedzenia wersja robocza HTTP / 2 na temat idempotence:

4.2.2 Idempotentne metody

Metodę żądania uważa się za „idempotentną”, jeśli zamierzony efekt na serwerze wielu identycznych żądań z tą metodą jest taki sam jak efekt dla pojedynczego takiego żądania. W przypadku metod żądań zdefiniowanych w tej specyfikacji PUT, DELETE i bezpieczne żądanie metody są idempotentne.

Podobnie jak definicja sejfu, właściwość idempotent ma zastosowanie tylko do tego, czego zażądał użytkownik; serwer może rejestrować każde żądanie osobno, zachować historię kontroli wersji lub wdrożyć inne nie idempotentne skutki uboczne dla każdego żądania idempotentnego .

Zamierzony efekt na serwerze dla każdego takiego żądania PUT jest zaktualizować zasób zidentyfikowany przez tego URI . Tak właśnie dzieje się w twoim przypadku.

To, że zdecydujesz się na wersję zasobów, tak naprawdę nie ma znaczenia. Jeśli nie chcesz tworzyć nowej wersji, gdy nic się nie zmieniło, musisz porównać ładunek w żądaniu PUT z najnowszą (lub w inny sposób zidentyfikowaną) wersją zasobu i gdy żadna z właściwości nie uległa zmianie możesz nie tworzyć nowej wersji .


Twoja edycja:

Historia będzie widoczna dla użytkownika, wielokrotne wywołanie spowodowałoby wiele konwersji

Jeśli chodzi o zasób, nie jest to efektem ubocznym . Zasób pod tym identyfikatorem URI nie zmienia się (te same właściwości otrzymują PUT). Historia to tylko metadane, ponieważ najprawdopodobniej jest ona wymagana przez inny identyfikator URI lub z różnymi nagłówkami żądania.

CodeCaster
źródło
Wyjątkiem jest to, że odpowiadasz na mój konkretny problem: „czy PUT może utworzyć historię widoczną dla użytkownika”, I daj mi rozwiązanie, dzięki
roo2
Na jaki problem to rozwiązanie? Czy timewłaściwość zostanie zaktualizowana? Myślę, że to także metadane, nawet jeśli znajdują się w zasobie.
CodeCaster
1
Problem polegał na tym, że jeśli wysłano wiele PUT, użytkownik otrzyma długą historię cofania z nadmiarowymi informacjami. sprawdzenie wyjątkowości rozwiązuje ten problem
roo2
12

HTTP rozróżnia dwie właściwości:

  • Idempotencja
  • Bezpieczeństwo

Idempotencja jest definiowana przez specyfikację w następujący sposób:

Metody mogą również mieć właściwość „ idempotence ”, ponieważ (oprócz błędów związanych z błędem lub wygasaniem) skutki uboczne N> 0 identycznych żądań są takie same jak w przypadku pojedynczego żądania. Metody GET, HEAD, PUTi DELETEdzielić tę właściwość. Ponadto metody OPTIONSi TRACE NIE POWINNY mieć skutków ubocznych, a zatem są z natury idempotentne.

I bezpieczeństwo:

W szczególności ustanowiono konwencję, że metody GETi NIE POWINNY mieć znaczenia innego niż odzyskanie. Metody te należy uznać za „ bezpieczne ”. Pozwala to aplikacje klienckie do reprezentowania innych metod, takich jak , i , w szczególny sposób, tak, że użytkownik jest świadomy faktu, że być może niebezpieczne działanie jest wymagane.HEADPOSTPUTDELETE

Oczywiście nie można zapewnić, że serwer nie generuje skutków ubocznych w wyniku wykonania GETżądania; w rzeczywistości niektóre zasoby dynamiczne uważają tę funkcję. Ważnym rozróżnieniem jest tutaj to, że użytkownik nie zażądał skutków ubocznych, dlatego nie może być pociągnięty do odpowiedzialności za nie.

Zauważ, że bezpieczeństwo oznacza idempotencję: jeśli metoda nie ma skutków ubocznych, wielokrotne jej wykonanie przyniesie taki sam efekt uboczny, jak jednorazowe wykonanie, a mianowicie brak.

To dzieli metody na trzy kategorie:

  • bezpieczny (a więc również idempotent): GET, HEAD, OPTION,TRACE
  • idempotent ale niekoniecznie bezpieczne: PUT,DELETE
  • ani idempotentny, ani bezpieczny: POST

PUT nie musi mieć żadnych skutków ubocznych.

To jest złe. PUTjest idempotentny, ale nie bezpieczny. Cały punkt o PUTto, aby mieć efekt uboczny, mianowicie aktualizowanie zasobów. Idempotencja oznacza, że ​​wielokrotne aktualizowanie tego samego zasobu o tej samej zawartości powinno mieć taki sam efekt, jak aktualizowanie go tylko raz.

Zwróć uwagę na ostatni akapit w rozdziale dotyczącym bezpieczeństwa [moje podkreślenie]:

Oczywiście nie można zapewnić, że serwer nie generuje skutków ubocznych w wyniku wykonania GETżądania; w rzeczywistości niektóre zasoby dynamiczne uważają tę funkcję. Ważnym rozróżnieniem jest tutaj to, że użytkownik nie zażądał skutków ubocznych, dlatego nie może być pociągnięty do odpowiedzialności za nie .

Chociaż zdanie to mówi o GETbezpieczeństwie, możemy założyć, że autorzy zamierzali również zastosować to samo rozumowanie PUTi idempotencję. IOW: PUTpowinien mieć tylko jeden efekt uboczny widoczny dla użytkownika , a mianowicie aktualizację nazwanego zasobu. To może mieć inne skutki uboczne, ale użytkownik nie może być pociągnięty do odpowiedzialności za nich.

Na przykład fakt, że PUTjest idempotentny, oznacza, że ​​mogę próbować to tak często, jak chcę: specyfikacja gwarantuje , że wielokrotne wykonanie będzie dokładnie takie samo, jak wykonanie go raz. Całkowicie poprawne jest tworzenie zaległości starych wersji jako efekt uboczny tych wielu PUTżądań. Jeśli jednak w wyniku wielu prób baza danych zapełni się zaległymi wersjami starych wersji, to nie jest mój problem, tylko twój.

IOW: możesz mieć tyle efektów ubocznych, ile chcesz, ale

  1. musi wyglądać na użytkownika tak, jakby jego żądania były idempotentne
  2. jesteś odpowiedzialny za te skutki uboczne, a nie użytkownik
Jörg W Mittag
źródło
Tak, idempotencja dotyczy stanu umieszczanego zasobu, a nie jakiegokolwiek innego stanu serwera / usługi, na który wpływa akt PUT.
Marjan Venema,
Głosuj za doskonałym wyjaśnieniem bezpieczeństwa i idempotencji w spoczynku
roo2
Świetne wyjaśnienie. Jednak podajesz kilka takich stwierdzeń: „Cały sens PUT polega na wywołaniu efektu ubocznego, a mianowicie aktualizacji zasobu”. To wydaje się sprzecznością, chyba że masz na myśli coś innego pod pojęciem „efekt uboczny” niż „coś wtórnego lub niezamierzonego, które dzieje się oprócz pierwotnego zamierzonego efektu”.
MarredCheese
@MarredCheese: Używam tego terminu w jego standardowym znaczeniu programistycznym, co w zasadzie oznacza „każdy” wynik, który nie jest wartością zwracaną ”.
Jörg W Mittag
Ach, oczywiście. Dziękuję za wyjaśnienie.
MarredCheese
1

Masz rację, że PUT nie musi mieć żadnych skutków ubocznych , ale dodałbym coś do tego.

PUT nie musi mieć wpływu na zasoby, dla których wykonywana jest ta operacja PUT

Aktualizujesz personzasób, który jest zidentyfikowany jako F02E395A235, więc użycie PUT jest poprawne. Teraz jako reguła biznesowa śledzisz również zmiany, które są niewidoczne dla podmiotu wywołującego (konsumenta usługi REST). To nie doda nowego elementu do personzasobu. Historyczna migawka nie będzie dostępna przy użyciu /person/punktu końcowego. Uważam więc, że PUT powinien być w tym przypadku całkowicie do przyjęcia.

Aziz Shaikh
źródło
1
Brak nakładanych efektów ubocznych na zasób, ale dowolna liczba skutków ubocznych na inne rzeczy (liczniki, rejestrowanie, ślady audytu, ...) są całkowicie akceptowalne.
Marjan Venema,