Kiedy używać zagnieżdżonych zasobów w interfejsie API RESTful

16

Mam dwa zasoby: użytkowników i linki.

Użytkownicy mogą mieć kilka linków powiązanych z nimi. Zaprojektowałem mój interfejs API RESTful, abyś mógł uzyskać dostęp do linków powiązanych z użytkownikiem o następującym identyfikatorze URI:

/users/:id/links

Jednak zawsze muszę mieć identyfikator URI dla samych łączy - czasami mogę chcieć wszystkich łączy, niezależnie od użytkownika.

Do tego mam:

/links

Czy to brzmi dobrze? Masz dwa identyfikatory URI dla łączy?

Zastanawiam się, czy zamiast tego powinienem dotrzeć do linków użytkownika z identyfikatorem URI, takiego jak:

/links/user/:id lub /links/?user=:id

W ten sposób mam tylko jeden zasób dla linków.

Oliver Joseph Ash
źródło
3
hmm .. Ten wydaje się bardziej elegancki:/links/user/:id
theMarceloR
7
@ theMarceloR: To jedyny z trzech przykładów, który nie wydaje mi się szczególnie jasny. Czy zasób linku czy użytkownika? Identyfikator URI jest znacznie mniej dwuznaczny przy użyciu metody zagnieżdżonego zasobu ( /users/:id/links) lub metody ciągu zapytania ( /links/?user=:id), ponieważ w rzeczywistości jest zapytaniem. /links/user/:idmoże wyglądać ładnie i / lub być łatwiejszy w routingu w niektórych ramach, ale w rzeczywistości jest to dość mylące.
Aaronaught

Odpowiedzi:

16

Nie, nie ma nic złego w posiadaniu wielu zasobów dla tej samej „rzeczy”, w tym przypadku list łączy.

Ostatnio zmagaliśmy się z tym samym problemem. Naszą decyzją było posiadanie wszystkich zasobów, w których nie ma ścisłej własności, których nie można zagnieżdżać. Innymi słowy, linki będą modelowane pod

/links -- all links
/links/:linkid -- a particular link

Następnie filtry w kolekcji łączy są wyrażane jako parametry zapytania. Aby uzyskać linki do określonego użytkownika, użyj:

/links?user=/users/:userid

Pozwala to również na łatwiejsze komponowanie filtrów:

/links?user=/users/10&since=2013-01-01

I łatwo to zrozumieć koncepcyjnie - masz kolekcję przedmiotów i dodajesz do niej filtry.

Biorąc to pod uwagę, nie ma nic bardziej „RESTful” w tym podejściu niż jakikolwiek inny schemat nazewnictwa URI. To tylko konwencja, którą uznaliśmy za czytelną dla człowieka i łatwą do zrozumienia dla nas jako programistów. REST nie dba o to, co umieścisz w swoich identyfikatorach zasobów.

jemiołucha
źródło
1
„Wszystkie zasoby, w przypadku których nie istnieje ścisła własność, której nie można zagnieżdżać”, brzmi jak dobra zasada. Wielkie dzięki.
Oliver Joseph Ash
5
Powiedziałbym, że jest coś złego w posiadaniu wielu zasobów dla tego samego bytu fizycznego, ale tak nie jest. Każdy pojedynczy link ma kanoniczny adres URL (links /: id), podczas gdy każdy z powyższych zasobów jest w rzeczywistości pojedynczym zasobem (/ links) z zastosowanymi filtrami. Dziwię się ?user=users/:useridrównież ciągiem zapytania; co jest nie tak z just ?userid=:userid?
Aaronaught
2
@Aaronaught: powodem trzymania się identyfikatorów URI zamiast nagich identyfikatorów jest to, że klienci mogą traktować wszystkie identyfikatory zasobów jednakowo, to znaczy, że klient musi tylko wiedzieć, że „ten użytkownik jest identyfikowany przez "/*******"; gdzie *są nieprzezroczyste. ale zawsze można użyć tego samego identyfikatora w odniesieniu do tego zasobu (jak w, z linkami), aby pobrać najnowszą wersję tego zasobu i tak dalej. treść tego identyfikatora jest rozumiana tylko przez serwer pochodzenia. Oznacza to również, że jeśli identyfikatory wymagają zmiany, powiedzmy /realm/:businessid/users/:id, klient wcale się nie zmienia.
SingleNegationElimination
@TokenMacGuy: Przepraszam, nie rozumiem żadnej części twojego komentarza. Jaka jest praktyczna różnica między /users/:userid/linksi /links?userid=:userid? W obu przypadkach identyfikatory się nie zmieniają, pobierają bieżącą wersję tego zasobu, a klient traktuje je w ten sam sposób. Nie ma w tym przypadku kanonicznego adresu URL, ponieważ nie jest to zasób, to zapytanie, więc są to tylko dwa różne typy składni zapytania. Nie mam również jasności co do tego, jak klient nie musiałby zmieniać, gdyby zmieniła się struktura adresu URL; jest to uważane za przełomową zmianę w REST, chyba że 301 jest na miejscu.
Aaronaught
1
@Aaronaught: Mogłem źle zrozumieć twoje obawy. Załóżmy, że /linksobsługuje interfejs zapytań, /links?user=/users/123pozwala na podejście czarnej skrzynki do identyfikatorów zasobów u klientów, które /links?userid=123nie obsługuje. ten drugi wymaga od klienta zrozumienia, czym jest identyfikator użytkownika i jak go uzyskać, prawdopodobnie z zasobu, który uzyskał z /users/123lub /links/456/user. To pierwsze oznacza, że ​​klient może używać niezmodyfikowanego identyfikatora uri; założenie, że /links/.../userdaje odpowiedź hipermedialną (powiedzmy, z Location:nagłówkami).
SingleNegationElimination
1

Tak więc mam obawy: / users /: userid / links zwraca „links”, ALE jeśli user_id nie zostanie zidentyfikowany, to powinno zwrócić 404.

jednak

/ links? userid =: userid potencjalnie zwróci pustą listę (zasadniczo 200), co w rzeczywistości jest prawdopodobnie błędem. I całkiem możliwe.

Chociaż oba działają, zagnieżdżanie zapewnia dodatkową funkcjonalność, z której można później korzystać.

Richard Browne
źródło