原文http://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/
更新- 2016-08-06——自从我写这篇博客后,RFC 7396,引入了 JSON Merge Patch格式,其已创建。它可以被视为“只发送你需要的”格式。因此,,只要符合JSON Merge Patch格式,只需发送你需要更新的更改说明[description of changes]就是有效的。
一个无关的注解,我为滥用“白痴”这个词道歉。我不想粗鲁。相同的词法语不如英文的“强烈”。
修改HTTP资源并不是一个新话题。大多数现有的HTTP或REST api都提供了一种方法来修改资源。他们通常通过在资源上使用PUT方法来提供这样的功能,要求客户发送整个资源的更新值,但这需要一个新近获取的资源,和一个在GET调用和PUT之间不会错过更新的方法。事实上,可能在你之前更新值,它会导致不良的副作用。此外,发送一个完整的资源表示会占用更多的带宽,而且有时必须考虑到它。另外,大多数情况下,您希望更新资源中的一个或两个值,而不是所有的值,所以对于部分更新来说,PUT方法可能不是正确的解决方案,这是用来描述这种用例的术语。
另一种解决方案是公开你希望进行编辑的资源属性,并使用PUT方法发送更新的值。在下面的示例中,用户123的电子邮件属性被公开:
PUT /users/123/email
new.email@example.org
虽然它使事情变得清晰,而且它看起来像一个很好的来决定公开和不公开的方式,这个解决方案在API中引入了很多复杂性(控制器中的更多操作(action)、路由定义、文档等)。然而,它是兼容REST的,并不是一个糟糕的解决方案,但还有一个更好的选择:补丁(PATCH)
PATCH 是一个HTTP方法(即动词)已在RFC 5789中描述。最初的想法是提出一个修改现有HTTP资源的新方法。这种方法的最大问题是人们误解了其用法。不!PATCH 不是严格发送一个更新的值,不是本文的第一段中描述的整个资源。请现在停止做这件事!这是不正确的:
P
ATCH /users/123
{ "email": "new.email@example.org" }
而且,这也是不正确的:
PATCH /users/123?email=new.email@example.org
PATCH方法请求一个更改的集合,在请求实体中描述,必须应用于请求URI标识的资源。这个集合包含的指令描述如何修改当前驻留在源服务器上的资源以生成新版本。你可以认为这是一个差异(diff):
PATCH /users/123
[description of changes]
整个更改集合必须自动被应用,以及API绝不能随便提供部分修改陈述。值得一提的是,要修补(PATCH)的请求实体与正在修改的资源相比是不同的内容类型(content-type)。必须使用定义PATCH语义的媒体类型,否则你将失去该方法的优点,,并且你可以使用PUT或POST。来自RFC:
PUT和PATCH请求之间的差异,反映在服务器处理封闭的实体以修改资源由Request-URI所指定资源的方式上。在PUT请求中,封闭的实体被认为是一个存储在源服务器上的资源的修改版本,并且客户端请求替换存储的版本。然而,使用PATCH,封闭的实体包含一组指令,描述当前驻留在源服务器上的资源应该如何修改产生新版本。PATCH方法会影响Request-URI指定的资源,它也可能会对其他资源产生副作用,也就是说,通过PATCH的应用程序,可以创建新的资源,或者修改现有的。
你可以使用任何你想要的格式作为[description of changes],它的语义是明确定义的。这就是为什么使用补丁只发送更新的值是不合适的。
RFC 6902定义了一个JSON文档结构,用于表示要应用于JSON文档的一系列操作,PATCH 方法适合使用。它看起来像:
[
{ "op": "test", "path": "/a/b/c", "value": "foo" },
{ "op": "remove", "path": "/a/b/c" },
{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] },
{ "op": "replace", "path": "/a/b/c", "value": 42 },
{ "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
{ "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
]
它依赖于在RFC 6901中描述的JSONPointer,以识别JSON文档中的特殊的值,即在HTTP资源表示中。
通过应用PATCH方法修改用户123的电子邮件,它的JSON表示形式如下:
PATCH /users/123
[
{ "op": "replace", "path": "/email", "value": "new.email@example.org" }
]
所以易读的,有表现力的!这是多么美妙,这就是PATCH方法必须使用的。如果它成功了,你会得到一个200的响应。
对于XML爱好者,RFC 5261描述了一个XML patch框架使用XML Path 语言(XPath)选择器来更新现有的XML文档。
截至2014年底(也就是说,在这篇文章的出版),一个新的RFC引入JSON Merge Patch格式,并描述了另一种发送更改的集合的方式。它非常类似于只发送需要更新的内容,但这是显式的application/merge-patch+json内容类型。
总之,PATCH 方法不是一个替代POST或PUT的方法。它适用于增量修改(diff),而不是替换整个资源。要PATCH的请求实体与正在修改的资源相比内容类型不同。它不是一个完整的资源表示,而是描述在资源上应用的更改的资源。
现在,请要么不要使用PATCH方法,要么使用正确的方式!
值得一提的是,PATCH 并不是专为真正的REST API设计的,Fielding的论文没有定义任何部分修改资源的方法。但是,Roy Fielding自己说,PATCH 是(他)为最初的HTTP / 1.1协议而创建的,因为部分PUT从来不是RESTful。确定你不是传递一个完整的表示,但无论如何REST都不需要完整的表示。
网友评论