在REST中更新资源且不影响子资源的正确HTTP方法是什么?

3
假设我有两个实体-项目团队和员工。每个员工都可以是多个团队的一部分,每个团队可以拥有多个员工作为团队成员。 我需要提供REST API来操作团队,员工以及它们之间的关系。
我已经确定了3个资源-团队、员工和成员(团队和员工之间的关联), 成员是团队的子资源。我选择将成员作为子资源的原因纯粹基于该资源的生命周期。每当团队被删除时,成员也会被删除,因为它们在团队之外没有意义。
我公开以下API(相关的API):
- POST /teams:创建名称、部门ID等新的团队记录。 - POST /teams/{name}/members:创建标识为名称的团队和特定员工之间的关联,因此输入数据包含员工ID。
我还需要提供API来更新团队的部门ID和其他属性。看起来PUT是自然的选择,但PUT的语义非常清晰——我必须替换整个资源,这在这种情况下意味着替换所有成员子资源。
当我只想更新团队属性同时保留成员关联的时候应该使用什么方法(或方法)?请记住,我还希望这个请求是幂等的。
3个回答

5
看起来PUT是一个自然的选择,但PUT的语义非常清晰——我必须替换整个资源,这在这种情况下意味着替换所有成员子资源。
我从未听说过有人做出这种关联。如果我在PUT /Foo中进行操作,我认为它对/Foo/bar绝对没有任何影响。仅仅因为资源可以通过分层的URI空间访问,并不意味着这些资源之间存在任何其他关系。
我听说过人们做相反的情况,即你可以在PUT /Foo/bar中进行操作,如果服务器知道这将影响/Foo的状态,你可以包含一个Content-Location头,指向/Foo,以允许智能缓存使/Foo无效。然而,Content-Location需要明确地创建两个资源之间的关系。

这是非常好的观点。我认为你说得对,我应该将URI视为资源集合的严格树形表示,而不是仅仅作为一种标识方式,其中嵌套意味着命名空间的规范。顺便说一下,如果我按照这个假设去做,"team"的GET响应会是什么样子?在正文中返回成员的URI是一个好主意吗?还是只返回团队属性并省略成员?也许包括像"/teams/123/members"这样的URI是一个好方法? - oiavorskyi
@Oleg,我会在您的团队表示中包含一个到/teams/123/members的链接。 - Darrel Miller
谢谢,我还需要澄清一个问题。当对 /teams/123 进行 PUT 操作时,是否应该省略将链接 /teams/123/members 包含在请求体中,还是这会让客户端感到困惑? - oiavorskyi
2
@Oleg 表示,客户端无法控制的表示元素(例如超媒体链接)可以安全地从 PUT 请求正文中省略。 - Darrel Miller

2
Ideally,你应该使用 PATCH 方法,但不确定哪个实现使用它。如果没有,请采用直接 GET -> 本地修改 -> PUT 循环方式。
另外,根据你的设计,你可能会解释子资源不是资源本身的一部分。例如,团队资源的内容可能包含到资源成员列表的链接,例如“/team/{name}/members”,但没有整个列表作为一个包含元素。

问题在于使用GET->修改->PUT将会显著复杂化服务器上的逻辑。它必须确定哪些关联已更改并相应地执行后端操作。我宁愿在方法和正文中传达仅更新团队属性的意图。 - oiavorskyi
1
另一方面,不将成员视为单独的资源可能是个好主意。因此,如果有人想要获取成员列表,就必须发出单独的请求,而对“团队”URI的POST/PUT操作将始终仅操作团队属性。 - oiavorskyi

-2

我有点觉得你已经回答了自己的问题。PUT 意味着创建一个资源,使用 POST 进行更新。

另一种看待这个问题的方式是,对资源所做的每个更改都实际上是在 "创建新版本" 的资源。每次创建、更新和删除都会添加一个具有新属性的新版本,并且该版本具有其自己的唯一标识符。当您 PUT 现有资源的新版本时,旧资源仍然存在,以便新资源可以继承自它。尝试在较新的版本已经存在的情况下将新版本 PUT 到旧版本上会返回重定向而不是成功,并且某些 GET 请求也会返回重定向(指示它们实际上正在查找旧版本)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接