json 在 REST API 现实生活场景中使用 PUT 与 PATCH 方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28459418/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Use of PUT vs PATCH methods in REST API real life scenarios
提问by Dmitry Kudryavtsev
First of all, some definitions:
首先,一些定义:
PUT is defined in Section 9.6 RFC 2616:
PUT 在第 9.6 节 RFC 2616 中定义:
The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.
PUT 方法请求将封闭的实体存储在提供的 Request-URI 下。如果请求 URI 引用了一个已经存在的资源,则包含的实体应该被视为驻留在源服务器上的实体的修改版本。如果 Request-URI 不指向现有资源,并且该 URI 能够被请求的用户代理定义为新资源,则源服务器可以使用该 URI 创建资源。
PATCH is defined in RFC 5789:
PATCH 在RFC 5789 中定义:
The PATCH method requests that a set of changesdescribed in the request entity be applied to the resource identified by the Request- URI.
PATCH 方法请求将请求实体中描述的一组更改应用于由 Request-URI 标识的资源。
Also according to RFC 2616 Section 9.1.2PUT is Idempotent while PATCH is not.
同样根据RFC 2616 Section 9.1.2PUT 是幂等的,而 PATCH 不是。
Now let us take a look at a real example. When I do POST to /userswith the data {username: 'skwee357', email: '[email protected]'}and the server is capable of creating a resource, it will respond with 201 and resource location (lets assume /users/1) and any next call to GET /users/1will return {id: 1, username: 'skwee357', email: '[email protected]'}.
现在让我们看一个真实的例子。当我/users对数据执行 POST{username: 'skwee357', email: '[email protected]'}并且服务器能够创建资源时,它将以 201 和资源位置(假设/users/1)响应,并且对 GET 的任何下一次调用都/users/1将返回{id: 1, username: 'skwee357', email: '[email protected]'}。
Now let us say I want to modify my email. Email modification is considered "a set of changes" and therefore I should PATCH /users/1with "patch document". In my case it would be the json document: {email: '[email protected]'}. The server then returns 200 (assuming permission are ok). This brings me to first question:
现在让我们说我想修改我的电子邮件。电子邮件修改被认为是“一组更改”,因此我应该/users/1使用“补丁文档”进行PATCH 。就我而言,它将是 json 文档:{email: '[email protected]'}. 然后服务器返回 200(假设权限没问题)。这让我想到第一个问题:
- PATCH is NOT idempotent. It said so in RFC 2616 and RFC 5789. However if I issue the same PATCH request (with my new email), I will get the same resource state (with my email being modified to the requested value). Why is PATCH not then idempotent?
- PATCH 不是幂等的。它在 RFC 2616 和 RFC 5789 中是这样说的。但是,如果我发出相同的 PATCH 请求(使用我的新电子邮件),我将获得相同的资源状态(将我的电子邮件修改为请求的值)。为什么 PATCH 不是幂等的?
PATCH is a relatively new verb (RFC introduced in March 2010), and it comes to solve the problem of "patching" or modifying a set of fields. Before PATCH was introduced, everybody used PUT to update resources. But after PATCH was introduced, it leaves me confused about what PUT is used for. And this brings me to my second (and the main) question:
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 replaceentire 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.
/usersto replace the entire collection. Issuing PUT on a specific entity makes no sense after PATCH was introduced. Am I wrong?
- PUT 和 PATCH 之间的真正区别是什么?我在某处读到 PUT 可能用于替换特定资源下的整个实体,因此应该发送完整实体(而不是像 PATCH 那样发送一组属性)。这种情况的真正实际用途是什么?您希望何时替换/覆盖特定资源 URI 处的实体,为什么不考虑更新/修补实体的此类操作?我看到的 PUT 的唯一实际用例是在集合上发出 PUT,即
/users替换整个集合。引入 PATCH 后,在特定实体上发出 PUT 没有任何意义。我错了吗?
回答by Dan Lowe
NOTE: When I first spent time reading about REST, idempotence was a confusing concept to try to get right. I still didn't get it quite right in my original answer, as further comments (and Jason Hoetger's answer) have shown. For a while, I have resisted updating this answer extensively, to avoid effectively plagiarizing Jason, but I'm editing it now because, well, I was asked to (in the comments).
注意:当我第一次花时间阅读 REST 时,幂等性是一个令人困惑的概念。正如进一步的评论(和Jason Hoetger 的回答)所显示的那样,我的原始答案仍然没有完全正确。有一段时间,我一直拒绝广泛更新这个答案,以避免有效地抄袭杰森,但我现在正在编辑它,因为,好吧,我被要求(在评论中)。
After reading my answer, I suggest you also read Jason Hoetger's excellent answerto this question, and I will try to make my answer better without simply stealing from Jason.
阅读我的回答后,我建议你也阅读Jason Hoetger对这个问题的优秀回答,我会尽量让我的回答更好,而不是简单地从 Jason 那里窃取。
Why is PUT idempotent?
为什么 PUT 是幂等的?
As you noted in your RFC 2616 citation, PUT is considered idempotent. When you PUT a resource, these two assumptions are in play:
正如您在 RFC 2616 引用中所指出的,PUT 被认为是幂等的。当你 PUT 一个资源时,这两个假设在起作用:
You are referring to an entity, not to a collection.
The entity you are supplying is complete (the entireentity).
您指的是一个实体,而不是一个集合。
您提供的实体是完整的(整个实体)。
Let's look at one of your examples.
让我们看看你的例子之一。
{ "username": "skwee357", "email": "[email protected]" }
If you POST this document to /users, as you suggest, then you might get back an entity such as
如果您/users按照您的建议将此文档发布到,那么您可能会返回一个实体,例如
## /users/1
{
"username": "skwee357",
"email": "[email protected]"
}
If you want to modify this entity later, you choose between PUT and PATCH. A PUT might look like this:
如果以后要修改此实体,请在 PUT 和 PATCH 之间进行选择。PUT 可能如下所示:
PUT /users/1
{
"username": "skwee357",
"email": "[email protected]" // new email address
}
You can accomplish the same using PATCH. That might look like this:
您可以使用 PATCH 完成相同的操作。这可能看起来像这样:
PATCH /users/1
{
"email": "[email protected]" // new email address
}
You'll notice a difference right away between these two. The PUT included all of the parameters on this user, but PATCH only included the one that was being modified (email).
您会立即注意到这两者之间的区别。PUT 包含该用户的所有参数,但 PATCH 仅包含正在修改的参数 ( email)。
When using PUT, it is assumed that you are sending the complete entity, and that complete entity replacesany existing entity at that URI. In the above example, the PUT and PATCH accomplish the same goal: they both change this user's email address. But PUT handles it by replacing the entire entity, while PATCH only updates the fields that were supplied, leaving the others alone.
使用 PUT 时,假定您正在发送完整实体,并且该完整实体将替换该 URI 处的任何现有实体。在上面的示例中,PUT 和 PATCH 实现了相同的目标:它们都更改了该用户的电子邮件地址。但是 PUT 通过替换整个实体来处理它,而 PATCH 只更新提供的字段,其他的则不管。
Since PUT requests include the entire entity, if you issue the same request repeatedly, it should always have the same outcome (the data you sent is now the entire data of the entity). Therefore PUT is idempotent.
由于 PUT 请求包括整个实体,如果您重复发出相同的请求,它应该始终具有相同的结果(您发送的数据现在是实体的整个数据)。因此 PUT 是幂等的。
Using PUT wrong
使用 PUT 错误
What happens if you use the above PATCH data in a PUT request?
如果在 PUT 请求中使用上述 PATCH 数据会发生什么?
GET /users/1
{
"username": "skwee357",
"email": "[email protected]"
}
PUT /users/1
{
"email": "[email protected]" // new email address
}
GET /users/1
{
"email": "[email protected]" // new email address... and nothing else!
}
(I'm assuming for the purposes of this question that the server doesn't have any specific required fields, and would allow this to happen... that may not be the case in reality.)
(为了这个问题的目的,我假设服务器没有任何特定的必填字段,并且会允许这种情况发生......实际上可能并非如此。)
Since we used PUT, but only supplied email, now that's the only thing in this entity. This has resulted in data loss.
由于我们使用了 PUT,但只提供了email,现在这是这个实体中唯一的东西。这导致了数据丢失。
This example is here for illustrative purposes -- don't ever actually do this. This PUT request is technically idempotent, but that doesn't mean it isn't a terrible, broken idea.
这个例子是为了说明目的——不要真的这样做。这个 PUT 请求在技术上是幂等的,但这并不意味着它不是一个糟糕的、破碎的想法。
How can PATCH be idempotent?
PATCH 怎么可能是幂等的?
In the above example, PATCH wasidempotent. You made a change, but if you made the same change again and again, it would always give back the same result: you changed the email address to the new value.
在上面的例子中,PATCH是幂等的。您进行了更改,但是如果您一次又一次地进行相同的更改,它总是会返回相同的结果:您将电子邮件地址更改为新值。
GET /users/1
{
"username": "skwee357",
"email": "[email protected]"
}
PATCH /users/1
{
"email": "[email protected]" // new email address
}
GET /users/1
{
"username": "skwee357",
"email": "[email protected]" // email address was changed
}
PATCH /users/1
{
"email": "[email protected]" // new email address... again
}
GET /users/1
{
"username": "skwee357",
"email": "[email protected]" // nothing changed since last GET
}
My original example, fixed for accuracy
我的原始示例,已修复以提高准确性
I originally had examples that I thought were showing non-idempotency, but they were misleading / incorrect. I am going to keep the examples, but use them to illustrate a different thing: that multiple PATCH documents against the same entity, modifying different attributes, do not make the PATCHes non-idempotent.
我最初有一些我认为显示非幂等性的示例,但它们具有误导性/不正确。我将保留示例,但使用它们来说明不同的事情:针对同一实体的多个 PATCH 文档,修改不同的属性,不会使 PATCH 成为非幂等的。
Let's say that at some past time, a user was added. This is the state that you are starting from.
假设在过去的某个时间,添加了一个用户。这是您开始的状态。
{
"id": 1,
"name": "Sam Kwee",
"email": "[email protected]",
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "10001"
}
After a PATCH, you have a modified entity:
在 PATCH 之后,您有一个修改过的实体:
PATCH /users/1
{"email": "[email protected]"}
{
"id": 1,
"name": "Sam Kwee",
"email": "[email protected]", // the email changed, yay!
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "10001"
}
If you then repeatedly apply your PATCH, you will continue to get the same result: the email was changed to the new value. A goes in, A comes out, therefore this is idempotent.
如果您随后重复应用 PATCH,您将继续获得相同的结果:电子邮件已更改为新值。A进去,A出来,所以这是幂等的。
An hour later, after you have gone to make some coffee and take a break, someone else comes along with their own PATCH. It seems the Post Office has been making some changes.
一个小时后,在你去泡咖啡并休息一下后,其他人带着他们自己的 PATCH 来了。邮局似乎正在做出一些改变。
PATCH /users/1
{"zip": "12345"}
{
"id": 1,
"name": "Sam Kwee",
"email": "[email protected]", // still the new email you set
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "12345" // and this change as well
}
Since this PATCH from the post office doesn't concern itself with email, only zip code, if it is repeatedly applied, it will also get the same result: the zip code is set to the new value. A goes in, A comes out, therefore this is alsoidempotent.
由于邮局的这个PATCH本身不关心电子邮件,只关心邮政编码,如果重复应用,它也会得到相同的结果:邮政编码设置为新值。A进去,A出来,所以这也是幂等的。
The next day, you decide to send your PATCH again.
第二天,您决定再次发送您的 PATCH。
PATCH /users/1
{"email": "[email protected]"}
{
"id": 1,
"name": "Sam Kwee",
"email": "[email protected]",
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "12345"
}
Your patch has the same effect it had yesterday: it set the email address. A went in, A came out, therefore this is idempotent as well.
您的补丁具有与昨天相同的效果:它设置了电子邮件地址。A进去,A出来,所以这也是幂等的。
What I got wrong in my original answer
我在原始答案中做错了什么
I want to draw an important distinction (something I got wrong in my original answer). Many servers will respond to your REST requests by sending back the new entity state, with your modifications (if any). So, when you get this responseback, it is different from the one you got back yesterday, because the zip code is not the one you received last time. However, your request was not concerned with the zip code, only with the email. So your PATCH document is still idempotent - the email you sent in PATCH is now the email address on the entity.
我想画一个重要的区别(我在原始答案中弄错了)。许多服务器将通过发回新的实体状态以及您的修改(如果有)来响应您的 REST 请求。因此,当您收到此回复时,它与您昨天收到的回复不同,因为邮政编码不是您上次收到的。但是,您的请求与邮政编码无关,只与电子邮件有关。所以你的 PATCH 文档仍然是幂等的——你在 PATCH 中发送的电子邮件现在是实体上的电子邮件地址。
So when is PATCH not idempotent, then?
那么什么时候 PATCH 不是幂等的呢?
For a full treatment of this question, I again refer you to Jason Hoetger's answer. I'm just going to leave it at that, because I honestly don't think I can answer this part better than he already has.
对于这个问题的完整处理,我再次向您推荐Jason Hoetger 的回答。我只是打算就此搁笔,因为老实说,我认为我无法比他已经回答的更好。
回答by Jason Hoetger
Though Dan Lowe's excellent answer very thoroughly answered the OP's question about the difference between PUT and PATCH, its answer to the question of why PATCH is not idempotent is not quite correct.
尽管 Dan Lowe 的出色回答非常彻底地回答了 OP 关于 PUT 和 PATCH 之间区别的问题,但它对为什么 PATCH 不是幂等的问题的回答并不完全正确。
To show why PATCH isn't idempotent, it helps to start with the definition of idempotence (from Wikipedia):
为了说明为什么 PATCH 不是幂等的,它有助于从幂等的定义开始(来自Wikipedia):
The term idempotent is used more comprehensively to describe an operation that will produce the same results if executed once or multiple times [...] An idempotent function is one that has the property f(f(x)) = f(x) for any value x.
术语幂等更全面地用于描述如果执行一次或多次将产生相同结果的操作 [...] 幂等函数是具有属性 f(f(x)) = f(x) 的函数任何值 x。
In more accessible language, an idempotent PATCH could be defined as: After PATCHing a resource with a patch document, all subsequent PATCH calls to the same resource with the same patch document will not change the resource.
在更易于理解的语言中,幂等 PATCH 可以定义为:在使用补丁文档对资源进行 PATCH 后,所有对具有相同补丁文档的同一资源的后续 PATCH 调用都不会更改资源。
Conversely, a non-idempotent operation is one where f(f(x)) != f(x), which for PATCH could be stated as: After PATCHing a resource with a patch document, subsequent PATCH calls to the same resource with the same patch document dochange the resource.
相反,非幂等操作是其中 f(f(x)) != f(x) 的操作,对于 PATCH 可以表示为:在使用补丁文档修补资源后,后续 PATCH 调用相同资源的相同的补丁文件确实改变了资源。
To illustrate a non-idempotent PATCH, suppose there is a /users resource, and suppose that calling GET /usersreturns a list of users, currently:
为了说明非幂等 PATCH,假设有一个 /users 资源,并假设调用GET /users返回一个用户列表,当前:
[{ "id": 1, "username": "firstuser", "email": "[email protected]" }]
Rather than PATCHing /users/{id}, as in the OP's example, suppose the server allows PATCHing /users. Let's issue this PATCH request:
与 OP 示例中的 PATCHing /users/{id} 不同,假设服务器允许 PATCHing /users。让我们发出这个 PATCH 请求:
PATCH /users
[{ "op": "add", "username": "newuser", "email": "[email protected]" }]
Our patch document instructs the server to add a new user called newuserto the list of users. After calling this the first time, GET /userswould return:
我们的补丁文件指示服务器向用户newuser列表中添加一个名为的新用户。第一次调用后,GET /users将返回:
[{ "id": 1, "username": "firstuser", "email": "[email protected]" },
{ "id": 2, "username": "newuser", "email": "[email protected]" }]
Now, if we issue the exact samePATCH request as above, what happens? (For the sake of this example, let's assume that the /users resource allows duplicate usernames.) The "op" is "add", so a new user is added to the list, and a subsequent GET /usersreturns:
现在,如果我们发出与上面完全相同的PATCH 请求,会发生什么?(为了这个例子,我们假设 /users 资源允许重复的用户名。)“op”是“add”,所以一个新用户被添加到列表中,随后GET /users返回:
[{ "id": 1, "username": "firstuser", "email": "[email protected]" },
{ "id": 2, "username": "newuser", "email": "[email protected]" },
{ "id": 3, "username": "newuser", "email": "[email protected]" }]
The /users resource has changed again, even though we issued the exact samePATCH against the exact sameendpoint. If our PATCH is f(x), f(f(x)) is not the same as f(x), and therefore, this particular PATCH is not idempotent.
/users 资源再次更改,即使我们针对完全相同的端点发布了完全相同的PATCH 。如果我们的 PATCH 是 f(x),则 f(f(x)) 与 f(x) 不同,因此,这个特定的 PATCH 不是幂等的。
Although PATCH isn't guaranteedto be idempotent, there's nothing in the PATCH specification to prevent you from making all PATCH operations on your particular server idempotent. RFC 5789 even anticipates advantages from idempotent PATCH requests:
尽管不保证PATCH 是幂等的,但 PATCH 规范中没有任何内容可以阻止您在特定服务器上进行所有 PATCH 操作是幂等的。RFC 5789 甚至预测了幂等 PATCH 请求的优势:
A PATCH request can be issued in such a way as to be idempotent, which also helps prevent bad outcomes from collisions between two PATCH requests on the same resource in a similar time frame.
PATCH 请求可以以幂等的方式发出,这也有助于防止在相似的时间范围内同一资源上的两个 PATCH 请求之间发生冲突而导致不良结果。
In Dan's example, his PATCH operation is, in fact, idempotent. In that example, the /users/1 entity changed between our PATCH requests, but not because ofour PATCH requests; it was actually the Post Office's differentpatch document that caused the zip code to change. The Post Office's different PATCH is a different operation; if our PATCH is f(x), the Post Office's PATCH is g(x). Idempotence states that f(f(f(x))) = f(x), but makes no guarantes about f(g(f(x))).
在 Dan 的例子中,他的 PATCH 操作实际上是幂等的。在那个例子中, /users/1 实体在我们的 PATCH 请求之间发生了变化,但不是因为我们的 PATCH 请求;实际上是邮局的不同补丁文件导致邮政编码发生变化。邮局不同的PATCH是不同的操作;如果我们的 PATCH 是 f(x),则邮局的 PATCH 是 g(x)。幂等性声明f(f(f(x))) = f(x),但不保证f(g(f(x)))。
回答by Kalel Wade
I was curious about this as well and found a few interesting articles. I may not answer your question to its full extent, but this at least provides some more information.
我也对这个很好奇,发现了一些有趣的文章。我可能无法全面回答您的问题,但这至少提供了更多信息。
http://restful-api-design.readthedocs.org/en/latest/methods.html
http://restful-api-design.readthedocs.org/en/latest/methods.html
The HTTP RFC specifies that PUT must take a full new resource representation as the request entity. This means that if for example only certain attributes are provided, those should be remove (i.e. set to null).
HTTP RFC 指定 PUT 必须采用全新的资源表示作为请求实体。这意味着,例如,如果仅提供某些属性,则应删除这些属性(即设置为 null)。
Given that, then a PUT should send the entire object. For instance,
鉴于此,PUT 应该发送整个对象。例如,
/users/1
PUT {id: 1, username: 'skwee357', email: '[email protected]'}
This would effectively update the email. The reason PUT may not be too effective is that your only really modifying one field and including the username is kind of useless. The next example shows the difference.
这将有效地更新电子邮件。PUT 可能不太有效的原因是您唯一真正修改一个字段并包含用户名是无用的。下一个示例显示了差异。
/users/1
PUT {id: 1, email: '[email protected]'}
Now, if the PUT was designed according the spec, then the PUT would set the username to null and you would get the following back.
现在,如果 PUT 是根据规范设计的,那么 PUT 会将用户名设置为 null,您将得到以下结果。
{id: 1, username: null, email: '[email protected]'}
When you use a PATCH, you only update the field you specify and leave the rest alone as in your example.
当您使用 PATCH 时,您只需更新您指定的字段,而其余部分则如您的示例中所示。
The following take on the PATCH is a little different than I have never seen before.
以下对 PATCH 的看法与我以前从未见过的略有不同。
http://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/
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.
PUT 和 PATCH 请求之间的区别体现在服务器处理封闭实体以修改 Request-URI 标识的资源的方式上。在 PUT 请求中,包含的实体被认为是存储在源服务器上的资源的修改版本,并且客户端请求替换存储的版本。但是,对于 PATCH,封闭的实体包含一组指令,描述当前驻留在源服务器上的资源应如何修改以生成新版本。PATCH 方法影响由 Request-URI 标识的资源,也可能对其他资源产生副作用;即,可以通过 PATCH 的应用程序创建新资源或修改现有资源。
PATCH /users/123
[
{ "op": "replace", "path": "/email", "value": "[email protected]" }
]
You are more or less treating the PATCH as a way to update a field. So instead of sending over the partial object, you're sending over the operation. i.e. Replace email with value.
您或多或少地将 PATCH 视为更新字段的一种方式。因此,您不是通过部分对象发送,而是通过操作发送。即用值替换电子邮件。
The article ends with this.
文章到此结束。
It is worth mentioning that PATCH is not really designed for truly REST APIs, as Fielding's dissertation does not define any way to partially modify resources. But, Roy Fielding himself said that PATCH was something [he] created for the initial HTTP/1.1 proposal because partial PUT is never RESTful. Sure you are not transferring a complete representation, but REST does not require representations to be complete anyway.
值得一提的是,PATCH 并不是真正为真正的 REST API 设计的,因为 Fielding 的论文没有定义任何部分修改资源的方法。但是,Roy Fielding 自己说 PATCH 是 [他] 为最初的 HTTP/1.1 提案创建的东西,因为部分 PUT 从来都不是 RESTful。确定您没有传输完整的表示,但无论如何 REST 并不要求表示是完整的。
Now, I don't know if I particularly agree with the article as many commentators point out. Sending over a partial representation can easily be a description of the changes.
现在,我不知道我是否像许多评论家指出的那样特别同意这篇文章。发送部分表示可以很容易地描述更改。
For me, I am mixed on using PATCH. For the most part, I will treat PUT as a PATCH since the only real difference I have noticed so far is that PUT "should" set missing values to null. It may not be the 'most correct' way to do it, but good luck coding perfect.
对我来说,我对使用 PATCH 感到很困惑。在大多数情况下,我会将 PUT 视为 PATCH,因为到目前为止我注意到的唯一真正区别是 PUT“应该”将缺失值设置为 null。这可能不是“最正确”的方法,但祝你编码完美。
回答by Bin Ni
The difference between PUT and PATCH is that:
PUT 和 PATCH 的区别在于:
- PUT is required to be idempotent. In order to achieve that, you have to put the entire complete resource in the request body.
- PATCH can be non-idempotent. Which implies it can also be idempotent in some cases, such as the cases you described.
- PUT 必须是幂等的。为了实现这一点,您必须将整个完整资源放入请求正文中。
- PATCH 可以是非幂等的。这意味着它在某些情况下也可以是幂等的,例如您描述的情况。
PATCH requires some "patch language" to tell the server how to modify the resource. The caller and the server need to define some "operations" such as "add", "replace", "delete". For example:
PATCH 需要一些“补丁语言”来告诉服务器如何修改资源。调用者和服务器需要定义一些“操作”,例如“添加”、“替换”、“删除”。例如:
GET /contacts/1
{
"id": 1,
"name": "Sam Kwee",
"email": "[email protected]",
"state": "NY",
"zip": "10001"
}
PATCH /contacts/1
{
[{"operation": "add", "field": "address", "value": "123 main street"},
{"operation": "replace", "field": "email", "value": "[email protected]"},
{"operation": "delete", "field": "zip"}]
}
GET /contacts/1
{
"id": 1,
"name": "Sam Kwee",
"email": "[email protected]",
"state": "NY",
"address": "123 main street",
}
Instead of using explicit "operation" fields, the patch language can make it implicit by defining conventions like:
补丁语言可以通过定义如下约定使其隐式,而不是使用显式的“操作”字段:
in the PATCH request body:
在 PATCH 请求正文中:
- The existence of a field means "replace" or "add" that field.
- If the value of a field is null, it means delete that field.
- 字段的存在意味着“替换”或“添加”该字段。
- 如果某个字段的值为空,则表示删除该字段。
With the above convention, the PATCH in the example can take the following form:
根据上述约定,示例中的 PATCH 可以采用以下形式:
PATCH /contacts/1
{
"address": "123 main street",
"email": "[email protected]",
"zip":
}
Which looks more concise and user-friendly. But the users need to be aware of the underlying convention.
这看起来更简洁和用户友好。但是用户需要了解底层约定。
With the operations I mentioned above, the PATCH is still idempotent. But if you define operations like: "increment" or "append", you can easily see it won't be idempotent anymore.
通过我上面提到的操作,PATCH 仍然是幂等的。但是,如果您定义了诸如“增量”或“追加”之类的操作,您将很容易看到它不再是幂等的。
回答by Bijan
TLDR - Dumbed Down Version
TLDR - 简化版
PUT=> Set all new attributes for an existing resource.
PUT=> 为现有资源设置所有新属性。
PATCH=> Partially update an existing resource (not all attributes required).
PATCH=> 部分更新现有资源(并非所有属性都需要)。
回答by Rolvernew
Let me quote and comment more closely the RFC 7231 section 4.2.2, already cited in earlier comments :
让我更仔细地引用和评论RFC 7231 第 4.2.2 节,在之前的评论中已经引用:
A request method is considered "idempotent" if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request. Of the request methods defined by this specification, PUT, DELETE, and safe request methods are idempotent.
(...)
Idempotent methods are distinguished because the request can be repeated automatically if a communication failure occurs before the client is able to read the server's response. For example, if a client sends a PUT request and the underlying connection is closed before any response is received, then the client can establish a new connection and retry the idempotent request. It knows that repeating the request will have the same intended effect, even if the original request succeeded, though the response might differ.
如果使用该方法的多个相同请求对服务器的预期效果与单个此类请求的效果相同,则该请求方法被认为是“幂等的”。在本规范定义的请求方法中,PUT、DELETE 和安全请求方法是幂等的。
(……)
幂等方法的区别在于,如果在客户端能够读取服务器的响应之前发生通信失败,则可以自动重复请求。例如,如果客户端发送 PUT 请求,并且在收到任何响应之前底层连接已关闭,则客户端可以建立新连接并重试幂等请求。它知道重复请求将具有相同的预期效果,即使原始请求成功,但响应可能不同。
So, what should be "the same" after a repeated request of an idempotent method? Not the server state, nor the server response, but the intended effect. In particular, the method should be idempotent "from the point of view of the client". Now, I think that this point of view shows that the last example in Dan Lowe's answer, which I don't want to plagiarize here, indeed shows that a PATCH request can be non-idempotent (in a more natural way than the example in Jason Hoetger's answer).
那么,在重复请求幂等方法之后,什么应该是“相同的”?不是服务器状态,也不是服务器响应,而是预期的效果。特别是,该方法应该是“从客户端的角度”的幂等的。现在,我认为这个观点表明Dan Lowe 的回答中的最后一个例子,我不想在这里抄袭,确实表明 PATCH 请求可以是非幂等的(以比Jason Hoetger 的回答)。
Indeed, let's make the example slightly more precise by making explicit one possible intendfor the first client. Let's say that this client goes through the list of users with the project to check their emails andzip codes. He starts with user 1, notices that the zip is right but the email is wrong. He decides to correct this with a PATCH request, which is fully legitimate, and sends only
事实上,让我们通过为第一个客户明确一个可能的意图来使这个例子更加精确。假设该客户端通过项目用户列表检查他们的电子邮件和邮政编码。他从用户 1 开始,注意到 zip 是正确的,但电子邮件是错误的。他决定用一个完全合法的 PATCH 请求来纠正这个问题,并且只发送
PATCH /users/1
{"email": "[email protected]"}
since this is the only correction. Now, the request fails because of some network issue and is re-submitted automatically a couple of hours later. In the meanwhile, another client has (erroneously) modified the zip of user 1. Then, sending the same PATCH request a second time does not achieve the intended effectof the client, since we end up with an incorrect zip. Hence the method is not idempotent in the sense of the RFC.
因为这是唯一的更正。现在,请求由于某些网络问题而失败,并在几个小时后自动重新提交。与此同时,另一个客户端(错误地)修改了用户 1 的 zip。然后,第二次发送相同的 PATCH 请求并没有达到客户端的预期效果,因为我们最终得到了一个不正确的 zip。因此,该方法在 RFC 的意义上不是幂等的。
If instead the client uses a PUT request to correct the email, sending to the server all properties of user 1 along with the email, his intended effect will be achieved even if the request has to be re-sent later and user 1 has been modified in the meanwhile --- since the second PUT request will overwrite all changes since the first request.
相反,如果客户端使用 PUT 请求来更正电子邮件,将用户 1 的所有属性与电子邮件一起发送到服务器,即使稍后必须重新发送请求并且用户 1 已被修改,他的预期效果也将实现同时 --- 因为第二个 PUT 请求将覆盖自第一个请求以来的所有更改。
回答by Zbigniew Szcz?sny
In my humble opinion, idempotence means:
在我看来,幂等性意味着:
- PUT:
- 放:
I send a compete resource definition, so - the resulting resource state is exactly as defined by PUT params. Each and every time I update the resource with the same PUT params - the resulting state is exactly the same.
我发送了一个竞争资源定义,因此 - 生成的资源状态与 PUT 参数所定义的完全相同。每次我使用相同的 PUT 参数更新资源时 - 结果状态完全相同。
- PATCH:
- 修补:
I sent only part of the resource definition, so it might happen other users are updating this resource's OTHER parameters in a meantime. Consequently - consecutive patches with the same parameters and their values might result with different resource state. For instance:
我只发送了资源定义的一部分,因此可能会发生其他用户同时更新此资源的 OTHER 参数的情况。因此 - 具有相同参数及其值的连续补丁可能会导致不同的资源状态。例如:
Presume an object defined as follows:
假设一个对象定义如下:
CAR: - color: black, - type: sedan, - seats: 5
汽车: - 颜色:黑色, - 类型:轿车, - 座位:5
I patch it with:
我修补它:
{color: 'red'}
{红色'}
The resulting object is:
结果对象是:
CAR: - color: red, - type: sedan, - seats: 5
汽车: - 颜色:红色, - 类型:轿车, - 座位:5
Then, some other users patches this car with:
然后,其他一些用户修补了这辆车:
{type: 'hatchback'}
{类型:'掀背车'}
so, the resulting object is:
所以,结果对象是:
CAR: - color: red, - type: hatchback, - seats: 5
汽车: - 颜色:红色, - 类型:掀背车, - 座位:5
Now, if I patch this object again with:
现在,如果我再次修补这个对象:
{color: 'red'}
{红色'}
the resulting object is:
结果对象是:
CAR: - color: red, - type: hatchback, - seats: 5
汽车: - 颜色:红色, - 类型:掀背车, - 座位:5
What is DIFFERENT to what I've got previously!
和我之前得到的有什么不同!
This is why PATCH is not idempotent while PUT is idempotent.
这就是为什么 PATCH 不是幂等的,而 PUT 是幂等的。
回答by Mohammad-Ali A'R?BI
To conclude the discussion on the idempotency, I should note that one can define idempotency in the REST context in two ways. Let's first formalize a few things:
为了结束对幂等性的讨论,我应该指出,可以通过两种方式在 REST 上下文中定义幂等性。让我们首先正式确定一些事情:
A resourceis a function with its codomain being the class of strings. In other words, a resource is a subset of String × Any, where all the keys are unique. Let's call the class of the resources Res.
一个资源是它的值域是类字符串的函数。换句话说,资源是 的子集String × Any,其中所有的键都是唯一的。让我们调用资源的类Res。
A REST operation on resources, is a function f(x: Res, y: Res): Res. Two examples of REST operations are:
对资源的 REST 操作是一个函数f(x: Res, y: Res): Res。REST 操作的两个示例是:
PUT(x: Res, y: Res): Res = x, andPATCH(x: Res, y: Res): Res, which works likePATCH({a: 2}, {a: 1, b: 3}) == {a: 2, b: 3}.
PUT(x: Res, y: Res): Res = x, 和PATCH(x: Res, y: Res): Res,其工作原理类似于PATCH({a: 2}, {a: 1, b: 3}) == {a: 2, b: 3}。
(This definition is specifically designed to argue about PUTand POST, and e.g. doesn't make much sense on GETand POST, as it doesn't care about persistence).
(这个定义是专门用来争论PUTand 的POST,eg 对GETand没有多大意义POST,因为它不关心持久性)。
Now, by fixing x: Res(informatically speaking, using currying), PUT(x: Res)and PATCH(x: Res)are univariate functions of type Res → Res.
现在,通过修复x: Res(从信息上讲,使用柯里化),PUT(x: Res)和PATCH(x: Res)是 类型的单变量函数Res → Res。
A function
g: Res → Resis called globally idempotent, wheng ○ g == g, i.e. for anyy: Res,g(g(y)) = g(y).Let
x: Resa resource, andk = x.keys. A functiong = f(x)is called left idempotent, when for eachy: Res, we haveg(g(y))|? == g(y)|?. It basically means that the result should be same, if we look at the applied keys.
一个函数
g: Res → Res被称为全局幂等的, wheng ○ g == g,即对于任何y: Res,g(g(y)) = g(y)。让
x: Res一个资源,和k = x.keys。一个函数g = f(x)被称为左幂等的,当对于每个y: Res,我们有g(g(y))|? == g(y)|?。如果我们查看应用的键,它基本上意味着结果应该是相同的。
So, PATCH(x)is not globally idempotent, but is left idempotent. And left idempotency is the thing that matters here: if we patch a few keys of the resource, we want those keys to be same if we patch it again, and we don't care about the rest of the resource.
所以,PATCH(x)不是全局幂等的,而是左幂等的。左幂等性在这里很重要:如果我们修补资源的几个键,我们希望这些键在我们再次修补时相同,而我们不关心资源的其余部分。
And when RFC is talking about PATCH not being idempotent, it is talking about global idempotency. Well, it's good that it's not globally idempotent, otherwise it would have been a broken operation.
当 RFC 谈论 PATCH 不是幂等时,它谈论的是全局幂等性。好吧,最好它不是全局幂等的,否则它会是一个失败的操作。
Now, Jason Hoetger's answeris trying to demonstrate that PATCH is not even left idempotent, but it's breaking too many things to do so:
现在,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 withx 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: "[email protected]"}must match with{email: "[email protected]"}, 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.
- 首先,PATCH 用于集合,尽管 PATCH 被定义为适用于地图/字典/键值对象。
- 如果有人真的想将 PATCH 应用于集合,那么应该使用一个自然的翻译:
t: Set<T> → Map<T, Boolean>,用x in A iff t(A)(x) == True. 使用这个定义,补丁是幂等的。 - 在这个例子中,没有使用这个翻译,而是 PATCH 像 POST 一样工作。首先,为什么要为对象生成一个ID?什么时候生成?如果首先将对象与集合的元素进行比较,如果没有找到匹配的对象,则生成 ID,然后程序应该再次以不同的方式工作(
{id: 1, email: "[email protected]"}必须与 匹配{email: "[email protected]"},否则程序总是被破坏并且 PATCH 不可能修补)。如果在检查集合之前生成了 ID,则程序再次被破坏。
One can make examples of PUT being non-idempotent with breaking half of the things that are broken in this example:
可以举出 PUT 非幂等的例子,打破这个例子中被破坏的一半:
- An example with generated additional featureswould be versioning. One may keep record of the number of changes on a single object. In this case, PUT is not idempotent:
PUT /user/12 {email: "[email protected]"}results in{email: "...", version: 1}the first time, and{email: "...", version: 2}the second time. - Messing with the IDs, one may generate a new ID every time the object is updated, resulting in a non-idempotent PUT.
- 生成附加功能的一个例子是版本控制。可以记录单个对象的更改次数。在这种情况下,PUT 不是幂等的:第一次
PUT /user/12 {email: "[email protected]"}产生结果,第二次产生结果。{email: "...", version: 1}{email: "...", version: 2} - 与 ID 混淆,可能会在每次更新对象时生成一个新 ID,从而导致非幂等 PUT。
All the above examples are natural examples that one may encounter.
以上所有例子都是人们可能会遇到的自然例子。
My final point is, that PATCH should not be globally idempotent, otherwise won't give you the desired effect. You want to change the email address of your user, without touching the rest of the information, and you don't want to overwrite the changes of another party accessing the same resource.
我的最后一点是, PATCH 不应该是全局幂等的,否则不会给你想要的效果。您想更改用户的电子邮件地址,而不触及其余信息,并且不想覆盖访问同一资源的另一方的更改。
回答by Benjamin
One additional information I just one to add is that a PATCH request use less bandwidth compared to a PUT request since just a part of the data is sent not the whole entity. So just use a PATCH request for updates of specific records like (1-3 records) while PUT request for updating a larger amount of data. That is it, don't think too much or worry about it too much.
我要补充的一个额外信息是,与 PUT 请求相比,PATCH 请求使用更少的带宽,因为只发送了一部分数据而不是整个实体。因此,只需使用 PATCH 请求更新特定记录,例如(1-3 条记录),而 PUT 请求更新大量数据。就是这样,不要想太多,也不要太担心。

