REST API PATCH或PUT

时间:2014-06-16 10:44:47

标签: http rest http-put http-method http-patch

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

有一个小组。每个组都有一个状态。该组可以由管理员激活或取消激活。

我应该将我的终点设计为

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

OR

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

with request body like 
{action:activate|deactivate}

6 个答案:

答案 0 :(得分:284)

当您更新现有资源(组ID)时,PATCH方法是正确的选择。只有在完全替换资源时才应使用PUT

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

  

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

答案 1 :(得分:159)

REST中的 R 代表资源

(事实并非如此,因为它代表Representational,但它是一个很好的技巧,可以记住资源在REST中的重要性。)

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

你在做什么呢?要么在组中“添加”,“删除”或“更新”激活,要么您更喜欢:在组上操作“status”-resource。就个人而言,我会使用“激活”,因为它们比“状态”概念更不明确:创建状态是模糊的,创建激活不是。

  • 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}

如果您想更新属性。例如。 “status”可以是可以设置的Groups的属性。诸如“状态”之类的属性通常是限制值列入白名单的良好候选者。示例使用一些未定义的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}

如果您想要更换整个组。这并不一定意味着服务器实际上创建了一个新组并抛出旧组,例如ids可能保持不变。但是对于客户来说,PUT 可以意味着什么:客户应该假设他根据服务器的响应获得了一个全新的项目。

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

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

答案 2 :(得分:11)

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

根据RFC5789(https://tools.ietf.org/html/rfc5789

  

现有的HTTP PUT方法只允许完全替换   一个文件。此提议添加了一个新的HTTP方法PATCH进行修改   现有的HTTP资源。

另外,更多细节,

  

PUT和PATCH请求之间的区别反映在   服务器处理封闭实体以修改资源的方式   由Request-URI标识。在PUT请求中,包含的实体   被认为是存储在
的资源的修改版本   原始服务器,客户端请求存储的版本
  被替换。但是,使用PATCH,随附的实体包含一个集合   描述资源当前如何驻留在中的说明   应修改原始服务器以生成新版本。 PATCH   方法影响Request-URI标识的资源,并且它   也可能对其他资源产生副作用;即新资源
  可以通过应用a来创建或修改现有的   PATCH。

     

PATCH既不是[RFC2616]定义的安全也不是幂等的,   部分      9.1。

     

客户端需要选择何时使用PATCH而不是PUT。对于
  例如,如果补丁文档大小大于
的大小   将在PUT中使用的新资源数据,然后它可能使得   感觉使用PUT而不是PATCH。与POST的比较甚至更多   很难,因为POST的使用方式多种多样,而且可以   如果服务器选择,则包含PUT和PATCH类操作。如果
  该操作不会修改请求所标识的资源 -   以可预测的方式使用URI,应该考虑POST而不是PATCH
  或PUT。

PATCH的响应代码是

  

使用204响应代码,因为响应不带有      消息体(具有200代码的响应)。注意      其他成功代码也可以使用。

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

  

警告:实现PATCH的API必须以原子方式进行修补。它绝不可以   当GET请求时,可能会对资源进行半修补。

答案 3 :(得分: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架构风格的hypermedia as the engine of application state条件。

答案 4 :(得分:0)

我通常希望使用一些更简单的东西,例如activate / deactivate子资源(由Link标题与rel=service链接)。

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

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

对于消费者而言,该接口非常简单,它遵循REST原理,不会使您陷入将“激活”概念化为单独资源的麻烦。

答案 5 :(得分:0)

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

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提出的想法,但在实际实现上略有变化。