更新 RESTful 资源中的复合实体

Updating composite entities in a RESTful resource

我有一个具有多个属性的实体,比如 «project»。除了简单的属性外,项目可能有一个“状态”列表,其中最后一个是当前状态。我有一个 create/edit 项目的 Web 表单。这个项目的所有属性都可以在这个表单中更改,用户也可以为项目添加新的状态(但不能更改或删除旧状态)。

项目状态是纯粹的复合实体,它们在项目范围之外没有任何独特的意义或标识,并且不能直接处理它们,因此它们显然不值得特殊的根 REST 资源。

根据 REST 架构,我创建了一个名为 /projects 的资源。 POST用于创建新项目,PUT用于更改现有项目。

但是,我不希望客户端将项目及其所有历史状态PUT在一起,一是因为这个集合太重了,二是因为业务逻辑只允许添加状态,不能更改或删除它们, 因此将项目及其所有状态放在一起没有任何意义。

PUT 仅具有新状态的项目也不是一种选择,因为它违反了 PUT 的幂等性。

我也不喜欢在第二个 HTTP 请求中 POSTing 状态的想法,比如 /project/{id}/status,因为这会破坏更新操作的原子性用户的立场。如果第二个请求在网络上丢失,那么项目将与编辑它的用户不一致(属性已更改,但状态保持不变)。创建 RESTful "transactions" 对于这个更新看似单一的实体的简单任务来说似乎有点矫枉过正(而且容易出错)。

这种问题在我的工作中非常普遍,并且可以概括为:什么是 RESTful 更新复杂复合实体的正确且原子的方式,其中只允许部分更新业务逻辑?

您需要 HTTP PATCH 吗?它是表示对资源的增量更新的动词。

https://www.rfc-editor.org/rfc/rfc5789

我认为如果你想做部分更新(实际上是你的情况),你应该使用方法PATCH。这允许更新没有依赖项(状态)的项目或没有项目提示的依赖项。

您会注意到有一种格式来描述方法中要执行的操作 PATCH。它叫做 JSON 补丁(参见 https://www.rfc-editor.org/rfc/rfc6902)。这种格式描述了您想在请求中执行的操作:添加元素、更新元素、删除元素...

我认为如果你想(例如)更新特定项目的名称,删除状态(这也是一个示例,因为我读到你想禁止这样做!),你可以有类似的东西!在一个原子请求中添加一个新的:

PATCH /projects/1
[
    {
        "op": "replace",
        "path": "/name",
        "value": "the new name of the project"
    },
    {
        "op": "remove",
        "path": "/statuses/1"
    },
    {
        "op": "add",
        "path": "/statuses/",
        "value": {
           "name": "my status",
          (...)
        }
    }
]

请注意,您可以在属性 name 中放入您想要的内容,以标识资源状态中的相关元素。所以 /statuses/1 可以是数组中的第二个元素,id 值为 1 的状态或其他。

请求的服务器端处理可以是原子的。

我写了一篇关于批量更新的博客 post:https://templth.wordpress.com/2015/05/14/implementing-bulk-updates-within-restful-services/。我认为“实施批量更新”部分可能符合您的要求。

希望对你有帮助, 蒂埃里