Aby dołączyć identyfikator zasobu do ładunku lub wyprowadzić z URI

13

Projektując interfejs API, postawiliśmy pytanie, czy ładunek PUT powinien zawierać identyfikator aktualizowanego zasobu.

Oto, co obecnie mamy:

PUT /users/123 Payload: {name: "Adrian"}

Nasz kod trasy wyodrębnia identyfikator z identyfikatora URI i kontynuuje aktualizację.

Pierwsi użytkownicy naszego API pytają, dlaczego nie pozwalamy na identyfikatory w ładunku:

PUT /users/123 Payload: {id: 123, name: "Adrian"}

Nie zezwalamy na to, ponieważ identyfikator jest zduplikowany w ładunku i identyfikatorze URI.

Zastanawiając się nad tym, łączymy zasób z identyfikatorem URI.

Jeśli identyfikator URI nie ma identyfikatora, należy zmienić ładunek:

PUT /no/id/here Payload: {name: "Adrian"} < What user???

Czy są jakieś powody, aby tego nie robić?

Adrian Lynch
źródło

Odpowiedzi:

14

Powinieneś powiązać jednolity identyfikator zasobu z zasobem .

Gdy REST jest implementowany z HTTP, użyj GET, aby pobrać bieżącą wartość zasobu i PUT, aby ustawić nową wartość. GET nie ma ładunku, więc zasób musi być identyfikowany przez URI. PUT jest logicznie wykonywany na tym samym URI, a ładunek powinien wyglądać dokładnie tak, jak chcesz, aby następny GET zwrócił.

Możesz użyć POST do innego identyfikatora URI, ale miałoby to tylko mniej sensu, ponieważ byłoby niepotrzebnie asymetryczne względem GET. Test POST na wspólny identyfikator URI może mieć sens tylko przy tworzeniu nowych zasobów ( POST /users/new, ładunek:, {name: "Adrian"}odpowiedź {id: 345, name: "Adrian"}), ale nie jest to idempotentne i dlatego należy go unikać jeśli dążysz do REST¹. Zamiast tego powinieneś zarezerwować identyfikator za pomocą jednego połączenia, a następnie użyć PUT, aby ustawić nowy identyfikator; jest to odporne na uszkodzenia, ponieważ jeśli pierwsze żądanie zakończy się niepowodzeniem, rezerwacja identyfikatora może ostatecznie PUTprzekroczyć limit czasu i jest idempotentny. Lub użyj wygenerowanego przez klienta UUID.


¹ Definicja REST nie mówi nic o idempotencji, więc nie mogę tak naprawdę twierdzić, że nie jest to REST, jeśli masz operacje nie idempotentne. Nie zmienia to faktu, że trzymanie się idempotentnych żądań czyni rzeczy bardziej niezawodnymi bez ich komplikowania i dlatego jest zalecane.

Jan Hudec
źródło
3
O ile mi wiadomo, żądanie POST nie musi być idempotentne. Więc nie widzę problemu z publikowaniem na /users(nie ma potrzeby dodawania „nowego”).
lex82
dzięki za odpowiedź. Widzę, że fajnie jest mieć idempotencję dla wszystkich żądań, ale kto mówi, że jest to wymagane dla interfejsu API REST? Z pewnością wiele interfejsów API, które nazywają się RESTful, zezwala na nie idempotentne żądania POST (szczególnie, gdy serwer generuje identyfikatory nowych zasobów). Ale nawet jeśli zastosujesz bardzo ścisłą definicję REST, nie widzę, które ograniczenie architektoniczne zostało naruszone przez nie idempotentne testy POST, jak w powyższym przykładzie.
lex82
Z pewnością mogę wyobrazić sobie żądania POST, które naruszają ograniczenie. Po prostu nie rozumiem, dlaczego umieszczanie zasobu w kolekcji i pozwalanie serwerowi decydować o jego identyfikatorze narusza ograniczenia REST.
lex82
3
Cała idea POST nie powinna być idempotentna ...
EralpB
2

Zastanawiając się nad tym, łączymy zasób z identyfikatorem URI.

Jeśli identyfikator URI nie ma identyfikatora, należy zmienić ładunek:

PUT / no / id / here Ładowność: {name: "Adrian"} <Jaki użytkownik ???

Czy są jakieś powody, aby tego nie robić?

Odpowiedź na to pytanie zależy od tego, czy chcesz pozwolić klientowi na zmianę identyfikatora?

Jeśli klient może zmienić identyfikator za pośrednictwem PUT, wówczas zmieni się identyfikator URI zasobu i należy podać 301 Przeniesiony na stałe za każdym razem, gdy zasób uzyskuje dostęp do starego identyfikatora URI.

Na przykład zaczynasz od zasobu w

/users/123

a klient PUT umieszcza następujące dane w zasobie

{id: 222, name: "Adrian"}

zasób został zaktualizowany, a jego identyfikator URI jest teraz

/users/222

LocationPola w odpowiedzi PUT powinien zawierać nowy URI, a jeśli idziesz do /users/123was powinien dostać 301odpowiedź z pola Lokalizacja wskazując nowego /users/222zasobu.

W większości przypadków tak naprawdę nie chcesz, aby klient mógł zmienić identyfikator, ponieważ może to dość szybko zrobić bałagan. W takim przypadku identyfikator jest czymś, co tylko serwer może zmienić i należy go pozostawić poza organem PUT, ponieważ klient nie może zaktualizować tego stanu.

Powiedzmy, że jeśli umieścisz żądanie do innego identyfikatora URI w tym samym zasobie

/users/adian_lync

to jeśli ten zasób nie istnieje, serwer powinien go utworzyć oraz utworzyć i ID, kiedy to robi

Cormac Mulhall
źródło
1
Powodem, dla którego zakwestionowaliśmy umieszczenie identyfikatora w ładunku, było domyślnie podanie przez Backbone.js identyfikatora w żądaniu PUT. Możemy temu zapobiec, ale teraz chcę wiedzieć, dlaczego jest to zachowanie domyślne.
Adrian Lynch
1
Obawiam się, że nie znam Backbone.js. Wydaje się niepotrzebne, jeśli identyfikator znajduje się również w adresie URL. Być może niedopatrzenie devs
Cormac Mulhall