猿问
下载APP

如何在RESTful API中处理多对多关系?

如何在RESTful API中处理多对多关系?

想象一下,你有2个实体,玩家团队,玩家可以在多个团队中。在我的数据模型中,我有一个每个实体的表,以及一个用于维护关系的连接表。Hibernate可以很好地处理这个问题,但是我如何在RESTful API中公开这种关系呢?

我可以想几个方面。首先,我可能让每个实体都包含另一个实体的列表,因此Player对象将拥有它所属的Teams列表,并且每个Team对象都有一个属于它的Players列表。因此,要向团队添加播放器,您只需将播放器的表示形式发布到端点,例如POST /player或POST /team,并将相应的对象作为请求的有效负载。这对我来说似乎是最“RESTful”但感觉有点奇怪。

/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'}

我能想到的另一种方法就是将关系本身作为一种资源来揭露。因此,要查看给定团队中所有玩家的列表,您可以执行GET /playerteam/team/{id}或类似的操作并获取PlayerTeam实体列表。要将球员添加到球队,请/playerteam使用适当构建的PlayerTeam实体作为有效负载进行POST 。

/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'        ]

这是什么最好的做法?


达令说
浏览 86回答 3
3回答

慕仰8121524

在RESTful接口中,您可以通过将这些关系编码为链接来返回描述资源之间关系的文档。因此,可以说团队有一个文档资源(/team/{id}/players),它是/player/{id}团队中玩家()的链接列表,玩家可以拥有文档资源(/player/{id}/teams)这是玩家所属团队的链接列表。不错,对称。你可以轻松地在该列表上进行地图操作,甚至可以给自己的关系提供一个关系(可以说他们有两个ID,这取决于你是在考虑团队优先关系还是玩家优先关系)如果这样可以让事情变得更容易。唯一棘手的一点是,如果从一端删除它,你必须记住从另一端删除关系,但是通过使用底层数据模型严格处理它,然后让REST接口成为该模型将使这更容易。关系ID可能应该基于UUID或同样长且随机的东西,无论您为团队和玩家使用何种类型的ID。这将允许您为关系的每一端使用相同的UUID作为ID组件,而不必担心冲突(小整数没有这种优势)。如果这些会员关系具有除了与玩家和团队双向关联的事实以外的任何属性,他们应该拥有独立于玩家和团队的自己的身份; /player/{playerID}/teams/{teamID}然后,玩家»团队视图()上的GET 可以进行HTTP重定向到双向视图(/memberships/{uuid})。我建议使用XLink xlink:href属性在您返回的任何XML文档中编写链接(如果您恰好正在生成XML)。

慕娘9325324

制作一组单独的/memberships/资源。如果没有别的话,REST就是要制作可进化的系统。此时,您可能只关心某个玩家是否在某个特定团队中,但在将来的某个时刻,您会希望用更多数据来注释该关系:他们在该团队中待了多长时间,谁引用了他们对那个团队,他们的教练是谁/当时在那个团队等等。REST依赖于缓存效率,这需要考虑缓存原子性和失效。如果您将新实体POST到/teams/3/players/该列表将失效,但您不希望备用URL /players/5/teams/保持缓存状态。是的,不同的缓存将包含不同年龄的每个列表的副本,并且我们无法做很多事情,但我们至少可以通过限制我们需要使实体无效的实体数量来最小化用户POST更新的混淆在他们的客户端的本地缓存中唯一一个在/memberships/98745(见“交替指数”的埃兰的讨论生命超越分布式事务的更详细的讨论)。您可以通过简单地选择/players/5/teams或/teams/3/players(但不是两者)来实现上述2点。让我们假设前者。但是,在某些时候,您需要保留当前成员资格/players/5/teams/的列表,并且能够在某处引用过去的成员资格。让超链接到列表中的资源,然后你可以添加当你喜欢,而不必打破大家的书签为个人会员资源。这是一般概念; 我相信你可以想象其他类似的期货更适用于你的具体情况。/players/5/memberships//memberships/{id}//players/5/past_memberships/

jeck猫

我会将这种关系映射到子资源,一般设计/遍历将是:# team resource/teams/{teamId}# players resource/players/{playerId}# teams/players subresource/teams/{teamId}/players/{playerId}在Restful-terms中,它有很多不用考虑SQL和连接,而是更多地考虑集合,子集合和遍历。一些例子:# 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 3GET /teams/3/players/3# adding player 3 also to team 2PUT /teams/2/players/3# getting all teams of player 3GET /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 yetPOST /players# from payload you get back the id, now place it officially to team 1PUT /teams/1/players/44如你所见,我没有使用POST将球员放到球队,而是PUT,它可以更好地处理球员和球队的n:n关系。
打开App,查看更多内容
随时随地看视频慕课网APP
我要回答