首先,一些定义:

PUT的定义见章节9.6 RFC 2616:

PUT方法要求将所包含的实体存储在所提供的Request-URI下。如果Request-URI引用了一个已经存在的资源,那么所包含的实体应该被认为是原始服务器上的实体的修改版本。如果Request-URI不指向现有资源,并且请求用户代理能够将该URI定义为新资源,则源服务器可以使用该URI创建资源。

PATCH在RFC 5789中定义:

方法中描述的一组更改 请求实体应用于由request -标识的资源 URI。

此外,根据RFC 2616节9.1.2 PUT是等幂的,而PATCH不是。

现在让我们看一个真实的例子。当我用数据{用户名:'skwee357',电子邮件:'skwee357@domain.example'} POST到/users时,服务器能够创建资源,它将响应201和资源位置(让我们假设/users/1),任何下一次调用GET /users/1将返回{id: 1,用户名:'skwee357',电子邮件:'skwee357@domain.example'}。

现在假设我想修改我的电子邮件。邮件修改被认为是“一组更改”,因此我应该用“补丁文档”PATCH /users/1。在我的例子中,它将是JSON文档:{email: 'skwee357@newdomain.example'}。然后服务器返回200(假设权限正常)。这让我想到了第一个问题:

PATCH不是等幂的。RFC 2616和RFC 5789都是这么说的。但是,如果我发出相同的PATCH请求(用我的新电子邮件),我将获得相同的资源状态(我的电子邮件被修改为所请求的值)。为什么PATCH不是幂等的?

PATCH是一个相对较新的动词(RFC于2010年3月引入),它用来解决“修补”或修改一组字段的问题。在PATCH引入之前,每个人都使用PUT来更新资源。但是在引入PATCH之后,它让我对PUT的用途感到困惑。这就引出了我的第二个(也是主要的)问题:

What is the real difference between PUT and PATCH? I have read somewhere that PUT might be used to replace entire entity under specific resource, so one should send the full entity (instead of set of attributes as with PATCH). What is the real practical usage for such case? When would you like to replace / overwrite an entity at a specific resource URI and why is such an operation not considered updating / patching the entity? The only practical use case I see for PUT is issuing a PUT on a collection, i.e. /users to replace the entire collection. Issuing PUT on a specific entity makes no sense after PATCH was introduced. Am I wrong?


当前回答

这里有一个很好的解释

https://blog.segunolalive.com/posts/restful-api-design-%E2%80%94-put-vs-patch/: ~:文本= RFC % 205789,而不是% 20要求% 20 % 20 % 20幂等。

〇正常载荷 // 1号地块上的房子 { 地址:“地块1” 老板:“根据”, 类型:“双”, 颜色:“绿色”, 房间:“5”, 厨房:' 1 ', windows: 20 } PUT For Updated- // PUT请求有效载荷来更新plot 1上House的窗口 { 地址:“地块1” 老板:“根据”, 类型:“双”, 颜色:“绿色”, 房间:“5”, 厨房:' 1 ', windows: 21 } 注:在上述有效载荷中,我们试图将窗口从20更新到21。

现在查看PATH有效负载- //补丁请求有效载荷来更新House上的窗口 { windows: 21 }

由于PATCH不是幂等的,失败的请求不会自动在网络上重新尝试。此外,如果PATCH请求是对一个不存在的url,例如试图替换一个不存在的建筑物的前门,它应该只是失败,而不像PUT那样创建一个新的资源,它会使用有效载荷创建一个新的资源。仔细想想,在一个住宅地址上只有一扇门是很奇怪的。

其他回答

我对此也很好奇,并找到了一些有趣的文章。我可能不能完全回答你的问题,但至少提供了一些信息。

http://restful-api-design.readthedocs.org/en/latest/methods.html

HTTP RFC指定PUT必须接受一个全新的资源 表示为请求实体。这意味着如果举个例子 只提供了某些属性,这些属性应该被删除(即设置 为null)。

鉴于此,PUT应该发送整个对象。例如,

/users/1
PUT {id: 1, username: 'skwee357', email: 'newemail@domain.example'}

这将有效地更新电子邮件。PUT可能不太有效的原因是,只修改一个字段并包括用户名是无用的。下一个例子显示了区别。

/users/1
PUT {id: 1, email: 'newemail@domain.example'}

现在,如果PUT是根据规范设计的,那么PUT将把用户名设置为null,您将得到以下结果。

{id: 1, username: null, email: 'newemail@domain.example'}

当您使用PATCH时,您只更新您指定的字段,其余的保持不变。

以下对PATCH的看法与我以前从未见过的略有不同。

http://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/

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 /users/123

[
    { "op": "replace", "path": "/email", "value": "new.email@example.org" }
]

您或多或少地将PATCH视为更新字段的一种方式。所以你发送的不是部分对象,而是操作。例如,用价值取代电子邮件。

文章的结尾是这样的。

值得一提的是PATCH并不是真正为REST设计的 api,因为Fielding的论文没有定义任何方法来部分 修改资源。但是Roy Fielding自己说PATCH是 这是他为最初的HTTP/1.1提案创建的,因为 partial PUT从来不是RESTful的。确定你没有转移一个完整的 表示,但是REST不需要表示 完成了。

现在,我不知道我是否像许多评论员指出的那样特别同意这篇文章。发送部分表示可以很容易地成为更改的描述。

对我来说,我不太喜欢使用PATCH。在大多数情况下,我将把PUT视为PATCH,因为到目前为止我注意到的唯一真正的区别是PUT“应该”将缺失的值设置为null。这可能不是“最正确”的方法,但祝你好运,能写出完美的代码。

PUT和PATCH的区别在于:

PUT必须是等幂的。为了实现这一点,您必须将整个完整的资源放在请求体中。 PATCH可以是非等幂的。这意味着在某些情况下它也可以是等幂的,比如你描述的情况。

PATCH需要一些“补丁语言”来告诉服务器如何修改资源。调用方和服务器需要定义一些“操作”,如“添加”、“替换”、“删除”。例如:

GET /contacts/1
{
  "id": 1,
  "name": "Sam Kwee",
  "email": "skwee357@olddomain.example",
  "state": "NY",
  "zip": "10001"
}

PATCH /contacts/1
{
 [{"operation": "add", "field": "address", "value": "123 main street"},
  {"operation": "replace", "field": "email", "value": "abc@myemail.example"},
  {"operation": "delete", "field": "zip"}]
}

GET /contacts/1
{
  "id": 1,
  "name": "Sam Kwee",
  "email": "abc@myemail.example",
  "state": "NY",
  "address": "123 main street",
}

而不是使用显式的“操作”字段,补丁语言可以通过定义如下约定使其隐式:

PATCH请求体:

字段的存在意味着“替换”或“添加”该字段。 如果字段的值为空,则表示删除该字段。

使用上述约定,示例中的PATCH可以采用以下形式:

PATCH /contacts/1
{
  "address": "123 main street",
  "email": "abc@myemail.example",
  "zip":
}

这看起来更简洁和用户友好。但是用户需要了解底层的约定。

通过上面提到的运算,PATCH仍然是幂等的。但如果你定义像"increment"或"append"这样的操作,你可以很容易地看到它不再是幂等的。

我将试着用门外汉的语言来总结我所理解的(也许这有帮助)

Patch不是完全等幂的(它可能是在一个理想的情况下,没有人改变你实体的另一个字段)。

在一个不理想的(现实生活)情况下,有人通过另一个补丁操作修改了对象的另一个字段,然后这两个操作都不是幂等的(意味着你都在修改的资源返回“错误”从任何一个角度来看)

所以你不能称它为等幂如果它不能覆盖所有的情况。 也许这对一些人来说不那么重要,但对另一些人来说很重要

考虑到你关于等幂的问题,我可能有点跑题了,但我希望你考虑一下进化论。

假设你有以下元素:

{
  "username": "skwee357",
  "email": "skwee357@domain.example"
}

如果你使用PUT进行修改,你必须给出对象的完整表示:

PUT /users/1
{
  "username": "skwee357",
  "email": "skwee357@newdomain.example"
}

现在更新模式,并添加一个现场电话:

PUT /users/1
{
  "username": "skwee357",
  "email": "skwee357@newdomain.example",
  "phone": "123-456-7890"
}

现在用PUT以同样的方式再次更新它,它将设置phone为null。为了避免这种坏的副作用,您必须在每次更新模式时更新所有修改元素的组件。站不住脚的。

使用PATCH就不会有这个问题,因为PATCH只更新给定的字段。因此,在我看来,您应该使用PATCH来修改一个元素(无论它是否真的是幂等的)。这是现实生活中经验的回归。

这里有一个很好的解释

https://blog.segunolalive.com/posts/restful-api-design-%E2%80%94-put-vs-patch/: ~:文本= RFC % 205789,而不是% 20要求% 20 % 20 % 20幂等。

〇正常载荷 // 1号地块上的房子 { 地址:“地块1” 老板:“根据”, 类型:“双”, 颜色:“绿色”, 房间:“5”, 厨房:' 1 ', windows: 20 } PUT For Updated- // PUT请求有效载荷来更新plot 1上House的窗口 { 地址:“地块1” 老板:“根据”, 类型:“双”, 颜色:“绿色”, 房间:“5”, 厨房:' 1 ', windows: 21 } 注:在上述有效载荷中,我们试图将窗口从20更新到21。

现在查看PATH有效负载- //补丁请求有效载荷来更新House上的窗口 { windows: 21 }

由于PATCH不是幂等的,失败的请求不会自动在网络上重新尝试。此外,如果PATCH请求是对一个不存在的url,例如试图替换一个不存在的建筑物的前门,它应该只是失败,而不像PUT那样创建一个新的资源,它会使用有效载荷创建一个新的资源。仔细想想,在一个住宅地址上只有一扇门是很奇怪的。