正確使用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
[email protected]

雖然它使事情變得清晰,而且它看起來像一個很好的來決定公開和不公開的方式,這個解決方案在API中引入了很多複雜性(控制器中的更多操作(action)、路由定義、文檔等)。然而,它是兼容REST的,並不是一個糟糕的解決方案,但還有一個更好的選擇:補丁(PATCH)

PATCH 是一個HTTP方法(即動詞)已在RFC 5789中描述。最初的想法是提出一個修改現有HTTP資源的新方法。這種方法的最大問題是人們誤解了其用法。不!PATCH 不是嚴格發送一個更新的值,不是本文的第一段中描述的整個資源。請現在停止做這件事!這是不正確的:
P
ATCH /users/123
{ “email”: “[email protected]” }
而且,這也是不正確的:
PATCH /users/[email protected]

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章