Wyobraź sobie, że masz 2 podmioty, Gracza i Drużyny , w których gracze mogą należeć do wielu drużyn. W moim modelu danych mam tabelę dla każdej jednostki i tabelę łączenia, aby zachować relacje. Hibernacja radzi sobie z tym dobrze, ale jak mogę ujawnić ten związek w interfejsie API RESTful?
Mogę wymyślić kilka sposobów. Po pierwsze, każda jednostka może mieć listę drugiej, więc obiekt Gracza miałby listę Drużyn, do których należy, a każdy obiekt Drużyny miałby listę Graczy, które do niej należą. Aby dodać gracza do zespołu, wystarczy POST przedstawić reprezentację gracza do punktu końcowego, coś w rodzaju POST /player
lub POST /team
z odpowiednim obiektem jako ładunkiem żądania. Wydaje mi się to najbardziej „ODPOCZYNEK”, ale wydaje mi się trochę dziwne.
/api/team/0:
{
name: 'Boston Celtics',
logo: '/img/Celtics.png',
players: [
'/api/player/20',
'/api/player/5',
'/api/player/34'
]
}
/api/player/20:
{
pk: 20,
name: 'Ray Allen',
birth: '1975-07-20T02:00:00Z',
team: '/api/team/0'
}
Innym sposobem, w jaki mogę to zrobić, byłoby ujawnienie relacji jako odrębnego zasobu. Aby zobaczyć listę wszystkich graczy w danym zespole, możesz zrobić GET /playerteam/team/{id}
lub coś w tym stylu i uzyskać listę podmiotów PlayerTeam. Aby dodać gracza do drużyny, POST /playerteam
z odpowiednio zbudowaną jednostką PlayerTeam jako ładunek.
/api/team/0:
{
name: 'Boston Celtics',
logo: '/img/Celtics.png'
}
/api/player/20:
{
pk: 20,
name: 'Ray Allen',
birth: '1975-07-20T02:00:00Z',
team: '/api/team/0'
}
/api/player/team/0/:
[
'/api/player/20',
'/api/player/5',
'/api/player/34'
]
Jaka jest najlepsza praktyka w tym zakresie?
źródło
Zmapowałbym taki związek z podrzędnymi zasobami, ogólny projekt / przejście byłoby wtedy:
W kategoriach Restful bardzo pomaga to nie myśleć o SQL i dołączać, ale bardziej w kolekcje, subkolekcje i przechodzenie.
Kilka przykładów:
Jak widać, nie używam POST do umieszczania graczy w drużynach, ale PUT, który lepiej radzi sobie z twoimi relacjami między graczami i drużynami n: n.
źródło
status
jako parametr w żądaniu PUT? Czy to podejście ma wadę?Istniejące odpowiedzi nie wyjaśniają roli spójności i idempotencji - które motywują ich rekomendacje
UUIDs
liczb losowych dla identyfikatorów iPUT
zamiast nichPOST
.Jeśli weźmiemy pod uwagę przypadek, w którym mamy prosty scenariusz, taki jak „ Dodaj nowego gracza do zespołu ”, możemy napotkać problemy ze spójnością.
Ponieważ odtwarzacz nie istnieje, musimy:
Należy jednak operacja klient nie po
POST
do/players
, stworzyliśmy gracza, który nie należy do zespołu:Teraz mamy osieroconego duplikata gracza
/players/5
.Aby to naprawić, możemy napisać niestandardowy kod odzyskiwania, który sprawdza osieroconych graczy, którzy pasują do jakiegoś naturalnego klucza (np
Name
.). Jest to niestandardowy kod, który należy przetestować, kosztuje więcej pieniędzy i czasu itpAby uniknąć potrzeby niestandardowego kodu odzyskiwania, możemy go wdrożyć
PUT
zamiastPOST
.Z RFC :
Aby operacja była idempotentna, musi wykluczać dane zewnętrzne, takie jak generowane przez serwer sekwencje identyfikatorów. To dlatego ludzie zalecają zarówno
PUT
iUUID
s dlaId
s razem.To pozwala nam ponownie uruchomić zarówno te, jak
/players
PUT
i/memberships
PUT
bez konsekwencji:Wszystko jest w porządku i nie musieliśmy robić nic więcej niż próbować w przypadku częściowych awarii.
Jest to raczej uzupełnienie istniejących odpowiedzi, ale mam nadzieję, że umieści je w kontekście szerszego obrazu tego, jak elastyczna i niezawodna może być ReST.
źródło
23lkrjrqwlej
?Moje preferowanym rozwiązaniem jest utworzenie trzech zasobów:
Players
,Teams
iTeamsPlayers
.Tak więc, aby zdobyć wszystkich graczy z drużyny, po prostu przejdź do
Teams
zasobów i zbierz wszystkich graczy, dzwoniącGET /Teams/{teamId}/Players
.Z drugiej strony, aby zdobyć wszystkie drużyny, w które grał gracz, zdobądź
Teams
zasoby w obrębiePlayers
. ZadzwońGET /Players/{playerId}/Teams
.I, aby uzyskać połączenie wiele-do-wielu
GET /Players/{playerId}/TeamsPlayers
lubGET /Teams/{teamId}/TeamsPlayers
.Zauważ, że w tym rozwiązaniu, kiedy dzwonisz
GET /Players/{playerId}/Teams
, otrzymujesz tablicęTeams
zasobów, czyli dokładnie taki sam zasób, jak dzwoniszGET /Teams/{teamId}
. Odwrotna zasada działa zgodnie z tą samą zasadą,Players
podczas połączenia otrzymujesz szereg zasobówGET /Teams/{teamId}/Players
.W obu połączeniach nie są zwracane informacje o relacji. Na przykład nie
contractStartDate
jest zwracane, ponieważ zwrócony zasób nie ma informacji o relacji, tylko o swoim własnym zasobie.Aby poradzić sobie z nn związku, albo połączenia
GET /Players/{playerId}/TeamsPlayers
lubGET /Teams/{teamId}/TeamsPlayers
. Połączenia te zwracają dokładnie zasobuTeamsPlayers
.Ten
TeamsPlayers
zasóbid
,playerId
,teamId
atrybuty, a także kilka innych do opisania relacji. Ma także metody niezbędne do radzenia sobie z nimi. GET, POST, PUT, DELETE itp., Które zwrócą, uwzględnią, zaktualizują, usuną zasób relacji.Te
TeamsPlayers
narzędzia zasobów niektórych zapytań, jakGET /TeamsPlayers?player={playerId}
zwrócić wszystkieTeamsPlayers
relacje gracz zidentyfikowane przez{playerId}
ma. Zgodnie z tym samym pomysłem, użyj,GET /TeamsPlayers?team={teamId}
aby zwrócić wszystkieTeamsPlayers
, które grały w{teamId}
zespole. W obuGET
połączeniach zasóbTeamsPlayers
jest zwracany. Wszystkie dane związane z relacją są zwracane.Podczas dzwonienia
GET /Players/{playerId}/Teams
(lubGET /Teams/{teamId}/Players
), zasobyPlayers
(lubTeams
) dzwonią ,TeamsPlayers
aby zwrócić powiązane drużyny (lub graczy) za pomocą filtra zapytań.GET /Players/{playerId}/Teams
działa w ten sposób:Możesz użyć tego samego algorytmu, aby pozyskać wszystkich graczy z drużyny, gdy dzwonisz
GET /Teams/{teamId}/Players
, ale wymieniaj drużyny i graczy.Moje zasoby wyglądałyby tak:
To rozwiązanie opiera się tylko na zasobach REST. Chociaż konieczne mogą być dodatkowe połączenia w celu uzyskania danych od graczy, zespołów lub ich relacji, wszystkie metody HTTP można łatwo wdrożyć. POST, PUT, DELETE są proste i jednoznaczne.
Za każdym razem, gdy relacja jest tworzona, aktualizowana lub usuwana, oba zasoby
Players
iTeams
zasoby są automatycznie aktualizowane.źródło
Wiem, że odpowiedź na to pytanie została oznaczona jako zaakceptowana, ale oto, w jaki sposób możemy rozwiązać wcześniej podniesione problemy:
Powiedzmy, że dla PUT
Na przykład poniższe działania przyniosą ten sam efekt bez potrzeby synchronizacji, ponieważ są wykonywane na jednym zasobie:
teraz, jeśli chcemy zaktualizować wiele członkostwa dla jednego zespołu, moglibyśmy zrobić w następujący sposób (z odpowiednią weryfikacją):
źródło
Wolę 2
źródło