今天,我们将就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 NadareishviliMike 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相关推荐

  1. java中避免空指针_在Java中避免空检查

    java中避免空指针 对于Java开发人员(从初级到专家)最糟糕的噩梦之一是空对象引用检查. 我很确定您已经看过几次这样的代码: public void addAddressToCustomer(Cu ...

  2. 在Java中避免空检查

    对于Java开发人员(从初级到专家)最糟糕的噩梦之一是空对象引用检查. 我很确定您已经看过几次这样的代码: public void addAddressToCustomer(Customer cust ...

  3. JVM C1 编译优化:空检查擦除

    1. 什么是空检查 在Java里经常会判断一个对象是否为空,如果为空的对象访问方法,字段会抛出空指针异常,而空指针异常为运行异常,如果不抓取这个异常,有的时候会导致程序异常,为了解决这个问题,我们通常 ...

  4. {code:-1,error:`QcloudSecretId`不能为空,请确保 SDK 配置已正确初始化}解决方法

    微信小程序开发交流qq群   173683895    承接微信小程序开发.扫码加微信. 微信小程序云开发登录报错: {"code":-1,"error":&q ...

  5. 使用Git生成patch和应用patch

    1. 在git源码目录下执行 git format-patch <old_sha>...<new_sha> -o <patch_dir> 如 git format- ...

  6. git 生成多个patch_如何用git命令生成Patch和打Patch

    在程序员的日常开发与合作过程中,对于code的生成patch和打patch(应用patch)成为经常需要做的事情.什么是patch?简单来讲,patch中存储的是你对代码的修改,生成patch就是记录 ...

  7. [git 基础] 生成patch 和 打patch 的实例

    简洁点啊,我看 网上 很多搞得很复杂的介绍, 然后,最关键,他们 这边版本不仅抄来抄去,而且,还是一个英文 谷歌网页翻译版本. 搞得好复杂, 还没讲 清楚. 1生成Patch 直接就是在GUI上,选一 ...

  8. 如何用git命令生成Patch和打Patch(git format-Patch和git am)

    在程序员的日常开发与合作过程中,对于code的生成patch和打patch(应用patch)成为经常需要做的事情. 什么是patch?简单来讲,patch中存储的是你对代码的修改 什么是生成patch ...

  9. java里patch什么意思,patch是什么意思

    大家知道patch是什么意思吗?随着时代的发展,越来越多的的新词汇出现在我们的生活中,下面就和康网小编一起来了解一下patch是什么意思吧. patch是什么意思: Patch多指补丁的意思比如内存补 ...

最新文章

  1. 每个设计师需知的40个设计素材站
  2. LwIP 之五 详解动态内存管理 内存堆(mem.c/h)
  3. Spring-AOP动态代理技术(底层代码)
  4. 软件测试需求人才越来越多
  5. FastJson的常用操作
  6. html文字转语音代码,【JavaScript】实现文本转语音功能
  7. Android图表引擎AChartEngine - 简介
  8. 快速设置 Docker 的三种网络代理配置
  9. Linux目录--proc详解
  10. zip压缩脚本(linux)
  11. ImportError: DLL load failed while importing ie_api
  12. java8 stream 原理_【修炼内功】[Java8] Stream是怎么工作的
  13. 高德地图-初始化地图
  14. 常用温度控制方法原理
  15. MMSeg错误:RuntimeError: Default process group has not been initialized
  16. 【全球软件大会】华为前端工程师分享:华为云官网的智能化实践
  17. TypeScript try-catch异常处理
  18. 13个问题,评估开发团队优劣
  19. 两个excel有两列不完全相同的数据,以它们为匹配ID,使一个excel中的数据转移到另外一个excel中
  20. Photoshop界面组成及介绍

热门文章

  1. Hadoop入门(十一)Mapreduce的InputFomrat各种子类
  2. Java多线程:实现方式Thread与Runnable
  3. Oracle入门(十四.19)之触发器简介
  4. vue利用级联选择器实现全国省市区乡村五级菜单联动
  5. 维护win10注册表
  6. Python缩进的几个原则
  7. Mysql对字符串去掉前后空格(trim)或者指定字符
  8. MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established
  9. 毕业设计word 表格标题 图表标题
  10. 摘要算法与加密(以MD5算法为例)