REST API - PUT与PATCH的实例 [英] REST API - PUT vs PATCH with real life examples

查看:168
本文介绍了REST API - PUT与PATCH的实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,一些定义:

PUT在第9.6节RFC 2616


PUT方法请求存储所包含的实体在提供的Request-URI下。如果Request-URI引用已存在的资源,则封闭的实体应该被视为驻留在源服务器上的实体的修改版本。如果Request-URI未指向现有资源,并且该URI能够被请求用户代理定义为新资源,则源服务器可以使用该URI创建资源。

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.

PATCH在 RFC 5789 中定义:

PATCH is defined in RFC 5789:


PATCH方法请求将
请求实体中描述的一组更改应用于由Request-
URI。

The PATCH method requests that a set of changes described in the request entity be applied to the resource identified by the Request- URI.

同样根据 RFC 2616第9.1.2节 PUT是幂等的,而PATCH则不是。

Also according to RFC 2616 Section 9.1.2 PUT is Idempotent while PATCH is not.

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

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'}.

现在假设我要修改我的电子邮件。电子邮件修改被视为一组更改,因此我应该使用 / users / 1 / rfc5789#section-2>补丁文件。在我的情况下,它将是一个json {email:'skwee357@newdomain.com'} 。然后服务器返回200(假设权限正常)。这让我想到第一个问题:

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". In my case it would be a json {email: 'skwee357@newdomain.com'}. The server then returns 200 (assuming permission are ok). This brings me to first question:


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

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

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 resource. But after PATCH was introduced, it leaves me confused what is PUT used for then? And this brings me to second (and the main) question:


  • PUT和PATCH之间的真正区别是什么?我读过PUT可能用于替换特定资源下的整个实体,因此应该发送完整实体(而不是像PATCH一样发送属性集)。这种情况的实际用法是什么?您希望何时替换/覆盖特定资源URI下的实体以及为什么不将此类操作视为更新/修补实体?我在PUT中看到的唯一实际用例是在集合上发布PUT,即 / users 来替换整个集合。在引入PATCH之后,在特定实体上发布PUT是没有意义的。我错了吗?

  • Whats the real difference between PUT and 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). 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? The only practical use case I see for PUT is issuing a PUT on 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?

推荐答案

注意:我第一次花的时候阅读关于REST的时间,幂等是一个令人困惑的概念,试图做到正确。我仍然没有在原来的答案中得到正确的答案,因为进一步的评论(以及 Jason Hoetger的回答)已经显示出来了。有一段时间,我拒绝广泛地更新这个答案,以避免有效地剽窃杰森,但我现在正在编辑它,因为,我被要求(在评论中)。

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).

在阅读完答案后,我建议你也阅读 Jason Hoetger对这个问题的出色答复,我会尝试无需简单地从杰森那里偷窃就能让我的答案更好。

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.

正如你所说的那样你的RFC 2616引用,PUT被认为是幂等的。当您投入资源时,这两个假设在起作用:

As you noted in your RFC 2616 citation, PUT is considered idempotent. When you PUT a resource, these two assumptions are in play:


  1. 您指的是实体,而不是集合。

  1. 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" }

如果您按照建议将此文档发布到 / users ,那么您可能会收回一个实体,例如

If you POST this document to /users, as you suggest, then you might get back an entity such as

## /users/1

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

如果您想稍后修改此实体,请在PUT和PATCH之间进行选择。 PUT可能如下所示:

If you want to modify this entity later, you choose between PUT and PATCH. A PUT might look like this:

PUT /users/1
{
    "username": "skwee357",
    "email": "skwee357@gmail.com"       // new email address
}

您可以使用PATCH完成相同的操作。这可能是这样的:

You can accomplish the same using PATCH. That might look like this:

PATCH /users/1
{
    "email": "skwee357@gmail.com"       // new email address
}

你会发现这两者之间存在差异。 PUT包含此用户的所有参数,但PATCH仅包括正在修改的参数(电子邮件)。

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时,假设您正在发送完整实体,并且该完整实体替换该URI处的任何现有实体。在上面的示例中,PUT和PATCH实现了相同的目标:它们都改变了该用户的电子邮件地址。但是PUT通过替换整个实体来处理它,而PATCH只更新所提供的字段,而不管其他字段。

When using PUT, it is assumed that you are sending the complete entity, and that complete entity replaces any 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请求包含整个实体,如果你重复发出相同的请求,它应该始终具有相同的结果(您发送的数据现在是实体的整个数据)。因此,PUT是幂等的。

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中使用上述PATCH数据会发生什么请求?

What happens if you use the above PATCH data in a PUT request?

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!
}

(我假设为了这个问题的目的,服务器没有' t有任何特定的必填字段,并允许这种情况发生......实际情况可能并非如此。)

(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.)

因为我们使用了PUT,但只提供了电子邮件,现在这是该实体中唯一的东西。这导致数据丢失。

Since we used PUT, but only supplied email, now that's the only thing in this entity. This has resulted in data loss.

此示例仅用于说明目的 - 实际上并不是这样做的。这个PUT请求在技术上是幂等的,但这并不意味着它不是一个可怕的,破碎的想法。

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.

在上面的例子中,PATCH 幂等的。您进行了更改,但如果您反复进行相同的更改,它将始终返回相同的结果:您将电子邮件地址更改为新值。

In the above example, PATCH was idempotent. 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
}



我的原始示例,为准确性而修复



我最初有一些例子,我认为它们显示出非幂等性,但是你误导/不正确。我将保留这些示例,但使用它们来说明不同的事情:针对同一实体的多个PATCH文档,修改不同的属性,不会使PATCHes成为非幂等的。

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.

让我们说在过去的某个时候,添加了一个用户。这是你开始的状态。

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"
}

在PATCH之后,你有一个修改后的实体:

After a PATCH, you have a modified entity:

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"
}

如果您再重复应用PATCH,您将继续获得相同的结果:电子邮件已更改为新值。 A进来了,A出来了,所以这是幂等的。

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。邮局似乎一直在做一些改变。

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 /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
}

由于来自邮局的这个PATCH不关心电子邮件,只有邮政编码,如果重复应用,它也会得到相同的结果:邮政编码设置为新值。 A进入,A出来,因此这幂等。

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 also idempotent.

第二天,你决定再次发送你的PATCH。

The next day, you decide to send your PATCH again.

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"
}

您的补丁与昨天的效果相同:它设置了电子邮件地址。 A进去了,A出来了,所以这也是幂等的。

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.

我想画出一个重要的区别(我在原来的答案中出错了)。许多服务器将通过发送回新的实体状态来响应您的REST请求(如果有的话)。因此,当您收到此响应时,它与您昨天收到的不同,因为邮政编码不是您上次收到的邮政编码。但是,您的请求与邮政编码无关,只与电子邮件有关。因此,您的PATCH文档仍然是幂等的 - 您在PATCH中发送的电子邮件现在是实体上的电子邮件地址。

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 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.

要完整处理这个问题,我再次推荐您 Jason Hoetger的回答。我只是想放弃它,因为老实说,我认为我不能比现在更好地回答这一部分。

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.

这篇关于REST API - PUT与PATCH的实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆