更新 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 吗?它是表示对资源的增量更新的动词。
我认为如果你想做部分更新(实际上是你的情况),你应该使用方法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/。我认为“实施批量更新”部分可能符合您的要求。
希望对你有帮助,
蒂埃里
我有一个具有多个属性的实体,比如 «project»。除了简单的属性外,项目可能有一个“状态”列表,其中最后一个是当前状态。我有一个 create/edit 项目的 Web 表单。这个项目的所有属性都可以在这个表单中更改,用户也可以为项目添加新的状态(但不能更改或删除旧状态)。
项目状态是纯粹的复合实体,它们在项目范围之外没有任何独特的意义或标识,并且不能直接处理它们,因此它们显然不值得特殊的根 REST 资源。
根据 REST 架构,我创建了一个名为 /projects 的资源。 POST用于创建新项目,PUT用于更改现有项目。
但是,我不希望客户端将项目及其所有历史状态PUT在一起,一是因为这个集合太重了,二是因为业务逻辑只允许添加状态,不能更改或删除它们, 因此将项目及其所有状态放在一起没有任何意义。
PUT 仅具有新状态的项目也不是一种选择,因为它违反了 PUT 的幂等性。
我也不喜欢在第二个 HTTP 请求中 POSTing 状态的想法,比如 /project/{id}/status,因为这会破坏更新操作的原子性用户的立场。如果第二个请求在网络上丢失,那么项目将与编辑它的用户不一致(属性已更改,但状态保持不变)。创建 RESTful "transactions" 对于这个更新看似单一的实体的简单任务来说似乎有点矫枉过正(而且容易出错)。
这种问题在我的工作中非常普遍,并且可以概括为:什么是 RESTful 更新复杂复合实体的正确且原子的方式,其中只允许部分更新业务逻辑?
您需要 HTTP PATCH 吗?它是表示对资源的增量更新的动词。
我认为如果你想做部分更新(实际上是你的情况),你应该使用方法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/。我认为“实施批量更新”部分可能符合您的要求。
希望对你有帮助, 蒂埃里