首先,一些定义:

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?


当前回答

TLDR -简化版

PUT =>设置现有资源的所有新属性。

PATCH =>部分更新现有资源(不需要所有属性)。

其他回答

TLDR -简化版

PUT =>设置现有资源的所有新属性。

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那样创建一个新的资源,它会使用有效载荷创建一个新的资源。仔细想想,在一个住宅地址上只有一扇门是很奇怪的。

为了结束对幂等性的讨论,我应该指出,在REST上下文中可以用两种方式定义幂等性。让我们先确定一些事情:

资源是上域为字符串类的函数。换句话说,资源是String × Any的子集,其中所有的键都是唯一的。让我们将资源的类称为Res。

资源上的REST操作是一个函数f(x: Res, y: Res): Res。REST操作的两个例子是:

PUT(x: Res, y: Res): Res = x, and PATCH(x: Res, y: Res): Res,其工作原理类似PATCH({a: 2}, {a: 1, b: 3}) == {a: 2, b: 3}。

(这个定义是专门为讨论PUT和POST而设计的,例如,在GET和POST上没有多大意义,因为它不关心持久性)。

现在,通过固定x: Res(通俗地说,使用curry), PUT(x: Res)和PATCH(x: Res)是类型为Res→Res的单变量函数。

当g〇g == g时,函数g: Res→Res称为全局幂等的,即对于任意y: Res, g(g(y)) = g(y)。 设x: Res一个资源,k = x.keys。函数g = f(x)称为左幂等,当对于每个y: Res,我们有g(g(y))|ₖ== g(y)|ₖ。它的意思是,如果我们看应用的键,结果应该是一样的。

PATCH(x)不是全局幂等的,而是左幂等的。左等幂是这里重要的东西:如果我们修补了资源的一些键,我们希望这些键在再次修补时是相同的,我们不关心资源的其他部分。

当RFC说PATCH不是等幂的时候,它说的是全局等幂。很好,它不是全局幂等的,否则它就是一个失败的运算。


现在,Jason Hoetger的答案试图证明PATCH甚至不是左幂等的,但它破坏了太多的东西:

First of all, PATCH is used on a set, although PATCH is defined to work on maps / dictionaries / key-value objects. If someone really wants to apply PATCH to sets, then there is a natural translation that should be used: t: Set<T> → Map<T, Boolean>, defined with x in A iff t(A)(x) == True. Using this definition, patching is left idempotent. In the example, this translation was not used, instead, the PATCH works like a POST. First of all, why is an ID generated for the object? And when is it generated? If the object is first compared to the elements of the set, and if no matching object is found, then the ID is generated, then again the program should work differently ({id: 1, email: "me@site.example"} must match with {email: "me@site.example"}, otherwise the program is always broken and the PATCH cannot possibly patch). If the ID is generated before checking against the set, again the program is broken.

我们可以通过打破这个例子中被打破的一半来证明PUT是非等幂的:

带有生成的附加特性的一个例子是版本控制。可以记录单个对象上的更改数量。在这种情况下,PUT不是幂等的:PUT /user/12 {email: "me@site.example"}第一次产生{email: "…",version: 1},第二次产生{email: "…",version: 2}。 如果修改了ID,就可能在每次对象更新时生成一个新ID,从而产生一个非幂等PUT。

以上所有的例子都是人们可能会遇到的自然例子。


我的最后一点是,PATCH不应该是全局幂等的,否则不会给你想要的效果。您希望更改用户的电子邮件地址,而不涉及其余信息,并且不希望覆盖访问同一资源的另一方的更改。

我要补充的另一个信息是,PATCH请求比PUT请求使用更少的带宽,因为只发送了数据的一部分,而不是整个实体。因此,只需使用PATCH请求更新特定的记录(如(1-3条记录)),而使用PUT请求更新更大量的数据。就是这样,不要想太多,也不要太担心。

在我看来,幂等性意味着:

把:

我发送了一个竞争资源定义,因此-结果资源状态与PUT参数所定义的完全相同。每次我用相同的PUT参数更新资源时——结果状态完全相同。

补丁:

我只发送了资源定义的一部分,所以可能会发生其他用户正在更新此资源的other参数。因此,具有相同参数及其值的连续补丁可能导致不同的资源状态。例如:

假设一个对象定义如下:

汽车: -颜色:黑色, -类型:轿车, -座位:5个

我用:

{颜色:“红色”}

结果对象是:

汽车: -颜色:红色, -类型:轿车, -座位:5个

然后,其他一些用户给这辆车打了补丁:

{类型:“掀背车”}

因此,结果对象是:

汽车: -颜色:红色, -类型:掀背式, -座位:5个

现在,如果我再次修补这个对象:

{颜色:“红色”}

结果对象是:

汽车: -颜色:红色, -类型:掀背式, -座位:5个

与我之前得到的有什么不同!

这就是为什么PATCH不是幂等的,而PUT是幂等的。