当前位置: 首页 > 面试题库 >

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

卞琨
2023-03-14
问题内容

首先,一些定义:

PUT在9.6节RFC
2616中
定义:

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

PATCH在RFC 5789中定义:

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

同样根据RFC2616,第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毫无意义。我错了吗?

问题答案:

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

阅读我的答案后,建议您也阅读JasonHoetger对这个问题的出色答案,我将尝试使我的答案更好,而不仅仅是从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仅包含正在修改的html" target="_blank">参数(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不是幂等的呢?

要对该问题进行全面处理,我再次请您参考JasonHoetger的回答。我只想保留它,因为老实说我认为我不能比他已经更好地回答这部分。



 类似资料:
  • 首先,一些定义: PUT在第9.6节RFC 2616中定义: PUT方法请求将封闭的实体存储在提供的request-uri下。如果Request-URI引用的是一个已经存在的资源,则所包含的实体应被视为驻留在原始服务器上的实体的修改版本。如果请求URI没有指向现有资源,并且该URI能够被请求的用户代理定义为新资源,则源服务器可以使用该URI创建资源。 修补程序定义在RFC 5789: 补丁方法请求

  • 我不知道PATCH和PUT方法之间的确切区别。有人能告诉我什么时候我们应该用适当的例子来使用这个方法吗?

  • 本篇为Powershell攻击指南——黑客后渗透之道系列之实战篇,主要介绍的一些实用的利用方式与利用场景和一些实用工具。 在实际的渗透环境中我们利用Powershell的方式无非两种: 使用编码的方式对Powershell命令进行编码之后运行 远程下载Powershell代码之后直接运行 两种方式各有利弊,第一种比较方便直接编码即可执行,第二种需要到一台拥有公网IP的机器,那么在实际的渗透环境中如

  • 我试图在我的客户端使用JAX RS的CXF实现补丁方法。首先,我将补丁注释定义为 参考这里的内容:如何为JAX-RS提供@PATCH注释? 然后我发现@PATCH被添加到CXF 3.1.2中,所以我在我的专家的pom中更改了版本.xml并且内部有,代码实际上看起来与我上面发布的内容完全相同。 但是,当我尝试在我的服务定义上使用此注释时 正如我在上面发布的queston链接中所说的,我以< code

  • WebAssembly 的整体目标 定义了 WebAssembly 适合做什么。哪些是在 Web 平台可以实现的,哪些是非 Web 平台可以实现的。下面给出了一个不完善的无序列表,包括应用/领域/计算等方向,它们可能将从 WebAssembly 中受益的, WebAssamlby 的设计过程中也会将它们做为用例。 在浏览器中 更好的让一些语言和工具可以编译到 Web 平台运行。 图片/视频编辑。

  • 4.1 使用场景 Camel管理端内部定义了很多模型对象。接口接受的数据均是模型对象对应的json。 Camel管理端,提供了界面和接口两种方式。这两种方式均可对Server、Upstream等配置信息进行更改,以及发布配置文件。那么什么场景下应该使用接口呢? 通过界面对配置文件进行更改,是一种可视化操作,非常的直观。界面方式适用于编辑配置信息,如增加location等操作。 如果通过界面进行配置