【问题标题】:REST and many to manyREST 和多对多
【发布时间】:2015-05-14 10:56:15
【问题描述】:

我的问题基于How to handle many-to-many relationships in a RESTful API?,但想从已接受的答案继续。

假设我们在球员和球队之间有一个多对多的关系(就像上面提到的问题一样)。

据我了解,有几个选项可以使用 REST 资源对此进行建模:

有效负载包含对相关资源的引用

GET /players/john

产量

{
    "name": "John",
    "_links": [
        {
            "rel": "team",
            "href": "/teams/1"
        },
        {
            "rel": "team",
            "href": "/teams/4"
        }
    ]
}

GET /teams/1

产量

{
    "name": "Team 1",
    "_links": [
        {
            "rel": "player",
            "href": "/players/john"
        },
        ...
    ]
}

当我只想将一名球员添加到球队时,这会迫使我更新球员资源。此外,当我使用玩家资源将玩家添加到团队时,相应的团队资源会自动更新。根据How to handle many-to-many relationships in a RESTful API?

您不希望备用 URL /players/5/teams/ 保持缓存状态

在这种情况下,当我更新玩家“John”以从中删除团队“Team 1”时,teams/1 可能会保持缓存!

关系被建模为另一个资源

GET /teams/1

产量

{
    "name": "Team 1",
}

GET /players/john

产量

{
    "name": "John",
}

最后,

GET /relationships

产量

    [
    {
        "_links": [
            {
                "rel": "player",
                "href": "/players/john"
            },
            {
                "rel": "team",
                "href": "/teams/1"
            }
        ]
    },

    ...
]

这样,我可以在不影响玩家资源和团队资源的情况下创建和删除关系。但是当我删除/players/john时,匹配关系是否也应该被自动删除?如果是这种情况,则违反了与上述相同的规则。如果不是这种情况,我们需要手动删除这些关系,这是我不想让我的 API 使用者负担的大量工作。

此外,如果我们想要更新某个玩家“John”所在的球队,我们需要删除一些关系并添加其他关系。当其他人正在编辑玩家“John”或团队“Team 1”时,我们会敞开心扉合并冲突(和比赛条件)。

关系的每一方都有自己的关系对象

GET /teams/1/players

产生类似的东西

{
    "_links": [
        {
            "rel": "player",
            "href": "/players/john"
        },
        ...
    ]
}

GET /players/john/teams

类似

{
    "_links": [
        {
            "rel": "team",
            "href": "/teams/1"
        },
        ...
    ]
}

但添加或删除一个可能仍会影响位于不同 URL(不共享根元素)的资源

我的问题

我在这两种情况下提到的问题都没有了吗?

这两种方法中哪一种是“首选”或更纯粹的 REST?

我应该对How to handle many-to-many relationships in a RESTful API?中提到的约束有多认真:

您不希望备用 URL /players/5/teams/ 保持缓存状态

提前谢谢你!

【问题讨论】:

  • 当我只想将一名球员添加到球队时,这会迫使我更新球员资源。为什么这样不好?
  • 例如,我需要知道一名球员所在的所有其他球队,以将球员更新到正确的状态(与球队有所有正确的关系)。此外,我希望允许球员更换球队,但我不想让球员改变他们的名字。这是管理员的责任。两者都使用相同的资源,我需要检查角色并将其与请求的更改进行比较,而不是仅仅禁止某些用户对资源进行 PUT 或 POST。
  • 我需要知道一名球员所在的所有其他球队为什么?您可以将一个团队附加到 player.teams
  • 这将如何工作?你会说你更喜欢第一种方法吗?
  • 您将 PUT 一个用户并将新团队添加到用户的团队列表中

标签: rest restful-architecture


【解决方案1】:

你可以有以下

团队

GET /teams/dream

{
    "_links": {
        "self": {
            "href": "/teams/dream"
        }
        "players": {
            "href": "/players?team=dream"
        }
    },
    "name": "Dream"
}

播放器

GET /player/john

{
    "_links": {
        "self": {
            "href": "/player/john"
        },
        "teams": {
            "href": "/teams?player=john"
        },
    },
    "name": "John",
}

约翰的团队

GET /teams?player=john

{
    "_links": {
    },
    "items": [
        {
            "href": "/teams/a-team"
        }
    ],
}

将 john 添加到梦之队,(例如使用 json 补丁)(补丁帖子上的查询字符串...等虽然很少见,但有效)

PATCH /teams?player=john

[{
    "op": "add",
    "path": "/-",
    "value": {
        "href": "/team/dream"
    }
}]

获取约翰的团队

GET /teams?player=john

{
    "_links": {
    },
    "items": [
        {
            "href": "/teams/a-team"
        },
        {
            "href": "/teams/dream"
        }
    ]
}

约翰离开了一个团队:

PATCH /teams?player=john

[{
    "op": "remove",
    "path": "/0"
}]

【讨论】:

  • 感谢您抽出宝贵时间回答我的问题。是否有理由使用“op”属性“remove”和“add”而不是使用 DELETE 和 PATCH?你如何删除约翰的所有关系?通过 /teams?user=john?
  • DELETE /teams?player=john 对于补丁,您可以使用任何您想要的结构,但我们正在努力为此制定标准。见jsonpatch.com
  • 感谢您的回答。我不知道 JSON 补丁,但看起来它可以解决我的很多问题!
  • 我会(并且在实践中)在删除操作中使用实际的资源路径(即/teams/a-team)。您不想使用索引。即使保证了顺序(在集合中很少如此),另一个用户也可以在您 GET 和 DELETE 之间删除较早的索引。你最终会影响错误的记录。
猜你喜欢
  • 2015-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-26
  • 1970-01-01
  • 2018-10-15
  • 1970-01-01
相关资源
最近更新 更多