Załóżmy, że interfejs API REST w odpowiedzi na GET
żądanie HTTP zwraca dodatkowe dane w podobiektie owner
:
{
id: 'xyz',
... some other data ...
owner: {
name: 'Jo Bloggs',
role: 'Programmer'
}
}
Oczywiście nie chcemy, aby ktokolwiek mógł się PUT
cofnąć
{
id: 'xyz',
... some other data ...
owner: {
name: 'Jo Bloggs',
role: 'CEO'
}
}
i odnieść sukces. Rzeczywiście, w tym przypadku prawdopodobnie nie zamierzamy nawet wdrożyć sposobu, aby nawet potencjalnie odnieść sukces.
Ale to pytanie nie dotyczy tylko podobiektów: co ogólnie należy zrobić z danymi, których nie można modyfikować w żądaniu PUT?
Czy powinien być wymagany brak w żądaniu PUT?
Czy należy to po cichu wyrzucić?
Czy należy to sprawdzić, a jeśli różni się od starej wartości tego atrybutu, zwróć kod błędu HTTP w odpowiedzi?
A może powinniśmy używać łatek JFC RFC 6902 zamiast wysyłać cały JSON?
rest
http
http-request
Robin Green
źródło
źródło
Odpowiedzi:
Nie ma żadnej reguły, ani w specyfikacji W3C, ani w nieoficjalnych regułach REST, która mówi, że
PUT
należy użyć tego samego schematu / modelu, który odpowiadaGET
.To miłe, jeśli są podobne , ale nie jest niczym niezwykłym,
PUT
że robią rzeczy nieco inaczej. Na przykład widziałem wiele interfejsów API, które zawierają pewien rodzaj identyfikatora w treści zwróconej przezGET
dla wygody. Ale zPUT
, ten identyfikator jest określany wyłącznie przez URI i nie ma znaczenia w treści. Każdy identyfikator znaleziony w ciele zostanie po cichu zignorowany.REST i ogólnie sieć są silnie powiązane z zasadą solidności : „Bądź konserwatywny w tym, co robisz [wysyłasz], bądź liberalny w tym, co akceptujesz”. Jeśli zgadzasz się z tym filozoficznie, rozwiązanie jest oczywiste: zignoruj wszelkie nieprawidłowe dane w
PUT
żądaniach. Dotyczy to zarówno niezmiennych danych, jak w twoim przykładzie, jak i faktycznych bzdur, np. Nieznanych pól.PATCH
jest potencjalnie kolejną opcją, ale nie należy jej wdrażać,PATCH
chyba że faktycznie będzie się wspierać częściowe aktualizacje.PATCH
oznacza jedynie aktualizację określonych atrybutów, które zawarłem w treści ; to jednak nie znaczy wymienić całą jednostkę jednak wykluczyć pewne konkretne pola . To, o czym tak naprawdę mówisz, nie jest tak naprawdę częściową aktualizacją, jest to pełna aktualizacja, idempotentna i wszystko, tylko część zasobów jest tylko do odczytu.Dobrą rzeczą do zrobienia, jeśli wybierzesz tę opcję, byłoby odesłanie 200 (OK) z faktycznie zaktualizowanym bytem w odpowiedzi, aby klienci mogli wyraźnie zobaczyć, że pola tylko do odczytu nie zostały zaktualizowane.
Z pewnością są ludzie, którzy myślą w drugą stronę - że próba aktualizacji części zasobu tylko do odczytu powinna być błędem. Jest to uzasadnione, przede wszystkim dlatego, że zdecydowanie zwróciłbyś błąd, gdyby cały zasób był tylko do odczytu, a użytkownik próbował go zaktualizować. Zdecydowanie jest to sprzeczne z zasadą niezawodności, ale możesz uznać, że jest to bardziej „samo dokumentujące” dla użytkowników Twojego API.
Istnieją w tym celu dwie konwencje, które odpowiadają twoim oryginalnym pomysłom, ale rozwinę je. Pierwszym z nich jest zabronienie pojawiania się w treści pól tylko do odczytu i zwrócenie HTTP 400 (Błędne żądanie), jeśli tak się stanie. Interfejsy API tego rodzaju powinny również zwracać HTTP 400, jeśli istnieją inne nierozpoznane / nieużywalne pola. Drugim jest wymaganie, aby pola tylko do odczytu były identyczne z bieżącą zawartością, i zwrócenie wartości 409 (konflikt), jeśli wartości nie są zgodne.
Naprawdę nie lubię sprawdzania równości z 409, ponieważ niezmiennie wymaga to od klienta wykonania
GET
w celu odzyskania bieżących danych przed wykonaniemPUT
. To po prostu nie jest miłe i prawdopodobnie doprowadzi gdzieś do słabej wydajności. Ja też naprawdę nie lubią 403 (zabronione) za to, ponieważ oznacza to, że cały zasób jest zabezpieczony, a nie tylko jego część. Więc moim zdaniem, jeśli absolutnie musisz zweryfikować zamiast postępować zgodnie z zasadą niezawodności, zweryfikuj wszystkie swoje prośby i zwróć 400 za wszystkie, które mają dodatkowe lub niepisywalne pola.Upewnij się, że twój 400/409 / cokolwiek zawiera informacje o tym, jaki jest konkretny problem i jak go naprawić.
Oba te podejścia są poprawne, ale wolę pierwsze z nich zgodnie z zasadą solidności. Jeśli kiedykolwiek doświadczyłeś pracy z dużym interfejsem API REST, docenisz wartość kompatybilności wstecznej. Jeśli kiedykolwiek zdecydujesz się usunąć istniejące pole lub ustawić je jako tylko do odczytu, jest to wstecznie kompatybilna zmiana, jeśli serwer po prostu zignoruje te pola, a starzy klienci będą nadal działać. Jeśli jednak dokonasz ścisłego sprawdzania poprawności treści, nie będzie ona już zgodna z poprzednimi wersjami, a starzy klienci przestaną działać. Ten pierwszy oznacza ogólnie mniej pracy zarówno dla osoby obsługującej interfejs API, jak i jego klientów.
źródło
Idem potencji
Po RFC PUT musiałby dostarczyć pełny obiekt do zasobu. Głównym tego powodem jest to, że PUT powinien być idempotentny. Oznacza to, że żądanie, które się powtarza, powinno dać taki sam wynik na serwerze.
Jeśli zezwolisz na częściowe aktualizacje, nie będzie to już możliwe. Jeśli masz dwóch klientów. Klient A i B mogą ewoluować następujący scenariusz:
Klient A pobiera obraz z obrazów zasobów. Zawiera opis obrazu, który jest nadal aktualny. Klient B umieszcza nowy obraz i odpowiednio aktualizuje opis. Obraz się zmienił. Klient A widzi, że nie musi zmieniać opisu, ponieważ jest tak, jak chce i umieszcza tylko obraz.
Doprowadzi to do niespójności, do obrazu dołączone są niewłaściwe metadane!
Jeszcze bardziej irytujące jest to, że każdy pośrednik może powtórzyć żądanie. W przypadku, gdy zdecyduje jakoś PUT nie powiodło się.
Znaczenie PUT nie może zostać zmienione (chociaż można go niewłaściwie użyć).
Inne opcje
Na szczęście istnieje inna opcja, to PATCH. PATCH to metoda, która pozwala częściowo zaktualizować strukturę. Możesz po prostu wysłać częściową strukturę. W przypadku prostych aplikacji jest to w porządku. Nie ma gwarancji, że ta metoda będzie silna. Klient powinien wysłać zapytanie w następującej formie:
A serwer może odpowiedzieć 204 (bez zawartości), aby oznaczyć sukces. W przypadku błędu nie można zaktualizować części struktury. Metoda PATCH jest atomowa.
Wadą tej metody jest to, że nie wszystkie przeglądarki obsługują to, ale jest to najbardziej naturalna opcja w usłudze REST.
Przykładowe żądanie łatki: http://tools.ietf.org/html/rfc5789#section-2.1
Łatanie Jsona
Opcja json wydaje się być dość kompleksowa i interesująca. Wdrożenie może być jednak trudne dla stron trzecich. Musisz zdecydować, czy Twoja baza użytkowników może to obsłużyć.
Jest to również nieco skomplikowane, ponieważ musisz zbudować mały interpreter, który konwertuje polecenia na częściową strukturę, której będziesz używać do aktualizacji swojego modelu. Ten interpreter powinien również sprawdzić, czy podane polecenia mają sens. Niektóre polecenia się znoszą. (napisz fielda, usuń fielda). Myślę, że chcesz zgłosić to klientowi, aby ograniczyć czas debugowania po jego stronie.
Ale jeśli masz czas, jest to naprawdę eleganckie rozwiązanie. Nadal powinieneś sprawdzić poprawność pól. Możesz połączyć to z metodą PATCH, aby pozostać w modelu REST. Ale myślę, że POST byłby do przyjęcia tutaj.
Idzie źle
Jeśli zdecydujesz się na opcję PUT, co jest nieco ryzykowne. W takim razie nie powinieneś przynajmniej odrzucać błędu. Użytkownik ma określone oczekiwania (dane zostaną zaktualizowane), a jeśli je przerwiesz, nie zapewnisz programistom dobrego czasu.
Możesz wybrać wycofanie: 409 Konflikt lub 403 Zakazane. To zależy, jak spojrzysz na proces aktualizacji. Jeśli zobaczysz to jako zestaw reguł (systemowo-centrycznych), konflikt będzie ładniejszy. Coś w rodzaju tych pól nie można aktualizować. (W sprzeczności z zasadami). Jeśli widzisz to jako problem z autoryzacją (zorientowany na użytkownika), powinieneś powrócić zabronione. Z: nie masz uprawnień do zmiany tych pól.
Nadal powinieneś zmusić użytkowników do wysłania wszystkich pól, które można modyfikować.
Rozsądną opcją do tego jest ustawienie podrzędnego zasobu, który oferuje tylko modyfikowalne dane.
Osobista opinia
Osobiście wybrałbym (jeśli nie musisz pracować z przeglądarkami) prosty model PATCH, a następnie rozszerzyłem go o procesor łatek JSON. Można to zrobić, rozróżniając typy mime: Typ mime łatki json:
application / json-patch
I json: application / json-patch
ułatwia wdrożenie go w dwóch fazach.
źródło