Imaginez que vous ayez 2 entités, Player et Team , où les joueurs peuvent faire partie de plusieurs équipes. Dans mon modèle de données, j'ai une table pour chaque entité et une table de jointure pour maintenir les relations. Hibernate gère bien cela, mais comment pourrais-je exposer cette relation dans une API RESTful?
Je peux penser à deux façons. Tout d'abord, je pourrais faire en sorte que chaque entité contienne une liste de l'autre, donc un objet Player aurait une liste des équipes auxquelles il appartient, et chaque objet Team aurait une liste des joueurs qui lui appartiennent. Donc, pour ajouter un joueur à une équipe, il vous suffirait de POSTER la représentation du joueur sur un point de terminaison, quelque chose comme POST /player
ou POST /team
avec l'objet approprié comme charge utile de la demande. Cela me semble le plus "reposant" mais ça fait un peu bizarre.
/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'
}
L'autre façon de penser à cela serait d'exposer la relation comme une ressource à part entière. Donc, pour voir une liste de tous les joueurs d'une équipe donnée, vous pouvez faire un GET /playerteam/team/{id}
ou quelque chose comme ça et récupérer une liste d'entités PlayerTeam. Pour ajouter un joueur à une équipe, POST /playerteam
avec une entité PlayerTeam correctement construite comme charge utile.
/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'
]
Quelle est la meilleure pratique pour cela?
la source
Je mapperais une telle relation avec les sous-ressources, la conception / traversée générale serait alors:
En termes reposants, cela aide beaucoup à ne pas penser à SQL et aux jointures, mais plutôt aux collections, sous-collections et traversées.
Quelques exemples:
Comme vous le voyez, je n'utilise pas POST pour placer des joueurs dans des équipes mais PUT, qui gère mieux votre relation n: n entre les joueurs et les équipes.
la source
status
comme paramètre dans la demande PUT? Y a-t-il un inconvénient à cette approche?Les réponses existantes n'expliquent le rôle de la cohérence et idempotence - qui motivent leurs recommandations
UUIDs
/ nombres aléatoires pour les ID et auPUT
lieu dePOST
.Si nous considérons le cas où nous avons un scénario simple comme " Ajouter un nouveau joueur à une équipe ", nous rencontrons des problèmes de cohérence.
Parce que le joueur n'existe pas, nous devons:
Toutefois, si l' échec de l'opération client après
POST
pour/players
, nous avons créé un joueur qui ne appartiennent à une équipe:Nous avons maintenant un lecteur dupliqué orphelin
/players/5
.Pour résoudre ce problème, nous pouvons écrire un code de récupération personnalisé qui recherche les joueurs orphelins qui correspondent à une clé naturelle (par exemple
Name
). Il s'agit d'un code personnalisé qui doit être testé, coûte plus d'argent et de temps, etc., etc.Pour éviter d'avoir besoin d'un code de récupération personnalisé, nous pouvons l'implémenter à la
PUT
place dePOST
.Du RFC :
Pour qu'une opération soit idempotente, elle doit exclure les données externes telles que les séquences d'ID générées par le serveur. C'est pourquoi les gens recommandent les deux
PUT
et lesUUID
s pour lesId
s ensemble.Cela nous permet de relancer à la fois le
/players
PUT
et le/memberships
PUT
sans conséquences:Tout va bien et nous n'avons pas eu besoin de faire autre chose que de réessayer pour des échecs partiels.
Il s'agit davantage d'un addendum aux réponses existantes, mais j'espère que cela les replace dans le contexte d'une vue d'ensemble de la flexibilité et de la fiabilité de ReST.
la source
23lkrjrqwlej
?Ma solution préférée est de créer trois ressources:
Players
,Teams
etTeamsPlayers
.Donc, pour obtenir tous les joueurs d'une équipe, allez simplement à la
Teams
ressource et obtenez tous ses joueurs en appelantGET /Teams/{teamId}/Players
.D'un autre côté, pour obtenir toutes les équipes qu'un joueur a jouées, obtenez la
Teams
ressource dans lePlayers
. AppelleGET /Players/{playerId}/Teams
.Et, pour obtenir l'appel de relation plusieurs-à-plusieurs
GET /Players/{playerId}/TeamsPlayers
ouGET /Teams/{teamId}/TeamsPlayers
.Notez que, dans cette solution, lorsque vous appelez
GET /Players/{playerId}/Teams
, vous obtenez un tableau deTeams
ressources, c'est exactement la même ressource que vous obtenez lorsque vous appelezGET /Teams/{teamId}
. L'inverse suit le même principe, vous obtenez un tableau dePlayers
ressources lors de l'appelGET /Teams/{teamId}/Players
.Dans les deux appels, aucune information sur la relation n'est renvoyée. Par exemple, aucun
contractStartDate
n'est renvoyé, car la ressource renvoyée ne contient aucune information sur la relation, uniquement sur sa propre ressource.Pour gérer la relation nn, appelez soit
GET /Players/{playerId}/TeamsPlayers
ouGET /Teams/{teamId}/TeamsPlayers
. Ces appels renvoient la ressource exactementTeamsPlayers
.Cette
TeamsPlayers
ressource aid
,playerId
,teamId
attributs, ainsi que d'autres pour décrire la relation. En outre, il dispose des méthodes nécessaires pour y faire face. GET, POST, PUT, DELETE etc. qui renverra, inclura, mettra à jour, supprimera la ressource de relation.La
TeamsPlayers
ressource implémente certaines requêtes, commeGET /TeamsPlayers?player={playerId}
retourner toutes lesTeamsPlayers
relations identifiées par le joueur{playerId}
. Suivant la même idée, utilisezGET /TeamsPlayers?team={teamId}
pour retourner tous ceuxTeamsPlayers
qui ont joué dans l'{teamId}
équipe. Dans les deuxGET
appels, la ressourceTeamsPlayers
est renvoyée. Toutes les données liées à la relation sont retournées.Lors de l'appel
GET /Players/{playerId}/Teams
(ouGET /Teams/{teamId}/Players
), la ressourcePlayers
(ouTeams
) appelleTeamsPlayers
pour renvoyer les équipes (ou joueurs) associés à l'aide d'un filtre de requête.GET /Players/{playerId}/Teams
fonctionne comme ceci:Vous pouvez utiliser le même algorithme pour obtenir tous les joueurs d'une équipe, lors de l'appel
GET /Teams/{teamId}/Players
, mais en échangeant des équipes et des joueurs.Mes ressources ressembleraient à ceci:
Cette solution repose uniquement sur les ressources REST. Bien que certains appels supplémentaires puissent être nécessaires pour obtenir des données des joueurs, des équipes ou de leur relation, toutes les méthodes HTTP sont facilement implémentées. POST, PUT, DELETE sont simples et directs.
Chaque fois qu'une relation est créée, mise à jour ou supprimée, les ressources
Players
et lesTeams
ressources sont automatiquement mises à jour.la source
Je sais qu'il y a une réponse marquée comme acceptée pour cette question, cependant, voici comment nous pourrions résoudre les problèmes soulevés précédemment:
Disons pour PUT
À titre d'exemple, les éléments suivants auront tous le même effet sans nécessiter de synchronisation, car ils sont effectués sur une seule ressource:
maintenant, si nous voulons mettre à jour plusieurs adhésions pour une même équipe, nous pouvons procéder comme suit (avec les validations appropriées):
la source
Je préfère 2
la source