正确使用Patch——部分更新

原文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都不需要完整的表示。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式。”但是在要求详细讲述它所提出的各个约束,以及如...
    时待吾阅读 3,339评论 0 19
  • 原文链接:https://https://howardwchen.com/2017/09/18/talk-abou...
    守望者Howard阅读 6,243评论 1 6
  • API定义规范 本规范设计基于如下使用场景: 请求频率不是非常高:如果产品的使用周期内请求频率非常高,建议使用双通...
    有涯逐无涯阅读 2,442评论 0 6
  • github地址,欢迎大家提交更新。 express() express()用来创建一个Express的程序。ex...
    Programmer客栈阅读 2,450评论 0 1