繁体   English   中英

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

[英]Use of PUT vs PATCH methods in REST API real life scenarios

首先,一些定义:

PUT 在第 9.6 节 RFC 2616中定义:

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

PATCH 在RFC 5789中定义:

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

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

现在让我们来看一个真实的例子。 当我使用数据{username: 'skwee357', email: 'skwee357@domain.example'} POST 到/users并且服务器能够创建资源时,它将响应 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 上的实体,为什么不考虑更新/修补实体这样的操作? 我看到的 PUT 的唯一实际用例是在集合上发出 PUT,即/users来替换整个集合。 在引入 PATCH 之后,在特定实体上发布 PUT 就没有意义了。 我错了吗?

注意:当我第一次花时间阅读 REST 时,幂等性是一个难以理解的概念。 正如进一步的评论(和Jason Hoetger 的回答)所显示的那样,我的原始答案仍然没有完全正确。 有一段时间,我一直拒绝广泛更新这个答案,以避免有效地抄袭杰森,但我现在正在编辑它,因为,好吧,我被要求(在评论中)。

看完我的回答后,我建议你也阅读一下Jason Hoetger 对这个问题的出色回答,我会尽量让我的回答变得更好,而不是简单地从 Jason 那里偷东西。

为什么 PUT 是幂等的?

正如您在 RFC 2616 引用中所指出的,PUT 被认为是幂等的。 当你 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 请求在技术上是幂等的,但这并不意味着它不是一个糟糕的、错误的想法。

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 不是幂等的,它有助于从幂等的定义开始(来自Wikipedia ):

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

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

相反,非幂等操作是 f(f(x)) != f(x),对于 PATCH 可以表示为:在使用补丁文档对资源进行 PATCH 之后,随后的 PATCH 调用使用相同的补丁文件确实会更改资源。

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

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

假设服务器允许 PATCHing /users,而不是像 OP 的示例中那样修补 /users/{id}。 让我们发出这个 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 资源允许重复的用户名。)“op”是“add”,所以一个新用户被添加到列表中,随后的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" }]

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

TLDR - 简化版

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

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

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

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

HTTP RFC 规定 PUT 必须采用全新的资源表示作为请求实体。 这意味着,例如,如果仅提供某些属性,则应删除这些属性(即设置为 null)。

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

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

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

/users/1
PUT {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 请求之间的区别体现在服务器处理封闭实体以修改由 Request-URI 标识的资源的方式上。 在 PUT 请求中,包含的实体被认为是存储在源服务器上的资源的修改版本,并且客户端请求替换存储的版本。 然而,对于 PATCH,封闭的实体包含一组指令,描述如何修改当前驻留在源服务器上的资源以生成新版本。 PATCH 方法会影响 Request-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。 这可能不是“最正确”的方法,但祝你编码完美。

tl;博士版

  • POST : 用于创建实体

  • PUT :用于更新/替换现有实体,您必须在其中发送您希望存储的实体的整个表示

  • PATCH :用于更新仅发送需要更新的字段的实体

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. 如果某个字段的值为空,则表示删除该字段。

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

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

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

通过我上面提到的操作,PATCH 仍然是幂等的。 但是如果你定义像“increment”或者“append”这样的操作,你可以很容易地看到它不再是幂等的了。

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

  • 放:

我发送了一个完整的资源定义,所以 - 生成的资源状态与 PUT 参数定义的完全相同。 每次我使用相同的 PUT 参数更新资源时 - 结果状态完全相同。

  • 修补:

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

假设一个对象定义如下:

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

我修补它:

{红色'}

结果对象是:

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

然后,其他一些用户对这辆车进行了修补:

{类型:'掀背车'}

所以,结果对象是:

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

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

{红色'}

结果对象是:

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

和我以前的有什么不同!

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

让我更仔细地引用和评论RFC 7231 部分 4.2.2 ,已在之前的评论中引用:

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

(...)

幂等方法的区别在于,如果在客户端能够读取服务器的响应之前发生通信故障,请求可以自动重复。 例如,如果客户端发送 PUT 请求,并且在收到任何响应之前关闭了底层连接,则客户端可以建立新连接并重试幂等请求。 它知道重复请求将具有相同的预期效果,即使原始请求成功,尽管响应可能不同。

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

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

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

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

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

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

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

为了结束关于幂等性的讨论,我应该指出,可以通过两种方式在 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: Res , g(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定义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方法可用于更新(或重组)存储在本地文件系统或无 sql 数据库中的 json 或 xml 格式的数据。 这可以通过提及要在请求中执行的操作/操作来执行,例如将键值对添加/删除/移动到 json 对象。 remove 操作可用于删除键值对,重复请求将导致错误,因为该键已被删除,使其成为非幂等方法。 有关 json 数据修补请求,请参阅RFC 6902

这篇文章有关于PATCH方法的详细信息。

一个很好的解释在这里-

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

A Normal Payload- // 地块 1 上的房子 { 地址:'地块 1',所有者:'segun',类型:'duplex',颜色:'green',房间:'5',厨房:'1',窗户: 20 } PUT For Updated- // PUT 请求有效负载以更新地块 1 上的房屋窗口 { 地址:'地块 1',所有者:'segun',类型:'duplex',颜色:'green',房间:'5' , kitchens: '1', windows: 21 } 注意:在上面的有效载荷中,我们试图将 windows 从 20 更新到 21。

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

由于 PATCH 不是幂等的,失败的请求不会在网络上自动重新尝试。 此外,如果对不存在的 url 发出 PATCH 请求,例如尝试替换不存在的建筑物的前门,它应该会失败而不会创建新资源,这与 PUT 不同,后者将使用有效负载创建新资源。 想一想,在一个家庭地址有一扇单独的门会很奇怪。

我将尝试用外行的方式总结我所理解的(也许有帮助)

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

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

因此,如果它不能涵盖 100% 的情况,则不能称其为幂等。 也许这对某些人来说并不那么重要,但对其他人来说

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

考虑你有以下元素:

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

如果使用 PUT 进行修改,则必须给出 object 的完整表示:

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 来修改一个元素(无论它是否真的是幂等的)。 这是真实的体验回归。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM