我想为下面的场景使用适当的方法设计rest端点。
有一群人。每个组都有一个地位。群组可以由管理员激活或灭活。
我应该把终点设计成这样吗
PUT /groups/api/v1/groups/{group id}/status/activate
OR
PATCH /groups/api/v1/groups/{group id}
with request body like
{action:activate|deactivate}
我想为下面的场景使用适当的方法设计rest端点。
有一群人。每个组都有一个地位。群组可以由管理员激活或灭活。
我应该把终点设计成这样吗
PUT /groups/api/v1/groups/{group id}/status/activate
OR
PATCH /groups/api/v1/groups/{group id}
with request body like
{action:activate|deactivate}
当前回答
REST中的R代表资源
(这是不正确的,因为它代表具象的,但是记住REST中的资源的重要性是一个很好的技巧)。
关于PUT /groups/api/v1/groups/{group id}/status/activate:你没有更新一个“activate”。“activate”不是一个东西,它是一个动词。动词从来不是好的资源。一个经验法则:如果动作(动词)在URL中,那么它可能不是RESTful的。
你现在在做什么?您可以“添加”,“删除”或“更新”组上的激活,或者如果您喜欢:操作组上的“状态”资源。就我个人而言,我会使用“激活”,因为它们比“状态”概念更模糊:创建状态是模糊的,创建激活则不是。
POST /groups/{group id}/activation Creates (or requests the creation of) an activation. PATCH /groups/{group id}/activation Updates some details of an existing activation. Since a group has only one activation, we know what activation-resource we are referring to. PUT /groups/{group id}/activation Inserts-or-replaces the old activation. Since a group has only one activation, we know what activation-resource we are referring to. DELETE /groups/{group id}/activation Will cancel, or remove the activation.
当“激活”Group有副作用时,例如支付、发送邮件等,此模式非常有用。只有POST和PATCH会有这样的副作用。例如,当删除激活需要通过邮件通知用户时,DELETE不是正确的选择;在这种情况下,你可能想要创建一个去激活资源:POST /groups/{group_id}/deactivation。
It is a good idea to follow these guidelines, because this standard contract makes it very clear for your clients, and all the proxies and layers between the client and you, know when it is safe to retry, and when not. Let's say the client is somewhere with flaky wifi, and its user clicks on "deactivate", which triggers a DELETE: If that fails, the client can simply retry, until it gets a 404, 200 or anything else it can handle. But if it triggers a POST to deactivation it knows not to retry: the POST implies this. Any client now has a contract, which, when followed, will protect against sending out 42 emails "your group has been deactivated", simply because its HTTP-library kept retrying the call to the backend.
更新单个属性:使用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是幂等的:如果您在更新Group(或更改激活)时需要副作用,您应该使用PATCH。所以,当更新导致发送邮件时,不要使用PUT。
其他回答
REST中的R代表资源
(这是不正确的,因为它代表具象的,但是记住REST中的资源的重要性是一个很好的技巧)。
关于PUT /groups/api/v1/groups/{group id}/status/activate:你没有更新一个“activate”。“activate”不是一个东西,它是一个动词。动词从来不是好的资源。一个经验法则:如果动作(动词)在URL中,那么它可能不是RESTful的。
你现在在做什么?您可以“添加”,“删除”或“更新”组上的激活,或者如果您喜欢:操作组上的“状态”资源。就我个人而言,我会使用“激活”,因为它们比“状态”概念更模糊:创建状态是模糊的,创建激活则不是。
POST /groups/{group id}/activation Creates (or requests the creation of) an activation. PATCH /groups/{group id}/activation Updates some details of an existing activation. Since a group has only one activation, we know what activation-resource we are referring to. PUT /groups/{group id}/activation Inserts-or-replaces the old activation. Since a group has only one activation, we know what activation-resource we are referring to. DELETE /groups/{group id}/activation Will cancel, or remove the activation.
当“激活”Group有副作用时,例如支付、发送邮件等,此模式非常有用。只有POST和PATCH会有这样的副作用。例如,当删除激活需要通过邮件通知用户时,DELETE不是正确的选择;在这种情况下,你可能想要创建一个去激活资源:POST /groups/{group_id}/deactivation。
It is a good idea to follow these guidelines, because this standard contract makes it very clear for your clients, and all the proxies and layers between the client and you, know when it is safe to retry, and when not. Let's say the client is somewhere with flaky wifi, and its user clicks on "deactivate", which triggers a DELETE: If that fails, the client can simply retry, until it gets a 404, 200 or anything else it can handle. But if it triggers a POST to deactivation it knows not to retry: the POST implies this. Any client now has a contract, which, when followed, will protect against sending out 42 emails "your group has been deactivated", simply because its HTTP-library kept retrying the call to the backend.
更新单个属性:使用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是幂等的:如果您在更新Group(或更改激活)时需要副作用,您应该使用PATCH。所以,当更新导致发送邮件时,不要使用PUT。
由于您希望使用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体系结构样式的应用程序状态条件的引擎。
实现这种行为的一个可能选项是
PUT /groups/api/v1/groups/{group id}/status
{
"Status":"Activated"
}
显然,如果有人需要停用它,PUT将在JSON中具有Deactivated状态。
在需要大规模激活/去激活的情况下,PATCH可以进入游戏(不是针对特定的群体,而是针对群体资源:
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建议的想法,但在具体实现上略有变化。
我通常更喜欢一些简单的东西,比如激活/取消激活子资源(通过rel=service的Link头链接)。
POST /groups/api/v1/groups/{group id}/activate
or
POST /groups/api/v1/groups/{group id}/deactivate
对于消费者来说,这个接口非常简单,并且它遵循REST原则,不会让您陷入将“激活”概念化为单个资源的困境。
我建议使用PATCH,因为您的资源“组”有许多属性,但在这种情况下,您只更新激活字段(部分修改)
根据RFC5789 (https://www.rfc-editor.org/rfc/rfc5789)
现有的HTTP PUT方法只允许完全替换 一个文档。这个建议添加了一个新的HTTP方法PATCH来修改 现有的HTTP资源。
此外,更详细地说,
The difference between the PUT and PATCH requests is reflected in the way the server processes the enclosed entity to modify the resource identified by the Request-URI. In a PUT request, the enclosed entity is considered to be a modified version of the resource stored on the origin server, and the client is requesting that the stored version be replaced. With PATCH, however, the enclosed entity contains a set of instructions describing how a resource currently residing on the origin server should be modified to produce a new version. The PATCH method affects the resource identified by the Request-URI, and it also MAY have side effects on other resources; i.e., new resources may be created, or existing ones modified, by the application of a PATCH. PATCH is neither safe nor idempotent as defined by [RFC2616], Section 9.1.
Clients need to choose when to use PATCH rather than PUT. For example, if the patch document size is larger than the size of the new resource data that would be used in a PUT, then it might make sense to use PUT instead of PATCH. A comparison to POST is even more difficult, because POST is used in widely varying ways and can encompass PUT and PATCH-like operations if the server chooses. If the operation does not modify the resource identified by the Request- URI in a predictable way, POST should be considered instead of PATCH or PUT.
PATCH的响应代码为
使用204响应码是因为响应不携带 消息体(带有200代码的响应将具有此消息体)。请注意 其他成功代码也可以被使用。
也可以参考thttp://restcookbook.com/HTTP%20Methods/patch/
警告:实现PATCH的API必须原子地打补丁。绝对不能 当GET请求资源时,资源可能打了一半补丁。