javascript
摆脱“空”检查的盛宴:使用JSON Patch正确执行PATCH
今天,我們將就REST(ful)服務和API進行一次對話,更準確地說,圍繞許多經驗豐富的開發人員正在努力解決的一個獨特主題。 為了使事情更直觀,我們將討論Web API,其中REST(ful)原則遵循HTTP協議并大量利用HTTP方法的語義,(通常但不一定)使用JSON表示狀態。
一種特殊的HTTP方法非常引人注目,盡管其含義聽起來很簡單,但實現方法遠非如此。 是的,我們正在尋找您, PATCH 。 那到底是什么問題呢? 這只是更新,對不對? 是的,實質上,在基于HTTP的REST(ful)Web服務的上下文中, PATCH方法的語義是資源的部分更新。 現在,Java開發人員將如何做到這一點? 這就是樂趣的開始。
讓我們來看一個非常簡單的圖書管理API示例,該示例使用最新的JSR 370:RESTful Web服務的Java API(JAX-RS 2.1)規范(最終包括@PATCH注釋!)和出色的Apache CXF框架進行建模 。 我們的資源只是一個非常簡單的Book類。
public class Book {private String title;private Collection>String< authors;private String isbn; }您將如何使用PATCH方法實施部分更新? 可悲的是,強力解決方案,即null宴席,在這里顯然是贏家。
@PATCH @Path("/{isbn}") @Consumes(MediaType.APPLICATION_JSON) public void update(@PathParam("isbn") String isbn, Book book) {final Book existing = bookService.find(isbn).orElseThrow(NotFoundException::new);if (book.getTitle() != null) {existing.setTitle(book.getTitle());}if (book.getAuthors() != null) {existing.setAuthors(book.getAuthors());}// And here it goes on and on ...// ... }簡而言之,這是空保護的PUT克隆。 可能有人會聲稱這行得通,并在這里宣布勝利。 但是希望對我們大多數人來說,這種方法顯然存在很多缺陷,因此永遠不應該采用。 備擇方案? 是的,絕對是RFC-6902:JSON補丁 ,目前還不是官方標準,但已經實現了。
RFC-6902:JSON修補程序通過表達一系列操作以應用于JSON文檔,從而徹底改變了游戲。 為了說明這一思想的實際效果,讓我們從更改書名的簡單示例入手,以所需的結果來描述。
{ "op": "replace", "path": "/title", "value": "..." }看起來很干凈,那么添加作者呢? 簡單 …
{ "op": "add", "path": "/authors", "value": ["...", "..."] }太棒了,賣完了,但是……從實現角度看,這似乎需要很多工作,不是嗎? 如果我們依賴最新和最強大的JSR 374:JSON處理1.1的Java API,它完全支持RFC-6902:JSON Patch,則不是真的。 有了合適的工具,這次讓我們做對了。
org.glassfishjavax.json1.1.2有趣的是,沒有多少人知道Apache CXF和通常的任何JAX-RS兼容框架都與JSON-P緊密集成并支持其基本數據類型。 對于Apache CXF ,只需添加cxf-rt-rs-extension-providers模塊依賴項即可:
org.apache.cxfcxf-rt-rs-extension-providers3.2.2并在服務器工廠bean中注冊JsrJsonpProvider ,例如:
@Configuration public class AppConfig {@Beanpublic Server rsServer(Bus bus, BookRestService service) {JAXRSServerFactoryBean endpoint = new JAXRSServerFactoryBean();endpoint.setBus(bus);endpoint.setAddress("/");endpoint.setServiceBean(service);endpoint.setProvider(new JsrJsonpProvider());return endpoint.create();} }將所有部分連接在一起,我們的PATCH操作可以使用JSR 374:僅用于JSON Processing 1.1的Java API來實現,僅需幾行:
@Service @Path("/catalog") public class BookRestService {@Inject private BookService bookService;@Inject private BookConverter converter;@PATCH@Path("/{isbn}")@Consumes(MediaType.APPLICATION_JSON)public void apply(@PathParam("isbn") String isbn, JsonArray operations) {final Book book = bookService.find(isbn).orElseThrow(NotFoundException::new);final JsonPatch patch = Json.createPatch(operations);final JsonObject result = patch.apply(converter.toJson(book));bookService.update(isbn, converter.fromJson(result));} }BookConverter執行Book類及其JSON表示之間的轉換(反之亦然),我們正在手工進行操作,以說明JSR 374:JSON處理1.1的Java API提供的另一種功能。
@Component public class BookConverter {public Book fromJson(JsonObject json) {final Book book = new Book();book.setTitle(json.getString("title"));book.setIsbn(json.getString("isbn"));book.setAuthors(json.getJsonArray("authors").stream().map(value -> (JsonString)value).map(JsonString::getString).collect(Collectors.toList()));return book;}public JsonObject toJson(Book book) {return Json.createObjectBuilder().add("title", book.getTitle()).add("isbn", book.getIsbn()).add("authors", Json.createArrayBuilder(book.getAuthors())).build();} }最后,讓我們將這個簡單的JAX-RS 2.1 Web API包裝到漂亮的Spring Boot信封中。
@SpringBootApplication public class BookServerStarter { public static void main(String[] args) {SpringApplication.run(BookServerStarter.class, args);} }并運行它。
mvn spring-boot:run結束討論,讓我們通過在目錄中故意添加一本不完整的書 ,來探討一些更實際的例子。
$ curl -i -X POST http://localhost:19091/services/catalog -H "Content-Type: application\json" -d '{"title": "Microservice Architecture","isbn": "978-1491956250","authors": ["Ronnie Mitra","Matt McLarty"]}'HTTP/1.1 201 Created Date: Tue, 20 Feb 2018 02:30:18 GMT Location: http://localhost:19091/services/catalog/978-1491956250 Content-Length: 0在本書的描述中,我們要糾正一些錯誤,即將標題設置為完整的“微服務體系結構:原則,實踐和文化的 一致性 ” ,并包括失蹤的合著者Irakli Nadareishvili和Mike Amundsen 。 借助我們不久前開發的API,這很容易。
$ curl -i -X PATCH http://localhost:19091/services/catalog/978-1491956250 -H "Content-Type: application\json" -d '[{ "op": "add", "path": "/authors/0", "value": "Irakli Nadareishvili" },{ "op": "add", "path": "/authors/-", "value": "Mike Amundsen" },{ "op": "replace", "path": "/title", "value": "Microservice Architecture: Aligning Principles, Practices, and Culture" }]'HTTP/1.1 204 No Content Date: Tue, 20 Feb 2018 02:38:48 GMT前兩個操作的路徑引用可能看起來有些混亂,但是不用擔心,讓我們澄清一下。 因為authors是一個集合(或就JSON數據類型而言,是一個數組),所以我們可以使用RFC-6902:JSON Patch數組索引符號來指定要插入新元素的確切位置。 第一個操作使用索引'0'來表示頭部位置,而第二個操作使用'-'占位符來簡化說“添加到集合的末尾”。 如果我們在更新后立即檢索到該書,則應該看到我們所做的修改完全按照我們的要求進行了應用。
$ curl http://localhost:19091/services/catalog/978-1491956250{"title": "Microservice Architecture: Aligning Principles, Practices, and Culture","isbn": "978-1491956250","authors": ["Irakli Nadareishvili","Ronnie Mitra","Matt McLarty","Mike Amundsen"] }干凈,簡單而強大。 公平地說,要付出代價是一種額外的JSON操作形式(以應用補丁程序),但是值得付出努力嗎? 我相信是……
下次您要設計新穎的REST(ful)Web API時 ,請認真考慮RFC-6902:JSON補丁以支持資源的PATCH實現。 我相信還將與JAX-RS進行更緊密的集成(如果還沒有的話),以直接支持JSONPatch類及其家族。
最后但并非最不重要的一點是,在本文中,我們僅涉及服務器端實現,但JSR 374:用于JSON處理1.1的Java API也包括方便的客戶端支架,從而對補丁提供了完整的編程控制。
final JsonPatch patch = Json.createPatchBuilder().add("/authors/0", "Irakli Nadareishvili").add("/authors/-", "Mike Amundsen").replace("/title", "Microservice Architecture: Aligning Principles, Practices, and Culture").build();完整的項目資源可在Github上找到 。
翻譯自: https://www.javacodegeeks.com/2018/02/run-away-null-checks-feast-patch-properly-json-patch.html
總結
以上是生活随笔為你收集整理的摆脱“空”检查的盛宴:使用JSON Patch正确执行PATCH的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑放静电怎么放(电脑放静电脑怎么放静电
- 下一篇: 我的世界电脑版石斧(我的世界电脑版石斧伤