本文翻译自:REST API - PUT vs PATCH with real life examples
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. PUT方法请求将封闭的实体存储在提供的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 . 如果Request-URI引用了已经存在的资源, 则应将封闭的实体视为驻留在源服务器上的实体的修改版本 。 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. 如果Request-URI没有指向现有资源,并且请求用户代理能够将该URI定义为新资源,则原始服务器可以使用该URI创建资源。
PATCH is defined in RFC 5789 : PATCH在RFC 5789中定义:
The PATCH method requests that a set of changes described 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.2 PUT is Idempotent while PATCH is not. 同样根据RFC 2616,第9.1.2节 PUT是幂等的,而PATCH不是。
Now lets take a look at a real example. 现在让我们看一个真实的例子。 When I do POST to /users with the data {username: 'skwee357', email: 'skwee357@domain.com'} 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/1 will return {id: 1, username: 'skwee357', email: 'skwee357@domain.com'} . 当我使用数据{username: 'skwee357', email: 'skwee357@domain.com'}对/users进行POST并且服务器能够创建资源时,它将响应201和资源位置(假设/users/1 )和对GET /users/1下一次调用将返回{id: 1, username: 'skwee357', email: 'skwee357@domain.com'} 。
Now lets say I want to modify my email. 现在说我要修改我的电子邮件。 Email modification is considered "a set of changes" and therefor I should PATCH /users/1 with " patch document ". 电子邮件修改被视为“一组更改”,因此,我应该使用“ 补丁文档 ”来修补 /users/1 。 In my case it would be a json {email: 'skwee357@newdomain.com'} . 在我的情况下,它将是json {email: 'skwee357@newdomain.com'} 。 The server then returns 200 (assuming permission are ok). 然后,服务器返回200(假设允许)。 This brings me to first question: 这使我想到第一个问题:
- PATCH is NOT idempotent. 补丁不是幂等的。 It said so in RFC 2616 and RFC 5789. However if I'll issue the same PATCH request (with my new email) Ill get the same resource state (with my email being modified to the requested value). 它在RFC 2616和RFC 5789中是这样说的。但是,如果我发出相同的PATCH请求(使用我的新电子邮件),则会得到相同的资源状态(将我的电子邮件修改为请求的值)。 Why isn't PATCH then idempotent? 为什么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. PATCH是一个相对较新的动词(2010年3月引入RFC),用于解决“修补”或修改一组字段的问题。 Before PATCH was introduced, everybody used PUT to update resource. 在引入PATCH之前,每个人都使用PUT来更新资源。 But after PATCH was introduced, it leaves me confused what is PUT used for then? 但是在引入PATCH之后,让我感到困惑的是,PUT是用来做什么的? And this brings me to second (and the main) question: 这使我想到了第二个(也是主要的)问题:
- Whats the real difference between PUT and PATCH? PUT和PATCH之间的真正区别是什么? I've read somewhere the 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). 我读过某处PUT可能会用来替换特定资源下的整个实体,因此应该发送完整的实体(而不是像PATCH那样发送一组属性)。 What is the real practical usage for such case? 这种情况的实际实际用途是什么? When would you like to replace / overwrite an entity under specific resource URI and why such operation is not considered as updating / patching the entity? 您何时要替换/覆盖特定资源URI下的实体,为什么不将这种操作视为更新/修补实体? The only practical use case I see for PUT is issuing a PUT on collection, ie
/usersto replace the entire collection. 我在PUT上看到的唯一实际用例是在集合上发布PUT,即/users替换整个集合。 Issuing PUT on a specific entity makes no sense after PATCH was introduced. 引入PATCH之后,在特定实体上发布PUT毫无意义。 Am I wrong? 我错了吗?
#1楼
参考:https://stackoom.com/question/1vPas/REST-API-带有实际示例的PUT与PATCH
#2楼
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. 在HTTP RFC规定PUT必须为请求实体焕发出新的资源表示。 This means that if for example only certain attributes are provided, those should be remove (ie set to null). 这意味着,例如,如果仅提供某些属性,则应将其删除(即设置为null)。
Given that, then a PUT should send the entire object. 鉴于此,那么PUT应该发送整个对象。 For instance, 例如,
/users/1
PUT {id: 1, username: 'skwee357', email: 'newemail@domain.com'}
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. PUT可能不太有效的原因是,您真正真正修改的一个字段并包括用户名是没有用的。 The next example shows the difference. 下一个示例显示了差异。
/users/1
PUT {id: 1, email: 'newemail@domain.com'}
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: 'newemail@domain.com'}
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. PUT和PATCH请求之间的差异反映在服务器处理封闭实体以修改由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. 在PUT请求中,封闭的实体被视为原始服务器上存储的资源的修改版本,并且客户端正在请求替换存储的版本。 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. 但是,对于PATCH,封闭的实体包含一组指令,这些指令描述了应如何修改当前驻留在源服务器上的资源以产生新版本。 The PATCH method affects the resource identified by the Request-URI, and it also MAY have side effects on other resources; PATCH方法影响由Request-URI标识的资源,并且可能对其他资源也有副作用。 ie, new resources may be created, or existing ones modified, by the application of a PATCH. 也就是说,可以通过应用PATCH来创建新资源或修改现有资源。
PATCH /users/123
[
{ "op": "replace", "path": "/email", "value": "new.email@example.org" }
]
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. 因此,不是通过部分对象发送,而是通过操作发送。 ie Replace email with value. 即用值替换电子邮件。
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. 值得一提的是,PATCH并不是真正为真正的REST API设计的,因为Fielding的论文没有定义任何部分修改资源的方法。 But, Roy Fielding himself said that PATCH was something [he] created for the initial HTTP/1.1 proposal because partial PUT is never RESTful. 但是,罗伊·菲尔丁(Roy Fielding)自己说,PATCH是他为最初的HTTP / 1.1提议创建的,因为部分PUT从来都不是RESTful的。 Sure you are not transferring a complete representation, but REST does not require representations to be complete anyway. 确保您没有传输完整的表示形式,但是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. 对我来说,我对使用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. 在大多数情况下,我会将PUT视为PATCH,因为到目前为止我所注意到的唯一真正的区别是PUT“应该”将缺失值设置为null。 It may not be the 'most correct' way to do it, but good luck coding perfect. 这样做可能不是“最正确”的方法,但是祝您好运。
#3楼
NOTE : When I first spent time reading about REST, idempotence was a confusing concept to try to get right. 注意 :当我第一次花时间阅读有关REST的知识时,幂等性是一个难以正确理解的概念。 I still didn't get it quite right in my original answer, as further comments (and Jason Hoetger's answer ) have shown. 正如我进一步的评论(以及Jason Hoetger的答案 )所显示的那样,我在原始答案中仍然没有完全正确。 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). 一段时间以来,我一直拒绝广泛更新此答案,以避免有效窃Jason,但现在我正在对其进行编辑,因为(在评论中)我被要求这样做。
After reading my answer, I suggest you also read Jason Hoetger's excellent answer to 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. 正如您在RFC 2616引用中指出的那样,PUT被认为是幂等的。 When you PUT a resource, these two assumptions are in play: 当您放置资源时,这两个假设起作用了:
You are referring to an entity, not to a collection. 您指的是实体,而不是集合。
The entity you are supplying is complete (the entire entity). 您要提供的实体是完整的( 整个实体)。
Let's look at one of your examples. 让我们来看一个例子。
{ "username": "skwee357", "email": "skwee357@domain.com" }
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": "skwee357@domain.com"
}
If you want to modify this entity later, you choose between PUT and PATCH. 如果以后要修改此实体,请在PUT和PATCH之间选择。 A PUT might look like this: 一个PUT可能看起来像这样:
PUT /users/1
{
"username": "skwee357",
"email": "skwee357@gmail.com" // new email address
}
You can accomplish the same using PATCH. 您可以使用PATCH完成相同的操作。 That might look like this: 可能看起来像这样:
PATCH /users/1
{
"email": "skwee357@gmail.com" // 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 replaces any existing entity at that URI. 使用PUT时,假定您正在发送完整实体,并且该完整实体将替换该URI上的任何现有实体。 In the above example, the PUT and PATCH accomplish the same goal: they both change this user's email address. 在上面的示例中,PUT和PATCH实现了相同的目标:它们都更改了该用户的电子邮件地址。 But PUT handles it by replacing the entire entity, while PATCH only updates the fields that were supplied, leaving the others alone. 但是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). 由于PUT请求包含整个实体,因此,如果您反复发出相同的请求,则它应始终具有相同的结果(您发送的数据现在是该实体的整个数据)。 Therefore PUT is idempotent. 因此,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": "skwee357@domain.com"
}
PUT /users/1
{
"email": "skwee357@gmail.com" // new email address
}
GET /users/1
{
"email": "skwee357@gmail.com" // 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. 由于我们使用了PUT,但只提供了email ,因此这是该实体中唯一的东西。 This has resulted in data loss. 这导致数据丢失。
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 was idempotent. 在上面的示例中,PATCH 是幂等的。 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. 您进行了更改,但是如果一次又一次地进行相同的更改,它将始终返回相同的结果:您已将电子邮件地址更改为新值。
GET /users/1
{
"username": "skwee357",
"email": "skwee357@domain.com"
}
PATCH /users/1
{
"email": "skwee357@gmail.com" // new email address
}
GET /users/1
{
"username": "skwee357",
"email": "skwee357@gmail.com" // email address was changed
}
PATCH /users/1
{
"email": "skwee357@gmail.com" // new email address... again
}
GET /users/1
{
"username": "skwee357",
"email": "skwee357@gmail.com" // 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": "skwee357@olddomain.com",
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "10001"
}
After a PATCH, you have a modified entity: 进行PATCH之后,您拥有一个已修改的实体:
PATCH /users/1
{"email": "skwee357@newdomain.com"}
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@newdomain.com", // 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. 如果您随后重复应用PATCH,则将继续获得相同的结果:电子邮件已更改为新值。 A goes in, A comes out, therefore this is idempotent. 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. 一个小时后,当您去煮咖啡并休息一会后,其他人会带上他们自己的PATCH。 It seems the Post Office has been making some changes. 看起来邮局已经在进行一些更改。
PATCH /users/1
{"zip": "12345"}
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@newdomain.com", // 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. 由于邮局发出的此PATCH本身与电子邮件无关,因此如果重复使用邮政编码,则也将得到相同的结果:邮政编码被设置为新值。 A goes in, A comes out, therefore this is also idempotent. A进去,A出来,因此这也是幂等的。
The next day, you decide to send your PATCH again. 第二天,您决定再次发送PATCH。
PATCH /users/1
{"email": "skwee357@newdomain.com"}
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@newdomain.com",
"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). 许多服务器将通过发送修改后的新实体状态(如果有)来响应您的REST请求。 So, when you get this response back, 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. 因此,您的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 . 要对该问题进行全面处理,我再次请您参考Jason Hoetger的回答 。 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. 我只想谈谈它,因为老实说我认为我无法比他已经更好地回答这部分。
#4楼
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. 虽然丹Lowe的出色答卷非常彻底回答了关于PUT和PATCH之间的差异OP的问题,它的答案,为什么补丁不幂等的问题是不太正确的。
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调用都不会更改该资源。
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 do change the resource. 相反,非等幂运算是其中f(f(x))!= f(x)的情况,对于PATCH,可以将其表示为:用补丁文档对资源进行PATCH之后,随后的PATCH用同一补丁文件确实会更改资源。
To illustrate a non-idempotent PATCH, suppose there is a /users resource, and suppose that calling GET /users returns a list of users, currently: 为了说明非幂等的PATCH,假设有一个/ users资源,并且假设调用GET /users返回了一个用户列表,当前:
[{ "id": 1, "username": "firstuser", "email": "firstuser@example.org" }]
Rather than PATCHing /users/{id}, as in the OP's example, suppose the server allows PATCHing /users. 假定服务器允许PATCHing / user,而不是像OP的示例中那样PATCHing / users / {id}。 Let's issue this PATCH request: 让我们发出以下PATCH请求:
PATCH /users
[{ "op": "add", "username": "newuser", "email": "newuser@example.org" }]
Our patch document instructs the server to add a new user called newuser to the list of users. 我们的补丁文档指示服务器将一个名为newuser的新用户添加到用户列表中。 After calling this the first time, GET /users would return: 第一次调用此命令后, GET /users将返回:
[{ "id": 1, "username": "firstuser", "email": "firstuser@example.org" },
{ "id": 2, "username": "newuser", "email": "newuser@example.org" }]
Now, if we issue the exact same PATCH request as above, what happens? 现在,如果我们发出与上述完全相同的 PATCH请求,会发生什么? (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 /users returns: (在这个例子的目的,让我们假设/用户资源允许重复的用户名)的“OP”是“添加”,这样一个新的用户添加到列表中,并且随后的GET /users的回报:
[{ "id": 1, "username": "firstuser", "email": "firstuser@example.org" },
{ "id": 2, "username": "newuser", "email": "newuser@example.org" },
{ "id": 3, "username": "newuser", "email": "newuser@example.org" }]
The /users resource has changed again , even though we issued the exact same PATCH against the exact same endpoint. 即使我们针对完全相同的端点发出了完全相同的 PATCH,/ users资源也再次发生了变化。 If our PATCH is f(x), f(f(x)) is not the same as f(x), and therefore, this particular PATCH is not idempotent . 如果我们的PATCH是f(x),则f(f(x))与f(x)不同,因此, 该特定PATCH不是幂等的 。
Although PATCH isn't guaranteed to be idempotent, there's nothing in the PATCH specification to prevent you from making all PATCH operations on your particular server idempotent. 尽管不能保证 PATCH是幂等的,但是PATCH规范中没有任何内容可以阻止您对特定服务器进行幂等的所有PATCH操作。 RFC 5789 even anticipates advantages from idempotent PATCH requests: 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. 补丁请求可以为幂等这样的方式,这也有助于防止坏的结果,从碰撞在相似的时间框架上的同一资源两个贴请求之间发出。
In Dan's example, his PATCH operation is, in fact, idempotent. 在Dan的示例中,他的PATCH操作实际上是幂等的。 In that example, the /users/1 entity changed between our PATCH requests, but not because of our PATCH requests; 在该示例中,/ users / 1实体在我们的PATCH请求之间进行了更改,但不是因为我们的PATCH请求而发生的;而是 it was actually the Post Office's different patch document that caused the zip code to change. 实际上是邮局的其他补丁文档导致了邮政编码的更改。 The Post Office's different PATCH is a different operation; 邮局的不同PATCH是不同的操作; if our PATCH is f(x), the Post Office's PATCH is g(x). 如果我们的PATCH为f(x),则邮局的PATCH为g(x)。 Idempotence states that f(f(f(x))) = f(x) , but makes no guarantes about f(g(f(x))) . 幂等性指出f(f(f(x))) = f(x) ,但对f(g(f(x)))不做任何保证。
#5楼
The difference between PUT and PATCH is that: PUT和PATCH之间的区别在于:
- PUT is required to be idempotent. PUT必须是幂等的。 In order to achieve that, you have to put the entire complete resource in the request body. 为了实现这一点,您必须将整个完整资源放入请求正文中。
- PATCH can be non-idempotent. PATCH可以是非幂等的。 Which implies it can also be idempotent in some cases, such as the cases you described. 这意味着它在某些情况下(例如您描述的情况)也可以是幂等的。
PATCH requires some "patch language" to tell the server how to modify the resource. PATCH需要某种“补丁语言”来告诉服务器如何修改资源。 The caller and the server need to define some "operations" such as "add", "replace", "delete". 调用方和服务器需要定义一些“操作”,例如“添加”,“替换”,“删除”。 For example: 例如:
GET /contacts/1
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@olddomain.com",
"state": "NY",
"zip": "10001"
}
PATCH /contacts/1
{
[{"operation": "add", "field": "address", "value": "123 main street"},
{"operation": "replace", "field": "email", "value": "abc@myemail.com"},
{"operation": "delete", "field": "zip"}]
}
GET /contacts/1
{
"id": 1,
"name": "Sam Kwee",
"email": "abc@myemail.com",
"state": "NY",
"address": "123 main street",
}
Instead of using explicit "operation" fields, the patch language can make it implicit by defining conventions like: 修补程序语言可以不使用显式的“ operation”字段,而可以通过定义如下约定来使其隐含:
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": "abc@myemail.com",
"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. 通过上面提到的操作,PATCH仍然是幂等的。 But if you define operations like: "increment" or "append", you can easily see it won't be idempotent anymore. 但是,如果您定义诸如“ increment”或“ append”之类的操作,则可以轻松地看到它不再是幂等的。
#6楼
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. 在本规范定义的请求方法中,PUT,DELETE和安全请求方法是幂等的。
(...) (...)
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. 例如,如果客户端发送一个PUT请求,并且在接收到任何响应之前关闭了基础连接,则客户端可以建立一个新的连接并重试幂等请求。 It knows that repeating the request will have the same intended effect, even if the original request succeeded, though the response might differ. 它知道即使原始请求成功,重复请求也将具有相同的预期效果,尽管响应可能会有所不同。
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的答案的最后一个示例(我不想在这里here窃)确实表明,PATCH请求可以是非幂等的(比第一个示例更自然) 杰森·霍特格(Jason Hoetger)的答案 )。
Indeed, let's make the example slightly more precise by making explicit one possible intend for the first client. 确实,让我们通过使第一个客户明确打算使用该示例来使示例更加精确。 Let's say that this client goes through the list of users with the project to check their emails and zip codes. 假设该客户端浏览了该项目的用户列表,以检查其电子邮件和邮政编码。 He starts with user 1, notices that the zip is right but the email is wrong. 他从用户1开始,注意到邮政编码正确,但是电子邮件错误。 He decides to correct this with a PATCH request, which is fully legitimate, and sends only 他决定使用完全合法的PATCH请求来更正此问题,并且仅发送
PATCH /users/1
{"email": "skwee357@newdomain.com"}
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 effect of the client, since we end up with an incorrect zip. 同时,另一个客户端(错误地)修改了用户1的zip。然后,第二次发送相同的PATCH请求并不能达到客户端的预期效果 ,因为我们最终得到了不正确的zip。 Hence the method is not idempotent in the sense of the RFC. 因此,在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请求将覆盖自第一个请求以来的所有更改。
本文探讨了REST API中PUT和PATCH方法的区别,特别是幂等性的概念。PUT方法要求提供完整的资源实体,而PATCH则是用于部分更新。文章通过实例解释了PUT和PATCH在幂等性上的差异,指出PATCH并非总是幂等的,而PUT理论上应该是幂等的。此外,还讨论了在实际应用中如何理解和使用这两种方法。
1929

被折叠的 条评论
为什么被折叠?



