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


680

首先,一些定义:

PUT在9.6节RFC 2616中定义:

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

PATCH在RFC 5789中定义:

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

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

现在让我们看一个真实的例子。当我/users对数据执行POST {username: 'skwee357', email: 'skwee357@domain.com'}并服务器能够创建资源时,它将以201和资源位置(假设/users/1)响应,并且对GET的下一次调用/users/1将返回{id: 1, username: 'skwee357', email: 'skwee357@domain.com'}

现在,让我们说我想修改我的电子邮件。电子邮件修改被视为“一组更改”,因此我应该/users/1使用“ 补丁文档 ”来进行修补。在我的情况下,它将是json文档:{email: 'skwee357@newdomain.com'}。然后,服务器返回200(假设允许)。这使我想到第一个问题:

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

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

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

1
a)是RFC 2616,而不是2612。b)RFC 2616已过时,PUT的当前规范在greenbytes.de/tech/webdav/rfc7231.html#PUT中,c)我没有收到您的问题;是不是很明显,PUT可以用来替换任何资源,而不仅仅是集合,d)在引入PATCH之前,人们通常使用POST,e)最后,是的,一个特定的 PATCH请求(取决于补丁格式)可以是幂等的;只是这不是一般情况。
朱利安·雷施克

如果有帮助,我已经写了一篇关于PATCH vs PUT eq8.eu/blogs/36-patch-vs-put-and-the-patch-json-syntax-war
等效8年

5
简单:POST在集合中创建一个项目。PUT替换项。PATCH修改项目。发布时,将计算新项目的URL并在响应中返回,而PUT和PATCH在请求中需要URL。对?
汤姆·罗素

这篇文章可能有用:POST vs PUT vs PATCH:mscharhag.com/api-design/http-post-put-patch
micha

Answers:


941

注意:当我第一次花时间阅读有关REST的知识时,幂等性是一个难以理解的概念。正如我进一步的评论(以及Jason Hoetger的答案)所显示的那样,我在原始答案中仍然没有完全正确。有一阵子,我拒绝广泛地更新此答案,以避免有效地Ja窃Jason,但我现在正在编辑它,因为(在评论中)我被要求这样做。

阅读我的回答后,建议您也阅读Jason Hoetger对这个问题的出色回答,我将尝试使我的回答更好,而不仅仅是从Jason那里窃取。

为什么PUT是幂等的?

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

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

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

让我们来看一个例子。

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

如果/users按照建议将文档发布到,则可能会找回诸如

## /users/1

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

如果以后要修改此实体,请在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.com"
}
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.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文档,修改不同的属性,不会使PATCH成为非幂等的。

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

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

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

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

一个小时后,当您去煮咖啡并休息一下之后,其他人就会带上他们自己的PATCH。看起来邮局已经在进行一些更改。

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出来,因此这也是幂等的。

第二天,您决定再次发送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"
}

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

我的原始答案有误

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

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

有关此问题的完整说明,我再次请您参考Jason Hoetger的回答。我只是要保留它,因为老实说我认为我不能比他已经更好地回答这部分。


2
这句话不太正确:“但是它是幂等的:只要A进来,B总是出来”。例如,如果您要GET /users/1在邮局更新邮政编码之前,然后在邮局更新之后再次发出相同的GET /users/1请求,则会得到两个不同的响应(不同的邮政编码)。相同的“ A”(GET请求)正在输入,但是得到的结果不同。但是GET仍然是幂等的。
杰森·霍特杰

@JasonHoetger GET是安全的(假定不会造成任何变化),但并不总是幂等的。它们是有区别的。参见RFC 2616秒。9.1
丹·罗威

1
@DanLowe:绝对可以肯定GET是幂等的。确切地说,在RFC 2616的9.1.2节和更新的规范RFC 7231的4.2.2节中,“在此规范定义的请求方法中,PUT,DELETE和安全请求方法是幂等的。” 幂等性并不意味着“每次发出相同的请求时都会得到相同的响应”。7231 4.2.2继续说:“即使原始请求成功,重复请求也将具有相同的预期效果,尽管响应可能会有所不同。
Jason Hoetger

1
@JasonHoetger我承认这一点,但是我不知道它与这个答案有什么关系,它讨论了PUT和PATCH,甚至从未提及GET ...
Dan Lowe

1
嗯,@ JasonHoetger的评论将其清除了:只有多个幂等方法请求的结果状态而不是响应是相同的。
汤姆·罗素

328

尽管Dan Lowe的出色回答非常彻底地回答了OP关于PUT和PATCH之间的区别的问题,但其对为什么PATCH不等幂的问题的回答并不十分正确。

为了显示PATCH为什么不是幂等的,它有助于从幂等的定义开始(来自Wikipedia):

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

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

相反,非等幂运算是其中f(f(x))!= f(x)的情况,对于PATCH,可以将其表示为:用补丁文档对资源进行PATCH之后,随后的PATCH用同一补丁文件确实会更改资源。

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

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

假定服务器允许PATCHing / user,而不是像OP的示例中那样PATCHing / 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" }]

即使我们针对完全相同的端点发出了完全相同的 PATCH ,/ users资源也再次发生变化。如果我们的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)))


11
假设服务器还允许在发出PUT /users,这也会使PUT也非幂等。所有这些归结为服务器如何设计来处理请求。
Uzair Sajid

13
因此,我们只能使用PATCH操作来构建API。然后,使用http VERBS对资源进行CRUD操作的REST原理变成了什么?我们不是在使PATCH边境绅士们变得过于复杂吗?
Bohr,2017年

6
如果在集合(例如/users)上实现了PUT,则任何PUT请求都应替换该集合的内容。因此,PUT /users应当期望用户的集合并删除所有其他用户。这是幂等的。您不太可能在/ users端点上执行此操作。但是类似的东西/users/1/emails可能是一个集合,因此允许用一个新集合替换整个集合可能是完全有效的。
Vectorjohn

5
尽管此答案提供了幂等的一个很好的例子,但我相信这可能会在典型的REST场景中造成混乱。在这种情况下,您将获得带有附加op操作的PATCH请求,该操作会触发特定的服务器端逻辑。这将要求服务器和客户端知道要传递给op字段的特定值,以触​​发服务器端工作流。在更直接的REST场景中,这种op功能是不好的做法,应该很可能直接通过HTTP动词来处理。
ivandov

7
我永远不会考虑针对集合发布PATCH,仅发布和删除。这真的做过吗?因此,PATCH在所有实际目的上都可以视为等幂的吗?
汤姆·罗素

72

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

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

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

/users/1
PUT {id: 1, email: 'newemail@domain.com'}

现在,如果PUT是根据规范设计的,那么PUT会将用户名设置为null,您将获得以下信息。

{id: 1, username: null, email: 'newemail@domain.com'}

使用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。这样做可能不是“最正确”的方法,但是祝您好运。


7
可能值得添加:在William Durand的文章(和RFC 6902)中,有一些示例,其中“ op”是“ add”。这显然不是幂等的。
约翰尼斯·布罗德沃尔

2
或者,您可以更轻松地使用RFC 7396合并修补程序,而不必构建修补程序JSON。
Piotr Kula

对于nosql表,patch和put之间的区别很重要,因为nosql没有列
stackdave

17

PUT和PATCH之间的区别在于:

  1. PUT必须是幂等的。为了实现这一点,您必须将整个完整资源放入请求正文中。
  2. PATCH可以是非幂等的。这意味着它在某些情况下(例如您描述的情况)也可以是幂等的。

PATCH需要某种“补丁语言”来告诉服务器如何修改资源。调用方和服务器需要定义一些“操作”,例如“添加”,“替换”,“删除”。例如:

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

修补程序语言可以不使用显式的“ operation”字段,而可以通过定义如下约定来使其隐含:

在PATCH请求正文中:

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

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

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

看起来更加简洁和用户友好。但是,用户需要了解基本约定。

通过上述操作,PATCH仍然是幂等的。但是,如果您定义诸如“ increment”或“ append”之类的操作,则可以轻松地看到它不再是幂等的。


7

TLDR-精简版

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

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


3

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

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

(...)

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

那么,在反复要求幂等方法之后,“相同”应该是什么?不是服务器状态,也不是服务器响应,而是预期的效果。特别地,该方法应该“从客户的角度考虑”是幂等的。现在,我认为这种观点表明,Dan Lowe的答案的最后一个示例(我不想在这里here窃)确实表明,PATCH请求可以是非幂等的(比第一个示例更自然)杰森·霍特格(Jason Hoetger)的答案)。

确实,让我们通过使第一个客户明确打算使用该示例来使示例更加精确。假设该客户端浏览了该项目的用户列表,以检查其电子邮件邮政编码。他从用户1开始,注意到邮政编码正确,但是电子邮件错误。他决定使用完全合法的PATCH请求来更正此问题,并且仅发送

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

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

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


2

以我的拙见,幂等是指:

  • 放:

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

  • 补丁:

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

假定一个定义如下的对象:

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

我用:

{红色'}

结果对象是:

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

然后,其他一些用户使用以下方法修补此汽车:

{type:'hatchback'}

因此,结果对象是:

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

现在,如果我再次使用以下方法修补此对象:

{红色'}

结果对象是:

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

与以前的内容有什么不同!

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


1

在结束关于幂等性的讨论时,我应该指出,可以通过两种方式在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,例如,对GET和没有多大意义POST,因为它不关心持久性)。

现在,通过修复x: Res(从信息上来讲,使用currying)PUT(x: Res)PATCH(x: Res)type是单变量函数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应用于集合,则应使用自然翻译:t: Set<T> → Map<T, Boolean>,用定义x in A iff t(A)(x) == True。使用此定义,修补是幂等的。
  • 在该示例中,未使用此翻译,而是PATCH像POST一样工作。首先,为什么为该对象生成一个ID?何时生成?如果首先将对象与集合中的元素进行比较,并且如果没有找到匹配的对象,则将生成ID,然后程序应再次以不同的方式工作({id: 1, email: "me@site.com"}必须与匹配{email: "me@site.com"},否则程序始终会中断并且PATCH无法补丁)。如果在检查设置之前生成了ID,则程序再次被破坏。

可以举一个PUT是非幂等的例子,其中破坏了该例子中一半的东西:

  • 具有生成的附加功能的示例是版本控制。可以记录单个对象上的更改数量。在这种情况下,PUT不是幂等的:第一次出现PUT /user/12 {email: "me@site.com"}结果{email: "...", version: 1},并且{email: "...", version: 2}第二次出现。
  • 加上这些ID,每次更新对象都会产生一个新ID,从而导致非幂等的PUT。

以上所有示例都是可能遇到的自然示例。


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


-1

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.