在REST API实际场景中使用PUT与PATCH方法

首先,一些定义:

PUT在第9.6节RFC 2616中定义:

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

PATCH在rfc5789中定义:

PATCH方法请求一系列变化中描述的请求实体应用于由请求标识的资源-URI。

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

现在让我们看一个真实的例子。当我用数据{username: 'skwee357', email: 'skwee357@domain.example'}/users发送POST并且服务器能够创建资源时,它将响应201和资源位置(让我们假设/users/1),并且任何下一次对GET/users/1的调用都将返回{id: 1, username: 'skwee357', email: '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的用途感到困惑。这就引出了我的第二个(也是主要)问题:

  • PUT和PATCH有什么区别?我在某个地方读到PUT可能用于取代特定资源下的整个实体,所以应该发送完整的实体(而不是像PATCH那样发送一组属性)。在这种情况下,真正的实际用法是什么?你什么时候想在特定资源URI上替换/覆盖一个实体,为什么这样的操作不被认为是更新/修补实体?
751547 次浏览

我对此也很好奇,发现了一些有趣的文章。我可能无法完全回答你的问题,但这至少提供了更多的信息。

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

HTTP RFC指定PUT必须采用完整的新资源表示作为请求实体。这意味着如果例如只提供某些属性,这些属性应该被删除(即设置为空)。

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

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

这将有效地更新电子邮件。PUT可能不太有效的原因是您只真正修改一个字段并包含用户名是无用的。下一个示例显示了差异。

/users/1PUT {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/

PUT和PATCH请求之间的区别反映在服务器处理封闭实体以修改资源的方式由请求URI标识。在PUT请求中,封闭的实体被认为是存储在源服务器,客户端请求存储的版本是但是,使用PATCH,封闭的实体包含一组描述资源当前如何驻留在应修改原始服务器以生成新版本。PATCH方法影响请求URI标识的资源,并且它还可能会对其他资源产生副作用;例如,新资源可能会通过PATCH的应用程序创建或修改现有的。

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

您或多或少地将PATCH视为更新字段的一种方式。因此,不是发送部分对象,而是发送操作。即用值替换电子邮件。

文章就此结束。

值得一提的是,PATCH并不是真正为REST设计的API,因为Fielding的论文没有定义任何部分但是,Roy Fielding自己说PATCH是他为最初的HTTP/1.1提案创建了一些东西,因为部分PUT永远不会是RESTful。当然,您不会传输完整的表示,但REST不需要表示全部完成

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

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

0:当我第一次花时间阅读REST时,幂等性是一个令人困惑的概念,很难正确理解。在我最初的答案中,我仍然没有完全正确,正如进一步的评论(和1)所示。有一段时间,我拒绝广泛更新这个答案,以避免有效地抄袭Jason,但我现在正在编辑它,因为,好吧,我被要求(在评论中)。

读完我的答案后,我建议你也阅读这个问题的0,我会努力让我的答案变得更好,而不是简单地从杰森那里偷东西。

为什么PUT是幂等的?

正如您在RFC 2616引文中指出的那样,PUT被认为是幂等的。当您放置资源时,以下两个假设正在发挥作用:

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

  2. 您提供的实体是完整的(整个实体)。

让我们来看看你的一个例子。

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

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

## /users/1
{"username": "skwee357","email": "skwee357@domain.example"}

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

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

您可以使用PATCH完成相同的操作。它可能看起来像这样:

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

您会立即注意到这两者之间的差异。PUT包含此用户的所有参数,但PATCH仅包含正在修改的参数(email)。

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

由于PUT请求包括整个实体,如果你重复发出同一个请求,它应该总是有相同的结果(你发送的数据现在是实体的全部数据)。因此PUT是幂等的。

使用PUT错误

如果您在PUT请求中使用上述PATCH数据会发生什么?

GET /users/1{"username": "skwee357","email": "skwee357@domain.example"}PUT /users/1{"email": "skwee357@gmail.com"       // new email address}
GET /users/1{"email": "skwee357@gmail.com"      // new email address... and nothing else!}

(对于这个问题,我假设服务器没有任何特定的必填字段,并且允许这种情况发生……实际情况可能并非如此。

由于我们使用了PUT,但只提供了email,现在这是该实体中的唯一内容。这导致了数据丢失。

这个例子是为了说明的目的——实际上不要这样做(除非你的意图是删除省略的字段,当然……那么你正在使用PUT,因为它应该被使用)。这个PUT请求在技术上是幂等的,但这并不意味着它不是一个糟糕的、破碎的想法。

PATCH如何是幂等的?

在上面的示例中,PATCH幂等。您进行了更改,但如果您一次又一次地进行相同的更改,它总是会返回相同的结果:您将电子邮件地址更改为新值。

GET /users/1{"username": "skwee357","email": "skwee357@domain.example"}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文档,修改不同的属性,不会使PATCH成为非幂等的。

假设在过去的某个时间,添加了一个用户。这是您开始的状态。

{"id": 1,"name": "Sam Kwee","email": "skwee357@olddomain.example","address": "123 Mockingbird Lane","city": "New York","state": "NY","zip": "10001"}

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

PATCH /users/1{"email": "skwee357@newdomain.example"}
{"id": 1,"name": "Sam Kwee","email": "skwee357@newdomain.example",    // the email changed, yay!"address": "123 Mockingbird Lane","city": "New York","state": "NY","zip": "10001"}

如果您然后重复应用PATCH,您将继续得到相同的结果:电子邮件被更改为新值。A进去,A出来,因此这是幂等的。

一个小时后,在你去煮咖啡休息一下之后,其他人带着他们自己的PATCH来了。看来邮局已经做出了一些改变。

PATCH /users/1{"zip": "12345"}
{"id": 1,"name": "Sam Kwee","email": "skwee357@newdomain.example",  // 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出来,因此这是幂等。

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

PATCH /users/1{"email": "skwee357@newdomain.example"}
{"id": 1,"name": "Sam Kwee","email": "skwee357@newdomain.example","address": "123 Mockingbird Lane","city": "New York","state": "NY","zip": "12345"}

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

我原来的答案错了什么

我想做一个重要的区分(我在最初的答案中弄错了一些东西)。许多服务器会通过发回新的实体状态来响应你的REST请求,以及你的修改(如果有的话)。所以,当你找回这个反应时,它是不同的你昨天拿回来的那个,因为邮政编码不是你上次收到的那个。然而,你的请求与邮政编码无关,只与电子邮件有关。所以你的PATCH文档仍然是幂等的——你在PATCH中发送的电子邮件现在是实体上的电子邮件地址。

那么什么时候PATCH不是幂等的呢?

对于这个问题的全面处理,我再次建议你参考Jason Hoetger的回答,它已经完全回答了这个问题。

虽然Dan Lowe的优秀回答非常彻底地回答了OP关于PUT和PATCH之间区别的问题,但它对为什么PATCH不是幂等的问题的回答并不完全正确。

为了说明为什么PATCH不是幂等性的,可以从幂等性的定义开始(从维基百科开始):

术语幂等更全面地用于描述如果执行一次或多次将产生相同结果的操作[…]幂等函数是对任何值x具有属性f(f(x))=f(x)的函数。

在更易于访问的语言中,幂等PATCH可以定义为:使用补丁文档对资源进行PATCHing后,对具有相同补丁文档的同一资源的所有后续PATCH调用都不会更改该资源。

相反,非幂等操作是f(f(x)) ! = f(x),对于PATCH可以声明为:在使用补丁文档PATCHing资源之后,后续PATCH调用具有相同补丁文档的相同资源更改资源。

为了说明一个非幂等PATCH,假设有一个 /users资源,并假设调用GET /users返回一个用户列表,当前:

[{ "id": 1, "username": "firstuser", "email": "firstuser@example.org" }]

与OP示例中 /users/{id}的PATCHing不同,假设服务器允许PATCHing /users.让我们发出这个PATCH请求:

PATCH /users[{ "op": "add", "username": "newuser", "email": "newuser@example.org" }]

我们的补丁文档指示服务器将一个名为newuser的新用户添加到用户列表中。第一次调用后,GET /users将返回:

[{ "id": 1, "username": "firstuser", "email": "firstuser@example.org" },{ "id": 2, "username": "newuser", "email": "newuser@example.org" }]

现在,如果我们像上面一样发出完全相同 PATCH请求,会发生什么?(为了这个例子,让我们假设 /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" }]

/users资源已更改再次,即使我们针对完全相同端点发布了完全相同 PATCH。如果我们的PATCH是f(x),则f(f(x))与f(x)不相同,因此,这个特定的PATCH不是幂等的

尽管PATCH不是保证的幂等,但PATCH规范中没有任何内容阻止您在特定服务器上进行所有PATCH操作。RFC 5789甚至预测了幂等PATCH请求的优势:

PATCH请求可以以幂等的方式发出,这也有助于防止两个人之间的碰撞产生不良后果在类似的时间范围内对同一资源的PATCH请求。

在Dan的例子中,他的PATCH操作实际上是幂等的。在那个例子中, /users/1实体在我们的PATCH请求之间发生了变化,但不是我们的PATCH请求之间发生了变化;实际上是邮局的不同补丁文档导致了邮政编码的变化。邮局的不同PATCH是不同的操作;如果我们的PATCH是f(x),邮局的PATCH是g(x)。幂等声明f(f(f(x))) = f(x),但对f(g(f(x)))没有保证。

PUT和PATCH的区别在于:

  1. PUT必须是幂等的。为了实现这一点,您必须将整个完整资源放在请求正文中。
  2. 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请求正文中:

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

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

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

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

使用我上面提到的操作,PATCH仍然是幂等的。但是如果你定义像“增量”或“追加”这样的操作,你可以很容易地看到它不再是幂等的。

让我更仔细地引用和评论RFC 7231第4.2.2节,在前面的评论中已经引用过:

请求方法被认为是“幂等的”,如果预期的效果使用该方法的多个相同请求的服务器是相同的作为单个此类请求的效果。的请求方法由本规范定义的PUT、DELETE和安全请求方法是幂等的

(…)

区分幂等方法,因为请求可以如果通信失败发生在客户端能够读取服务器的响应。例如,如果客户端发送PUT请求,底层连接关闭在收到任何响应之前,客户端可以建立一个新的连接并重试幂等请求。它知道重复请求将具有相同的预期效果,即使原始请求成功,但响应可能不同。

那么,在重复请求幂等方法之后,应该是什么呢?不是服务器状态,也不是服务器响应,而是的预期效果。特别是,“从客户端的角度”,该方法应该是幂等的。现在,我认为这个观点表明,Dan Lowe的回答中的最后一个例子,我不想在这里剽窃,确实表明PATCH请求可以是非幂等的(以比Jason Hoetger的回答中的例子更自然的方式)。

事实上,让我们通过为第一个客户端明确一个可能的打算来使示例稍微更精确。假设该客户端通过项目的用户列表来检查他们的电子邮件邮政编码。他从用户1开始,注意到邮政编码是正确的,但电子邮件是错误的。他决定使用完全合法的PATCH请求纠正这一点,并仅发送

PATCH /users/1{"email": "skwee357@newdomain.example"}

由于这是唯一的更正。现在,由于网络问题,请求失败,几个小时后自动重新提交。与此同时,另一个客户端(错误地)修改了用户1的zip。然后,第二次发送相同的PATCH请求不会实现客户端的预期效果,因为我们最终得到了一个不正确的zip。因此该方法不是RFC意义上的幂等。

相反,如果客户端使用PUT请求来更正电子邮件,将用户1的所有属性与电子邮件一起发送到服务器,即使请求稍后必须重新发送并且用户1已被修改,他的预期效果也将实现-因为第二个PUT请求将覆盖自第一个请求以来的所有更改。

在我的愚见中,幂等性意味着:

  • 放:

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

  • 补丁:

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

假设一个对象定义如下:

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

我用:

{颜色:“红色”}

生成的对象是:

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

然后,其他一些用户用以下方式修补这辆车:

{type:'hatchback'}

因此,结果对象是:

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

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

{颜色:“红色”}

生成的对象是:

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

什么是不同于我以前有什么!

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

TLDR-停用版本

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

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

为了结束对幂等性的讨论,我应该注意到可以通过两种方式在REST上下文中定义幂等性。

资源是一个函数,其共同域是字符串的类。换句话说,资源是String × Any的子集,其中所有键都是唯一的。让我们调用资源Res的类。

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

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

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

现在,通过修复x: Res(信息上讲,使用柯里化),PUT(x: Res)PATCH(x: Res)Res → Res类型的单变量函数。

  1. 函数g: Res → Res被称为全局幂等,当g ○ g == g时,即对于任何y: Resg(g(y)) = g(y)

  2. x: Res为资源,k = x.keys。函数g = f(x)称为左幂等,当每个y: Res时,我们有g(g(y))|ₖ == g(y)|ₖ。这基本上意味着如果我们查看应用的键,结果应该是相同的。

所以,PATCH(x)不是全局幂等的,而是左幂等的。这里的左幂等性很重要:如果我们修补了资源的几个键,我们希望这些键在再次修补时保持不变,我们不关心资源的其余部分。

当RFC谈论PATCH不是幂等的时候,它是在谈论全局幂等性。好的,它不是全局幂等的,否则它将是一个失败的操作。


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

  • 首先,PATCH用于集合,尽管PATCH被定义为处理映射/字典/键值对象。
  • 如果有人真的想将PATCH应用于集合,那么应该使用一个自然翻译:t: Set<T> → Map<T, Boolean>,用x in A iff t(A)(x) == True定义。使用这个定义,补丁是幂等的。
  • 在这个例子中,没有使用这种翻译,而是PATCH像POST一样工作。首先,为什么要为对象生成ID?它是什么时候生成的?如果首先将对象与集合的元素进行比较,如果没有找到匹配的对象,那么生成ID,那么程序应该以不同的方式工作({id: 1, email: "me@site.example"}必须与{email: "me@site.example"}匹配,否则程序总是坏的,PATCH不可能打补丁)。如果在检查集合之前生成ID,那么程序也坏了。

可以举例说明PUT是非幂等的,在这个例子中破坏了一半的东西:

  • 生成的附加功能的例子是版本控制。可以记录单个对象的更改次数。在这种情况下,PUT不是幂等的:PUT /user/12 {email: "me@site.example"}第一次导致{email: "...", version: 1},第二次导致{email: "...", version: 2}
  • 弄乱ID,每次更新对象时都可能生成一个新ID,从而导致非幂等PUT。

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


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

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

其他人都回答了PUT vs PATCH。我只是想回答原始问题标题的一部分:“…在REST API现实生活场景中”。在现实世界中,我遇到了这种情况,我的互联网应用程序有一个RESTful服务器和一个关系数据库,其中客户表“宽”(大约40列)。我错误地使用了PUT,但认为它就像一个SQL更新命令,并且没有填写所有的列。问题:1)一些列是可选的(所以空白是有效的答案),2)许多列很少更改,3)一些列用户不允许更改,例如上次购买日期的时间戳,4)一列是自由格式的文本“评论”列,用户努力填写半页的客户服务评论,如配偶姓名询问或通常的订单,5)我当时正在开发一个互联网应用程序,担心数据包大小。

PUT的缺点是它迫使你发送一个大的信息包(所有列包括整个评论列,即使只有少数内容发生了变化),并且2个以上用户同时编辑同一个客户的多用户问题(所以最后一个按Update的人获胜)。PATCH的缺点是你必须跟踪视图/屏幕端的变化,并且有一些智能来只发送变化的部分。Patch的多用户问题仅限于编辑同一客户的同一列。

这里有一个很好的解释——

https://blog.segunolalive.com/posts/restful-api-design-%E2%80%94-put-vs-patch/#:~: text=RFC%205789, not%20所需%20to%20be%20iemempotent

一个正常的Payload-//情节1上的房子{地址:“情节1”,所有者:'segun',类型:双工颜色:“绿色”,房间:'5',厨房:'1',windows: 20}PUT for Updated-//PUT请求有效负载以更新绘图1上House的窗口{地址:“情节1”,所有者:'segun',类型:双工颜色:“绿色”,房间:'5',厨房:'1',windows:21}注意:在上面的有效负载中,我们正在尝试将Windows从20更新到21。

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

由于PATCH不是幂等的,失败的请求不会在网络上自动重新尝试。此外,如果向不存在的URL发出PATCH请求,例如试图更换不存在的建筑物的前门,它应该简单地失败,而不会创建新的资源,这与PUT不同,PUT会使用有效负载创建新的资源。想想看,在房子地址上有一扇门会很奇怪。

tl; dr版本

  • POST:用于创建实体

  • :用于更新/替换现有实体,其中您希望存储实体的必须将整个陈述

  • 补丁:用于更新只发送需要更新的字段所在的实体

方法非常适合以表格格式更新数据,例如在关系数据库或存储等实体中。根据用例,它可用于部分更新数据或替换整个实体。这将始终是幂等的。

补丁方法可用于更新(或重组)存储在本地文件系统或无sql数据库中的json或xml格式的数据。这可以通过提及请求中要执行的操作来执行,例如向json对象添加/删除/移动键值对。删除操作可用于删除键值对,重复请求将导致错误,因为键被提前删除,使其成为非幂等方法。

文章包含与PATCH方法相关的详细信息。

我会试着用通俗的术语总结我所理解的(也许它有帮助)

补丁不是完全幂等的(它可以在没有人更改实体的另一个字段的理想情况下)。

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

所以你不能称它为幂等,如果它不能覆盖100%的情况。也许这对一些人来说并不重要,但对其他人来说是

考虑到你关于幂等性的问题,我可能有点偏离主题,但我希望你考虑进化论。

假设你有以下元素:

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

如果你用PUT修改,你必须给出对象的整个表示:

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

现在更新模式,并添加一个字段phone

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

现在用PUT再次更新它,它将设置phone为null。为了避免这种糟糕的副作用,您必须在每次更新模式时更新所有修改元素的组件。蹩脚。

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