ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

我应该将终点设计为

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

或者

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

with request body like 
{action:activate|deactivate}
两者都很好。但是请查看 JSON PATCH 格式 (tools.ietf.org/html/rfc6902) 的 RFC。 PATCH 期望为有效负载获取某种差异/补丁文档(原始 JSON 不是其中之一)。
@JørnWildt 不,PUT 将是一个可怕的选择。你在那儿放什么? PATCH 是唯一明智的选择。好吧,在这种情况下,您可以使用问题中提供的 PATCH 格式,而只需使用 PUT 方法; PUT 示例是错误的。
将一个或多个属性公开为客户端可以使用 PUT 获取和修改的独立资源并没有错。但是,是的,URL 应该是 /groups/api/v1/groups/{group id}/status 您可以将“活动”或“非活动”或 GET 放入其中以读取当前状态。
下面是关于如何真正使用 PATCH 的一个很好的解释:williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot
activate”不是充分的 RESTful 结构。您可能正在尝试将 status 更新为“活动”或“停用”。在这种情况下,您可以使用正文中的“active”或“deactive”字符串修补到 .../status。或者,如果您尝试更新 status.active 处的布尔值,您可以使用正文中的布尔值 PATCH 到 .../status/active

C
Community

当您更新现有资源 - 组 ID 时,PATCH 方法是正确的选择。仅当您替换整个资源时才应使用 PUT

RFC 5789 中提供了有关部分资源修改的更多信息。具体而言,PUT方法描述如下:

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


公平地说,您可以将字符串“激活”或“停用”放到资源中。因为(似乎)只有一件事可以切换,所以完全替换它并不是什么大不了的事。它确实允许(微不足道的)较小的请求。
值得注意的是,RFC 5789 仍处于提案阶段,尚未被正式接受,目前被标记为“存在错误”。这种“最佳实践”备受争议,从技术上讲,PATCH 还不是 HTTP 标准的一部分。
几年后只是我的 2 美分:您可以将状态本身视为一种资源,如果是这样,对 /status 使用 PUT 在技术上将替换该端点的状态资源。
我敢于反对这些文档,即使它是“最”的 RFC。文档声明您应该使用 PATCH 仅修改资源的一部分,但它忽略了 PATCH 方法被定义为非幂等方法的重要内容。为什么?如果创建 PUT 方法时考虑到整个资源的更新/替换,那么为什么不将 PATCH 方法创建为像 PUT 这样的幂等方法,如果它的目的只是更新资源的一部分?在我看来,更新的幂等性看起来更像是“a=5”(PUT)和“a=a+5”(PATCH)。两者都可以更新整个资源。
j
jhhwilliams

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


这对我很有帮助。 “当一个组的“激活”有副作用时,这种模式很有用” - 这种模式为什么有用,特别是关于动作何时有副作用,而不是 OP 初始端点
@Abdul,该模式很有用,原因有很多,但是有副作用,客户应该很清楚一个动作有什么影响。比如说,当一个 iOS 应用决定将整个地址簿作为“联系人”发送时,应该非常清楚联系人的创建、更新、删除等有什么副作用。例如,为了避免大量邮寄所有联系人。
在 RESTfull 中,PUT 还可以更改实体身份 - 例如可能导致并行请求失败的 PrimaryKey ID。 (例如更新整个实体需要删除一些行并添加新的行,从而创建新的实体)其中 PATCH 绝不能做到这一点,允许无限数量的 PATCH 请求而不影响其他“应用程序”
很有帮助的答案。谢谢!我还要添加一条评论,就像在 Luke 的回答中一样,指出 PUT/PATCH 之间的区别不仅仅是整体/部分更新,它也是幂等性的不同。这不是一个错误,这是一个有意的决定,我认为在决定 HTTP 方法的使用时,没有多少人会考虑到这一点。
我同意和不同意。 RESTful API 不应反映您的域。他们倾向于对应用程序的用例进行建模,而不是对业务进行建模。 RESTful api 遵循 RFC 2616 是有原因的。作为消费者,我不知道您的业务运营的“副作用”是什么。我所知道的是您的 HTTP VERBS 应该反映对 RESOURCE 的操作。因此,如果 DELETE 是幂等的,则意味着对 RESOURCE 的操作是幂等的。不是“副作用”。发送电子邮件不违反“幂等性”。这是一个商业问题,而不是 RESTful api。
C
Community

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

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

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

此外,更详细地说,

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


A
Andrew Dobrowolski

由于您想使用 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 条件。


I
Ivan Sokalskiy

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

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 建议的想法,但在具体实现上略有变化。


r
rich remer

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

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

或者

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

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