Dmi*_*sev 614 rest json http put http-method
首先,一些定义:
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
),并且/users/1
将返回对GET的任何下一次调用{id: 1, username: 'skwee357', email: 'skwee357@domain.com'}
.
现在假设我要修改我的电子邮件.电子邮件修改被视为"一组更改",因此我应该修补/users/1
" 补丁文档 ".在我的情况下,它将是一个json {email: 'skwee357@newdomain.com'}
.然后服务器返回200(假设权限正常).这让我想到第一个问题:
PATCH是一个相对较新的动词(2010年3月引入的RFC),它解决了"修补"或修改一组字段的问题.在引入PATCH之前,每个人都使用PUT来更新资源.但是在引入PATCH之后,让我感到困惑的是当时使用的PUT是什么?这让我想到了第二个(也是主要的)问题:
/users
替换整个集合.在引入PATCH之后,在特定实体上发布PUT是没有意义的.我错了吗?Dan*_*owe 863
注意:当我第一次花时间阅读有关REST时,幂等性是一个令人困惑的概念,试图做到正确.我仍然没有在原来的答案中得到正确的答案,因为进一步的评论(和Jason Hoetger的回答)已经表明了.有一段时间,我拒绝广泛地更新这个答案,以避免有效地剽窃杰森,但我现在正在编辑它,因为,我被要求(在评论中).
在阅读了我的答案后,我建议你也阅读Jason Hoetger对这个问题的出色回答,并且我会尝试更好地回答,而不是简单地从Jason那里偷窃.
正如您在RFC 2616引文中所述,PUT被认为是幂等的.当您投入资源时,这两个假设在起作用:
您指的是实体,而不是集合.
您提供的实体是完整的(整个实体).
我们来看看你的一个例子.
{ "username": "skwee357", "email": "skwee357@domain.com" }
Run Code Online (Sandbox Code Playgroud)
如果您/users
按照建议将此文档发布到,则可能会返回一个实体,例如
## /users/1
{
"username": "skwee357",
"email": "skwee357@domain.com"
}
Run Code Online (Sandbox Code Playgroud)
如果您想稍后修改此实体,请在PUT和PATCH之间进行选择.PUT可能如下所示:
PUT /users/1
{
"username": "skwee357",
"email": "skwee357@gmail.com" // new email address
}
Run Code Online (Sandbox Code Playgroud)
您可以使用PATCH完成相同的操作.这可能看起来像这样:
PATCH /users/1
{
"email": "skwee357@gmail.com" // new email address
}
Run Code Online (Sandbox Code Playgroud)
你会发现这两者之间存在差异.PUT包含该用户的所有参数,但PATCH仅包括正在修改的参数(email
).
使用PUT时,假设您正在发送完整实体,并且该完整实体将替换该URI处的任何现有实体.在上面的示例中,PUT和PATCH实现了相同的目标:它们都更改了此用户的电子邮件地址.但是PUT通过替换整个实体来处理它,而PATCH只更新提供的字段,而不管其他字段.
由于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!
}
Run Code Online (Sandbox Code Playgroud)
(我假设为了这个问题的目的,服务器没有任何特定的必填字段,并允许这种情况发生......实际情况可能并非如此.)
由于我们使用了PUT,但只提供了email
,现在这是该实体中唯一的东西.这导致数据丢失.
此示例仅用于说明目的 - 实际上并不是这样做的.这个PUT请求在技术上是幂等的,但这并不意味着它不是一个可怕的,破碎的想法.
在上面的例子中,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
}
Run Code Online (Sandbox Code Playgroud)
我最初有一些例子,我认为它们显示出非幂等性,但它们具有误导性/不正确性.我将保留示例,但使用它们来说明不同的事情:针对同一实体的多个PATCH文档,修改不同的属性,不会使PATCHes成为非幂等的.
让我们说在过去的某个时候,添加了一个用户.这是你开始的状态.
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@olddomain.com",
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "10001"
}
Run Code Online (Sandbox Code Playgroud)
在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"
}
Run Code Online (Sandbox Code Playgroud)
如果您随后重复应用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
}
Run Code Online (Sandbox Code Playgroud)
由于来自邮局的这个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"
}
Run Code Online (Sandbox Code Playgroud)
您的补丁与昨天的效果相同:它设置了电子邮件地址.A进去了,A出来了,因此这也是幂等的.
我想画出一个重要的区别(我原来的答案中出错了).许多服务器将通过发送回新的实体状态(如果有的话)来响应您的REST请求.因此,当您收到此回复时,它与您昨天收到的回复不同,因为邮政编码不是您上次收到的邮政编码.但是,您的请求与邮政编码无关,只与电子邮件有关.因此,您的PATCH文档仍然是幂等的 - 您在PATCH中发送的电子邮件现在是实体上的电子邮件地址.
为了全面处理这个问题,我再次向您推荐Jason Hoetger的答案.我只是要离开它,因为老实说,我认为我不能比现在更好地回答这部分.
Jas*_*ger 299
虽然Dan Lowe的优秀答案非常彻底地回答了OP关于PUT和PATCH之间差异的问题,但是对于为什么PATCH不是幂等的问题的答案并不完全正确.
为了说明PATCH不是幂等的原因,从idenotence的定义开始(来自维基百科):
术语幂等用于更全面地用于描述如果执行一次或多次将产生相同结果的操作[...]幂等函数是具有属性f(f(x))= f(x)的函数任何值x.
在更易于访问的语言中,幂等PATCH可以定义为:在使用补丁文档修补资源之后,对具有相同补丁文档的同一资源的所有后续PATCH调用都不会更改资源.
相反,非幂等操作是f(f(x))!= f(x),其中PATCH可以表示为:在使用补丁文档修补资源之后,后续PATCH调用同一资源相同的补丁文档确实会更改资源.
为了说明非幂等PATCH,假设有一个/ users资源,并假设该调用GET /users
返回一个用户列表,目前:
[{ "id": 1, "username": "firstuser", "email": "firstuser@example.org" }]
Run Code Online (Sandbox Code Playgroud)
而不是PATCHing/users/{id},如在OP的示例中,假设服务器允许PATCHing/users.让我们发出这个PATCH请求:
PATCH /users
[{ "op": "add", "username": "newuser", "email": "newuser@example.org" }]
Run Code Online (Sandbox Code Playgroud)
我们的补丁文档指示服务器将新用户添加到用户newuser
列表中.在第一次调用它之后,GET /users
将返回:
[{ "id": 1, "username": "firstuser", "email": "firstuser@example.org" },
{ "id": 2, "username": "newuser", "email": "newuser@example.org" }]
Run Code Online (Sandbox Code Playgroud)
现在,如果我们发出与上面完全相同的 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" }]
Run Code Online (Sandbox Code Playgroud)
/ 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).Idempotence指出f(f(f(x))) = f(x)
,但不保证f(g(f(x)))
.
Kal*_*ade 70
我对此也很好奇,并发现了一些有趣的文章.我可能不会完全回答你的问题,但这至少提供了一些信息.
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'}
Run Code Online (Sandbox Code Playgroud)
这将有效地更新电子邮件.PUT可能不太有效的原因是你唯一真正修改一个字段并包含用户名是没用的.下一个例子显示了差异.
/users/1
PUT {id: 1, email: 'newemail@domain.com'}
Run Code Online (Sandbox Code Playgroud)
现在,如果PUT是根据规范设计的,那么PUT会将用户名设置为null,您将获得以下内容.
{id: 1, username: null, email: 'newemail@domain.com'}
Run Code Online (Sandbox Code Playgroud)
使用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" }
]
Run Code Online (Sandbox Code Playgroud)
您或多或少将PATCH视为更新字段的方法.因此,您不是通过部分对象发送,而是发送操作.即用值替换电子邮件.
文章以此结尾.
值得一提的是,PATCH并非真正设计用于真正的REST API,因为Fielding的论文没有定义任何部分修改资源的方法.但是,Roy Fielding自己说PATCH是他为最初的HTTP/1.1提案创建的东西,因为部分PUT永远不会RESTful.当然,您没有转移完整的表示,但REST无论如何都不需要表示完整.
现在,我不知道我是否特别同意这篇文章,正如许多评论员指出的那样.发送部分表示可以很容易地描述变化.
对我来说,我使用PATCH混合使用.在大多数情况下,我会将PUT视为PATCH,因为到目前为止我注意到的唯一真正的区别是PUT"应该"将缺失值设置为null.它可能不是"最正确"的方式,但好运编码完美.
Bij*_*jan 65
PUT => 为现有资源设置所有新属性。
PATCH => 部分更新现有资源(并非所有属性都需要)。
Bin*_* Ni 14
PUT和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",
}
Run Code Online (Sandbox Code Playgroud)
补丁语言不是使用显式的"操作"字段,而是通过定义以下约定来使其隐式:
在PATCH请求体中:
根据上述约定,示例中的PATCH可以采用以下形式:
PATCH /contacts/1
{
"address": "123 main street",
"email": "abc@myemail.com",
"zip":
}
Run Code Online (Sandbox Code Playgroud)
这看起来更简洁,用户友好.但用户需要了解基本惯例.
通过我上面提到的操作,PATCH仍然是幂等的.但是如果你定义像"increment"或"append"这样的操作,你就可以很容易地看到它不再是幂等的.
小智 8
在我看来,幂等性意味着:
我发送了完整的资源定义,因此 - 结果资源状态与 PUT 参数所定义的完全相同。每次我使用相同的 PUT 参数更新资源时 - 结果状态完全相同。
我只发送了资源定义的一部分,因此其他用户可能会同时更新该资源的其他参数。因此,具有相同参数及其值的连续补丁可能会导致不同的资源状态。例如:
假设一个对象定义如下:
CAR:
- color: black,
- type: sedan,
- seats: 5
Run Code Online (Sandbox Code Playgroud)
我用以下方法修补它:
{color: 'red'}
Run Code Online (Sandbox Code Playgroud)
结果对象是:
CAR:
- color: red,
- type: sedan,
- seats: 5
Run Code Online (Sandbox Code Playgroud)
然后,其他一些用户用以下方法修补了这辆车:
{type: 'hatchback'}
Run Code Online (Sandbox Code Playgroud)
所以,得到的对象是:
CAR:
- color: red,
- type: hatchback,
- seats: 5
Run Code Online (Sandbox Code Playgroud)
现在,如果我再次使用以下命令修补该对象:
{color: 'red'}
Run Code Online (Sandbox Code Playgroud)
结果对象是:
CAR:
- color: red,
- type: hatchback,
- seats: 5
Run Code Online (Sandbox Code Playgroud)
和我之前得到的有什么不同!
这就是为什么 PATCH 不是幂等的,而 PUT 是幂等的。
考虑到您关于幂等性的问题,我可能有点偏离主题,但我希望您考虑进化性。
假设您有以下元素:
{
"username": "skwee357",
"email": "skwee357@domain.example"
}
Run Code Online (Sandbox Code Playgroud)
如果使用 PUT 进行修改,则必须给出对象的完整表示:
PUT /users/1
{
"username": "skwee357",
"email": "skwee357@newdomain.example"
}
Run Code Online (Sandbox Code Playgroud)
现在您更新架构并添加一个字段phone
:
PUT /users/1
{
"username": "skwee357",
"email": "skwee357@newdomain.example",
"phone": "123-456-7890"
}
Run Code Online (Sandbox Code Playgroud)
现在以同样的方式再次使用 PUT 更新它,它将设置phone
为 null。为了避免这种不良的副作用,您必须在每次更新架构时更新所有修改元素的组件。瘸。
通过使用 PATCH,就不会出现此问题,因为 PATCH 只更新给定的字段。所以,在我看来,你应该使用 PATCH 来修改一个元素(无论它是否真的幂等)。这就是现实生活中的经验回报。
归档时间: |
|
查看次数: |
361686 次 |
最近记录: |