Piszę usługę RESTful dla systemu zarządzania klientami i próbuję znaleźć najlepszą praktykę częściowej aktualizacji rekordów. Na przykład chcę, aby osoba dzwoniąca mogła odczytać pełny rekord za pomocą żądania GET. Ale do jego aktualizacji dozwolone są tylko niektóre operacje na rekordzie, takie jak zmiana stanu z WŁĄCZONEGO na WYŁĄCZONY. (Mam bardziej złożone scenariusze niż to)
Nie chcę, aby osoba dzwoniąca przesłała cały rekord ze zaktualizowanym polem ze względów bezpieczeństwa (wydaje się to również przesadą).
Czy istnieje zalecany sposób konstruowania identyfikatorów URI? Czytając książki REST, wywołania w stylu RPC wydają się być niezadowolone.
Jeśli poniższe wywołanie zwróci pełny rekord klienta dla klienta o identyfikatorze 123
GET /customer/123
<customer>
{lots of attributes}
<status>ENABLED</status>
{even more attributes}
</customer>
jak powinienem zaktualizować status?
POST /customer/123/status
<status>DISABLED</status>
POST /customer/123/changeStatus
DISABLED
...
Aktualizacja : Aby rozszerzyć pytanie. W jaki sposób włącza się „wywołania logiki biznesowej” do interfejsu API REST? Czy istnieje uzgodniony sposób na zrobienie tego? Nie wszystkie metody są z natury CRUD. Niektóre są bardziej złożone, na przykład „ sendEmailToCustomer (123) ”, „ mergeCustomers (123, 456) ”, „ countCustomers () ”
POST /customer/123?cmd=sendEmail
POST /cmd/sendEmail?customerId=123
GET /customer/count
POST
od samego Roy'a Fieldinga: roy.gbiv.com/untangled/2009/it-is-okay-to-use-post gdzie podstawową ideą jest: jeśli nie ma to metoda (taka jakGET
lubPUT
) idealnie dopasowana do twojego zastosowania operacyjnegoPOST
.Odpowiedzi:
Zasadniczo masz dwie opcje:
Użyj
PATCH
(ale pamiętaj, że musisz zdefiniować własny typ nośnika, który określa, co dokładnie się stanie)Użyj
POST
do zasobu podrzędnego i zwróć 303 Zobacz Inne z nagłówkiem Lokalizacja wskazującym na główny zasób. Zamiarem 303 jest powiedzenie klientowi: „Przeprowadziłem test POST i efektem było zaktualizowanie innego zasobu. Zobacz nagłówek lokalizacji, dla którego to było”. POST / 303 jest przeznaczony do iteracyjnych dodatków do zasobów w celu zbudowania stanu niektórych głównych zasobów i doskonale nadaje się do częściowych aktualizacji.źródło
Powinieneś używać POST do częściowych aktualizacji.
Aby zaktualizować pola dla klienta 123, wykonaj test POST na / customer / 123.
Jeśli chcesz zaktualizować tylko status, możesz również PUT ustawić na / customer / 123 / status.
Zasadniczo żądania GET nie powinny mieć żadnych skutków ubocznych, a PUT służy do zapisywania / zastępowania całego zasobu.
Wynika to bezpośrednio z HTTP, jak widać tutaj: http://en.wikipedia.org/wiki/HTTP_PUT#Request_methods
źródło
/customer/123
powinien stworzyć oczywistą rzecz, która logicznie jest pod klientem 123. Może zamówienie?/customer/123/status
Wydaje się, że PUT ma lepszy sens, zakładając, że test POST/customers
domyślnie utworzyłstatus
(i zakładając, że jest to legalny REST).POST
nie muszą być idempotentne. I jak wspomniano,PUT
musi zastąpić cały zasób.Powinieneś używać PATCH do częściowych aktualizacji - albo używając dokumentów łatki json (patrz http://tools.ietf.org/html/draft-ietf-appsawg-json-patch-08 lub http://www.mnot.net/ blog / 2012/09/05 / patch ) lub framework łatek XML (patrz http://tools.ietf.org/html/rfc5261 ). Moim zdaniem json-patch najlepiej pasuje do twoich danych biznesowych.
PATCH z dokumentami łatek JSON / XML ma bardzo wyraźną semantykę do przodu dla częściowych aktualizacji. Jeśli zaczniesz używać POST ze zmodyfikowanymi kopiami oryginalnego dokumentu, w przypadku częściowych aktualizacji wkrótce napotkasz problemy, w których chcesz, aby brakujące wartości (lub raczej wartości zerowe) reprezentowały albo „zignoruj tę właściwość”, albo „ustaw tę właściwość na pusta wartość ”- a to prowadzi do króliczej dziury zhakowanych rozwiązań, które ostatecznie doprowadzą do stworzenia własnego formatu łaty.
Bardziej szczegółową odpowiedź znajdziesz tutaj: http://soabits.blogspot.dk/2013/01/http-put-patch-or-post-partial-updates.html .
źródło
Mam podobny problem. Wydaje się, że PUT dla zasobu podrzędnego działa, gdy chcesz zaktualizować tylko jedno pole. Czasami jednak chcesz zaktualizować kilka rzeczy: Pomyśl o formularzu internetowym reprezentującym zasób z opcją zmiany niektórych wpisów. Przesłanie formularza przez użytkownika nie powinno skutkować wieloma PUT.
Oto dwa rozwiązania, o których mogę myśleć:
wykonaj PUT z całym zasobem. Po stronie serwera zdefiniuj semantykę, że PUT z całym zasobem ignoruje wszystkie wartości, które nie uległy zmianie.
zrobić PUT z częściowym zasobem. Po stronie serwera zdefiniuj semantykę tego połączenia.
2 jest tylko optymalizacją przepustowości wynoszącą 1. Czasami 1 jest jedyną opcją, jeśli zasób definiuje, że niektóre pola są polami wymaganymi (pomyślmy o buforach proto).
Problemem obu tych podejść jest oczyszczenie pola. Będziesz musiał zdefiniować specjalną wartość zerową (szczególnie dla buforów proto, ponieważ wartości zerowe nie są zdefiniowane dla buforów proto), co spowoduje wyczyszczenie pola.
Komentarze?
źródło
Myślę, że do modyfikowania statusu podejście RESTful polega na użyciu logicznego pod-zasobu, który opisuje status zasobów. To IMO jest bardzo przydatne i czyste, gdy masz ograniczony zestaw statusów. Sprawia, że interfejs API jest bardziej wyrazisty bez wymuszania istniejących operacji na zasobach klienta.
Przykład:
Usługa POST powinna zwrócić nowo utworzonego klienta o identyfikatorze:
GET dla utworzonego zasobu użyłby lokalizacji zasobu:
GET / klient / 123 / nieaktywny powinien zwrócić 404
W przypadku operacji PUT bez podania obiektu Json po prostu zaktualizuje status
Podanie podmiotu pozwoli ci zaktualizować zawartość klienta i jednocześnie zaktualizować status.
Tworzysz koncepcyjny pod-zasób dla zasobu klienta. Jest to również zgodne z definicją zasobu Roya Fieldinga: „... Zasób jest koncepcyjnym odwzorowaniem na zbiór bytów, a nie byt, który odpowiada odwzorowaniu w dowolnym momencie w czasie ...” W tym przypadku mapowanie koncepcyjne jest aktywne-klient do klienta ze statusem = AKTYWNY.
Przeczytaj operację:
Jeśli wykonujesz te połączenia jedna po drugiej, druga z nich musi zwrócić status 404, pomyślne wyjście może nie obejmować statusu, ponieważ jest niejawny. Oczywiście nadal możesz użyć GET / customer / 123? Status = AKTYWNY | NIEAKTYWNY do bezpośredniego zapytania do zasobu klienta.
Operacja DELETE jest interesująca, ponieważ semantyka może być myląca. Możesz jednak nie opublikować tej operacji dla tego zasobu koncepcyjnego lub użyć go zgodnie z logiką biznesową.
Ten może doprowadzić klienta do statusu USUŃ / WYŁĄCZONY lub do statusu przeciwnego (AKTYWNY / NIEAKTYWNY).
źródło
Rzeczy, które należy dodać do rozszerzonego pytania. Myślę, że często możesz doskonale zaprojektować bardziej skomplikowane działania biznesowe. Ale musisz podać sposób myślenia w stylu metody / procedury i więcej myśleć w zasobach i czasownikach.
wysyłki pocztowe
Wdrożenie tego zasobu + POST wyśle następnie pocztę. jeśli to konieczne, możesz zaoferować coś takiego jak / customer / 123 / outbox, a następnie zaoferować łącza do zasobów do / customer / mails / {mailId}.
liczba klientów
Możesz sobie z tym poradzić jak zasobem wyszukiwania (w tym metadanymi wyszukiwania z stronicowaniem i informacjami o liczbie znalezionych, co daje liczbę klientów).
źródło
Użyj PUT do aktualizacji niekompletnych / częściowych zasobów.
Możesz zaakceptować jObject jako parametr i przeanalizować jego wartość, aby zaktualizować zasób.
Poniżej znajduje się funkcja, której można użyć jako odniesienia:
źródło
Jeśli chodzi o twoją aktualizację.
Uważam, że koncepcja CRUD spowodowała pewne zamieszanie w zakresie projektowania API. CRUD to ogólna koncepcja niskiego poziomu dla podstawowych operacji wykonywanych na danych, a czasowniki HTTP to tylko metody żądania ( utworzone 21 lat temu ), które mogą, ale nie muszą, być mapowane na operację CRUD. W rzeczywistości spróbuj znaleźć obecność akronimu CRUD w specyfikacji HTTP 1.0 / 1.1.
Bardzo dobrze wyjaśniony przewodnik, który stosuje konwencję pragmatyczną, można znaleźć w dokumentacji interfejsu API platformy chmurowej Google . Opisuje koncepcje tworzenia interfejsu API opartego na zasobach, który kładzie duży nacisk na zasoby w porównaniu z operacjami, a także opisywane przypadki użycia. Chociaż jest to konwencyjny projekt ich produktu, myślę, że ma to sens.
Podstawową koncepcją tutaj (i taką, która powoduje wiele nieporozumień) jest mapowanie między „metodami” i czasownikami HTTP. Jedną rzeczą jest zdefiniowanie, jakie „operacje” (metody) będzie wykonywał Twój interfejs API w odniesieniu do rodzajów zasobów (na przykład uzyskać listę klientów lub wysłać wiadomość e-mail), a drugą są czasowniki HTTP. Musi istnieć definicja obu metod i czasowników, których zamierzasz użyć, oraz mapowanie między nimi .
Mówi też, że gdy operacja nie mapuje dokładnie ze standardową metodą (
List
,Get
,Create
,Update
,Delete
w tym przypadku), jeden może korzystać z metody „niestandardowe”, jakBatchGet
, który pobiera kilka obiektów na podstawie kilku wejścia ID obiektu, lubSendEmail
.źródło
RFC 7396 : Poprawka scalająca JSON (opublikowana cztery lata po opublikowaniu pytania) opisuje najlepsze praktyki dla PATCH pod względem formatu i reguł przetwarzania.
W skrócie, przesyłasz PATCH HTTP do zasobu docelowego z typem aplikacji / merge-patch + json MIME i treści reprezentujących tylko te części, które chcesz zmienić / dodać / usunąć, a następnie postępować zgodnie z poniższymi regułami przetwarzania.
Zasady :
Przykładowe przypadki testowe, które ilustrują powyższe reguły (jak widać w dodatku do tego dokumentu RFC):
źródło
Sprawdź http://www.odata.org/
Definiuje metodę MERGE, więc w twoim przypadku byłoby to mniej więcej tak:
Tylko
status
właściwość jest aktualizowana, a pozostałe wartości są zachowywane.źródło
MERGE
prawidłowy czasownik HTTP?MERGE was used to do PATCH before PATCH existed. Now that we have PATCH, we no longer need MERGE.
Zobacz docs.oasis-open.org/odata/new-in-odata/v4.0/cn01/…To nie ma znaczenia Jeśli chodzi o REST, nie możesz wykonać GET, ponieważ nie jest buforowalny, ale nie ma znaczenia, czy używasz POST, PATCH, PUT itp. I nie ma znaczenia, jak wygląda adres URL. Jeśli wykonujesz REST, ważne jest to, że gdy otrzymasz reprezentację twojego zasobu z serwera, reprezentacja ta może dać opcje przejścia do stanu klienta.
Jeśli Twoja odpowiedź GET zawierała zmiany stanu, klient musi tylko wiedzieć, jak je odczytać, a serwer może je zmienić w razie potrzeby. Tutaj aktualizacja jest wykonywana przy użyciu POST, ale jeśli zmieniono ją na PATCH lub jeśli zmieni się adres URL, klient nadal wie, jak dokonać aktualizacji:
Możesz posunąć się do wykazania wymaganych / opcjonalnych parametrów, które klient może Ci zwrócić. To zależy od zastosowania.
Jeśli chodzi o operacje biznesowe, może to być inny zasób powiązany z zasobem klienta. Jeśli chcesz wysłać wiadomość e-mail do klienta, być może ta usługa jest jego własnym zasobem, do którego możesz wysłać POST, więc możesz dołączyć następującą operację do zasobu klienta:
Oto kilka dobrych filmów i przykład architektury REST prezentera. Stormpath używa tylko GET / POST / DELETE, co jest w porządku, ponieważ REST nie ma nic wspólnego z używanymi operacjami ani z tym, jak powinny wyglądać adresy URL (oprócz GET powinny być buforowalne):
https://www.youtube.com/watch?v=pspy1H6A3FM ,
https://www.youtube.com/watch?v=5WXYw4J4QOU ,
http://docs.stormpath.com/rest/quickstart/
źródło