322

我想为以下场景使用适当的方法设计我的休息端点。

有一个群。每个组都有一个状态。该组可以由管理员激活或停用。

我应该将终点设计为

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

或者

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

with request body like 
{action:activate|deactivate}
4

6 回答 6

389

PATCH当您更新现有资源 - 组 ID 时, 该方法是此处的正确选择。仅当您要完全替换PUT资源时才应使用。

有关部分资源修改的更多信息,请参见RFC 5789。具体PUT方法如下:

一些扩展超文本传输​​协议 (HTTP) 的应用程序需要一项功能来进行部分资源修改。现有的 HTTP PUT 方法只允许完全替换文档。该提案添加了一个新的 HTTP 方法 PATCH,以修改现有的 HTTP 资源。

于 2014-06-16T10:48:30.570 回答
215

REST 中的R代表资源

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

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

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

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

当组的“激活”具有副作用时,这种模式很有用,例如付款、发送邮件等。只有 POST 和 PATCH 可能有这样的副作用。例如,当删除激活需要通过邮件通知用户时,DELETE 不是正确的选择。在这种情况下,您可能想要创建一个停用资源POST /groups/{group_id}/deactivation

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

更新单个属性:使用 PATCH

PATCH /groups/{group id}

如果您希望更新属性。例如,“状态”可以是组上可以设置的属性。诸如“状态”之类的属性通常是限制值白名单的好选择。示例使用一些未定义的 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.

于 2016-05-31T11:36:40.263 回答
13

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

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

现有的 HTTP PUT 方法只允许完全替换文档。该提案添加了一个新的 HTTP 方法 PATCH,以修改现有的 HTTP 资源。

此外,更详细地说,

PUT 和 PATCH 请求之间的区别体现在服务器处理封闭实体以修改
由 Request-URI 标识的资源的方式上。在 PUT 请求中,包含的实体被认为是存储在源服务器上的资源的修改版本,并且客户端请求 替换
存储的版本。
然而,对于 PATCH,封闭的实体包含一组指令,描述如何
修改当前驻留在源服务器上的资源以生成新版本。PATCH 方法会影响 Request-URI 标识的资源,
也可能对其他资源产生副作用;即,
可以通过应用程序创建新资源或修改现有资源
修补。

正如 [RFC2616] 第 9.1 节所定义的那样,PATCH 既不安全也不幂等。

客户端需要选择何时使用 PATCH 而不是 PUT。例如
,如果补丁文档的大小大于
将在 PUT 中使用的新资源数据的大小,那么
使用 PUT 而不是 PATCH 可能是有意义的。与 POST 的比较更加困难,因为 POST 的使用方式多种多样,
如果服务器选择,它可以包含 PUT 和类似 PATCH 的操作。如果
操作没有以可预测的方式修改由 Request-URI 标识的资源,则应考虑使用 POST 而不是 PATCH
或 PUT。

PATCH 的响应代码是

使用 204 响应代码是因为响应不包含消息正文(带有 200 代码的响应将具有)。请注意,也可以使用其他成功代码。

另请参阅 thttp://restcookbook.com/HTTP%20Methods/patch/

警告:实现 PATCH 的 API 必须自动修补。当 GET 请求时,资源不能被半修补。

于 2016-06-20T12:13:17.447 回答
7

由于您想使用 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 架构风格的应用程序状态条件的引擎。

于 2016-04-19T19:41:30.570 回答
1

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

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

显然,如果有人需要停用它,它将在 JSON 中PUT具有状态。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 建议的想法,但在具体实现上略有变化。

于 2020-01-07T20:54:50.190 回答
0

我通常更喜欢更简单的东西,比如activate/deactivate子资源(由带有 的Link标题链接rel=service)。

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

或者

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

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

于 2018-07-25T00:07:13.550 回答