我应该在我的REST API中使用PATCH还是PUT ?

我想为下面的场景使用适当的方法设计rest端点。

有一群人。每个组都有一个地位。群组可以由管理员激活或灭活。

我应该把终点设计成这样吗

PUT /groups/api/v1/groups/{group id}/status/activate

PATCH /groups/api/v1/groups/{group id}


with request body like
{action:activate|deactivate}
230996 次浏览

在这里,当您更新现有资源(组ID)时,PATCH方法是正确的选择。PUT只能在替换是一个完整的资源时使用。

关于部分资源修改的更多信息可以在RFC 5789中找到。具体来说,PUT方法的描述如下:

扩展超文本传输协议的几个应用程序 (HTTP)需要一个特性来做部分资源修改。的 现有的HTTP PUT方法只允许完全替换 文档。此提议添加了一个新的HTTP方法PATCH,用于修改

由于您希望使用REST体系结构风格设计API,因此需要考虑您的用例,以决定哪些概念足够重要,需要作为资源公开。如果你决定将一个组的状态作为子资源公开,你可以给它下面的URI,并实现对GET和PUT方法的支持:

/groups/api/groups/{group id}/status

与PATCH修改相比,这种方法的缺点是不能以原子方式和事务方式对组的多个属性进行更改。如果事务性更改很重要,那么使用PATCH。

如果您决定将状态公开为组的子资源,那么它应该是组表示中的一个链接。例如,如果代理获得组123并接受XML,响应体可能包含:

<group id="123">
<status>Active</status>
<link rel="/linkrels/groups/status" uri="/groups/api/groups/123/status"/>
...
</group>

需要一个超链接来满足REST架构风格的超媒体作为应用状态的引擎条件。

REST中的R代表资源

(这是不正确的,因为它代表具象的,但是记住REST中的资源的重要性是一个很好的技巧)。

关于PUT /groups/api/v1/groups/{group id}/status/activate:你正在更新一个“activate”。“activate”不是一个东西,它是一个动词。动词从来不是好的资源。一个经验法则:如果动作(一个动词)在URL中,它可能不是RESTful的

你现在在做什么?你可以“添加”,“删除”或“更新”组上的激活,或者如果你喜欢:操作组上的“状态”资源。就我个人而言,我会使用“激活”,因为它们比“状态”概念更模糊:创建状态是模糊的,创建激活则不是。

  • POST /groups/{group id}/activation创建(或请求创建)激活。
  • PATCH /groups/{group id}/activation更新现有激活的一些细节。因为一个组只有一个激活,所以我们知道我们所指的激活资源是什么。
  • PUT /groups/{group id}/activation插入或替换旧的激活。因为一个组只有一个激活,所以我们知道我们所指的激活资源是什么。
  • DELETE /groups/{group id}/activation将取消或删除激活。

当“激活”Group有副作用时,例如支付、发送邮件等,此模式非常有用。只有POST和PATCH会有这样的副作用。例如,当删除激活需要通过邮件通知用户时,DELETE不是正确的选择;在这种情况下,你可能需要创建去激活资源: POST /groups/{group_id}/deactivation

遵循这些指导方针是一个好主意,因为这个标准合同让你的客户端以及客户端和你之间的所有代理和层非常清楚地知道什么时候重试是安全的,什么时候不安全。假设客户端在某个wifi不稳定的地方,它的用户单击“deactivate”,这会触发DELETE:如果失败,客户端可以简单地重试,直到它得到404、200或其他它可以处理的东西。但是如果它触发了POST to deactivation,它知道不要重试:POST暗示了这一点 任何客户端现在都有一个合同,当遵循该合同时,将防止发送42封电子邮件“您的组已被停用”,仅仅是因为其http库不断重试到后端调用

更新单个属性:使用PATCH

PATCH /groups/{group id}

如果您希望更新一个属性。例如,“status”可以是一个可以设置的Groups属性。像“status”这样的属性通常适合限制在值的白名单中。下面的例子使用了一些未定义的json方案:

PATCH /groups/{group id} { "attributes": { "status": "active" } }
response: 200 OK


PATCH /groups/{group id} { "attributes": { "status": "deleted" } }
response: 406 Not Acceptable

替换资源,没有副作用使用PUT。

PUT /groups/{group id}

如果您希望替换整个组。这并不意味着服务器实际上创建了一个新的组,并将旧的组丢弃,例如,id可能保持不变。但是对于客户端,这就是PUT 可以的意思:客户端应该假设他得到了一个全新的项目,基于服务器的响应。

PUT请求的情况下,客户端应该始终发送整个资源,拥有创建新项所需的所有数据:通常与POST-create所需要的数据相同。

PUT /groups/{group id} { "attributes": { "status": "active" } }
response: 406 Not Acceptable


PUT /groups/{group id} { "attributes": { "name": .... etc. "status": "active" } }
response: 201 Created or 200 OK, depending on whether we made a new one.

一个非常重要的要求是PUT是幂等的:如果你在更新一个组(或改变一个激活)时需要副作用,你应该使用PATCH。因此,当更新导致发送邮件时,不要使用PUT

我建议使用PATCH,因为您的资源“组”有许多属性,但在这种情况下,您只更新激活字段(部分修改)

根据RFC5789 (https://www.rfc-editor.org/rfc/rfc5789)

现有的HTTP PUT方法只允许完全替换 一个文档。这个建议添加了一个新的HTTP方法PATCH来修改

.一个已存在的HTTP资源

此外,更详细地说,

PUT请求和PATCH请求的区别体现在 服务器处理封闭实体以修改资源
的方式 由Request-URI标识。在PUT请求中,所包含的实体 被视为存储在
源服务器,客户端请求存储的版本
被取代。然而,对于PATCH,所包含的实体包含一个集合 描述资源当前如何驻留在
应修改原服务器以生成新版本。补丁 方法影响Request-URI标识的资源,并且它
也可能对其他资源产生副作用;例如,new resources
可以通过
补丁。< / p >

PATCH既不是安全的,也不是[RFC2616]定义的幂等的, 部分 9.1 . < / p >

客户端需要选择何时使用PATCH而不是PUT。对
例如,补丁文件的大小大于
. b0的大小 将在PUT中使用的新资源数据,则它可能使
使用PUT而不是PATCH是明智的。与POST的比较甚至更多 很难,因为POST的使用方式多种多样,并且可以
如果服务器选择,包含PUT和类似补丁的操作。如果
该操作不会修改由Request-标识的资源 应该考虑POST而不是PATCH
或者把。< / p >

PATCH的响应代码为

使用204响应码,因为该响应不携带 消息体(带有200代码的响应将具有此消息体)。请注意

也可以参考thttp://restcookbook.com/HTTP%20Methods/patch/

注意:实现PATCH的API必须自动打补丁。绝对不能

.当GET请求资源时,资源可能打了一半补丁

我通常更喜欢一些更简单的东西,比如activate/deactivate子资源(由Link报头与rel=service链接)。

POST /groups/api/v1/groups/{group id}/activate

POST /groups/api/v1/groups/{group id}/deactivate

对于消费者来说,这个接口非常简单,并且它遵循REST原则,不会让您陷入将“激活”概念化为单个资源的困境。

实现这种行为的一个可能选项是

PUT /groups/api/v1/groups/{group id}/status
{
"Status":"Activated"
}

显然,如果有人需要停用它,PUT将在JSON中具有Deactivated状态。

在需要大规模激活/去激活的情况下,PATCH可以进入游戏(不是确切的组,而是groups资源:

PATCH /groups/api/v1/groups
{
{ “op”: “replace”, “path”: “/group1/status”, “value”: “Activated” },
{ “op”: “replace”, “path”: “/group7/status”, “value”: “Activated” },
{ “op”: “replace”, “path”: “/group9/status”, “value”: “Deactivated” }
}

总的来说,这是@Andrew Dobrowolski建议的想法,但在具体实现上略有变化。