349 votes

Comment gérer les relations plusieurs-à-plusieurs dans une API RESTful?

Imaginez que vous avez 2 entités, des joueurs et de l'Équipe, où les joueurs peuvent être sur plusieurs équipes. Dans mon modèle de données, j'ai une table pour chaque entité, et une table de jointure pour le maintien de relations. Hibernate est bien à la manipulation, mais comment pourrais-je exposer cette relation dans une API RESTful?

Je peux penser à un couple de façons. Tout d'abord, je pourrait avoir chaque entité contiennent une liste de l'autre, de sorte qu'un objet de Joueur aurait une liste des Équipes de la il appartient, et chaque Équipe objet aurait une liste de Joueurs qui en font partie. Donc, pour ajouter un Joueur à une Équipe, vous suffit de POSTER le joueur de représentation à un point de terminaison, quelque chose comme POST /joueur ou POST /d'équipe avec l'objet approprié de la charge utile de la demande. Ce qui me semble le plus "Reposant" pour moi, mais se sent un peu bizarre.

L'autre façon que je peux penser à faire serait d'exposer la relation comme une ressource dans son propre droit. Pour voir une liste de tous les joueurs d'une équipe, vous pourriez faire un GET /playerteam/équipe/{id} ou quelque chose comme ça et de revenir une liste de PlayerTeam entités. Pour ajouter un joueur à une équipe, POSTES /playerteam avec un construit de manière appropriée PlayerTeam entité de la charge utile.

Quelle est la meilleure pratique pour cela?

301voto

fumanchu Points 8291

En faire un ensemble distinct d' /memberships/ des ressources.

  1. RESTE est à propos de faire évolutives des systèmes si de rien d'autre. À ce moment, vous pouvez seulement de soins qu'un joueur est dans une équipe, mais à un certain moment dans l'avenir, vous aurez souhaitez annoter en relation avec plus de données: depuis combien de temps ils ont été dans l'équipe, qui les a renvoyés à cette équipe, qui leur entraîneur est/était sur l'équipe, etc etc.
  2. RESTE dépend de la mise en cache pour plus d'efficacité, ce qui nécessite une certaine considération pour le cache de l'atomicité et l'invalidation. Vous ne voulez pas afficher une nouvelle entité à l' /teams/3/players/ et pour que la liste soit invalidée, ont encore l'URL de remplacement /players/5/teams/ reste inchangé. Oui, caches différents auront une copie de chaque liste de différents âges, et il n'y a pas beaucoup que nous pouvons faire à ce sujet, mais nous pouvons au moins de minimiser la confusion pour l'utilisateur POST avec la mise à jour de la limitation du nombre d'entités, nous avons besoin de les invalider dans leur cache local du client à un et un seul à l' /memberships/98745 (voir Helland la discussion de la "alternate indices" dans la Vie au-delà des Transactions Distribuées pour une discussion plus détaillée).
  3. Vous pourriez mettre en œuvre les au-dessus de 2 points en choisissant simplement /players/5/teams ou /teams/3/players (mais pas les deux). Supposons que dans l'ancien. À un certain point, cependant, vous voulez réserver /players/5/teams/ pour une liste actuelle des adhésions, et encore être en mesure de se référer aux dernières adhésions quelque part. Faire /players/5/memberships/ une liste de liens hypertextes vers d' /memberships/{id}/ des ressources, et puis vous pouvez ajouter /players/5/past_memberships/ quand vous voulez, sans avoir à casser tous les signets pour l'adhésion individuelle des ressources. C'est un concept général; je suis sûr que vous pouvez imaginer d'autres semblables, les contrats à terme qui sont plus applicables à votre cas spécifique.

149voto

Donal Fellows Points 56559

Dans une interface RESTful, vous pouvez renvoyer les documents qui décrivent les relations entre les ressources en codant ces relations sous forme de liens. Ainsi, une équipe peut être dit d'avoir un document ressource (/team/{id}/players) qui est une liste de liens vers des joueurs (/player/{id}) sur l'équipe, et un joueur peut avoir un document ressource (/player/{id}/teams) qui est une liste de liens vers des équipes que le joueur est un membre de. Nice et symétrique. Vous pouvez la carte des opérations sur cette liste assez facilement, même en donnant une relation de son propre Id (sans doute ils auraient deux Id, selon que vous êtes en train de penser au sujet de la relation de l'équipe ou joueur) si cela rend les choses plus faciles. Le seul problème est que vous avez à penser à supprimer la relation à l'autre bout ainsi si vous le supprimer à partir d'une extrémité, mais rigoureusement de la manipulation de ce à l'aide d'un modèle de données sous-jacente et puis d'avoir le RESTE de l'interface être une vue de ce modèle va faciliter la vie.

Relation Id doit sûrement être fondée sur des Uuid ou quelque chose de tout aussi longue et aléatoire, quel qu'en soit le type d'Id que vous utilisez pour les équipes et les joueurs. Qui vous permettra d'utiliser le même UUID comme l'ID de l'élément pour chaque fin de la relation sans se soucier de collisions (petits entiers de ne pas avoir cet avantage). Si ces relations ont toutes des propriétés autres que le simple fait qu'ils se rapportent à un joueur et une équipe dans un bidirectionnel de la mode, ils devraient ont leur propre identité, qui est indépendant à la fois des joueurs et des équipes; un OBTENIR sur le joueur»de la vue d'équipe (/player/{playerID}/teams/{teamID}) pourrait alors faire une redirection HTTP vers la bidirectionnel vue (/memberships/{uuid}).

Je recommande l'écriture des liens dans tous les documents XML vous de retour (si vous arrive d'être la production de XML bien sûr) à l'aide de XLink xlink:href attributs.

76voto

manuel aldana Points 4317

Je mapperais une telle relation avec des sous-ressources, la conception générale / traversée serait alors:


# team resource
/teams/{teamId}

# players resource
/players/{playerId}

# teams/players subresource
/teams/{teamId}/players/{playerId} 

En termes de Restful, cela aide beaucoup à ne pas penser au SQL et aux jointures, mais plutôt aux collections, aux sous-collections et aux parcours.

Quelques exemples:


# getting player 3 who is on team 1
# or simply checking whether player 3 is on that team (200 vs. 404)
GET /teams/1/players/3

# getting player 3 who is also on team 3
GET /teams/3/players/3

# adding player 3 also to team 2
PUT /teams/2/players/3

# getting all teams of player 3
GET /players/3/teams

# withdraw player 3 from team 1 (appeared drunk before match)
DELETE /teams/1/players/3

# team 1 found a replacement, who is not registered in league yet
POST /players
# from payload you get back the id, now place it officially to team 1
PUT /teams/1/players/44
 

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 joueurs et équipes.

-5voto

MoaLaiSkirulais Points 24
  1. / joueurs (est une ressource principale)
  2. / teams / {id} / players (est une ressource relationnelle, donc elle réagit différemment que 1)
  3. / adhésions (est une relation mais sémantiquement compliquée)
  4. / joueurs / membres (est une relation mais sémantiquement compliquée)

Je préfère 2

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X