jax-ws和jax-rs_JAX-RS和OpenAPI对Hypermedia API的支持:任重而道远

jax-ws和jax-rs

或早或晚,大多数积极使用REST(ful) Web服务和API的开发人员偶然发现了这种真正的外星事物,即HATEOAS : 超文本作为应用程序状态的引擎 。 对HATEOAS是什么及其与REST的关系的好奇最终将导致发现Richardson成熟度模型 ,该模型使REST和RESTful的行业定义神秘化。 后者是一个启发,但提出了一个问题:这些年来,我们是否一直在做错误的REST ?

让我们尝试从不同的角度回答这个问题。 HATEOAS是REST核心架构约束之一。 从这个角度来看,答案是“是”,为了声明符合REST ,Web服务或API应该支持它。 但是,如果您四处浏览(甚至参考您过去或现在的经验),您可能会发现大多数Web服务和API只是域模型周围的CRUD包装器,而没有HATEOAS支持。 这是为什么? 可能有多个原因,但是从开发人员的工具箱角度来看, HATEOAS的支持不是那么好。

在今天的帖子中,我们将讨论关于HATEOAS的 JAX-RS 2.x必须提供的内容 ,如何从服务器和客户端的角度使用它,以及如何增强OpenAPI v3.0.x规范以暴露超媒体。作为合同的一部分。 如果您很兴奋,请让我们开始吧。

因此,我们的JAX-RS Web API将围绕管理公司及其员工而构建。 基础是Spring Boot和Apache CXF ,其中Swagger是OpenAPI规范的实现。 AppConfig是我们需要定义的唯一配置,以使应用程序启动并运行(这要归功于Spring Boot的自动配置功能)。

 @SpringBootConfiguration  public class AppConfig { @Bean OpenApiFeature createOpenApiFeature() { final OpenApiFeature openApiFeature = new OpenApiFeature(); openApiFeature.setSwaggerUiConfig( new SwaggerUiConfig().url( "/api/openapi.json" )); return openApiFeature; }     @Bean JacksonJsonProvider jacksonJsonProvider() { return new JacksonJsonProvider(); }  } 

CompanyPerson这个模型非常简单(请注意,这两个类之间没有直接关系)。

 public class Company { private String id; private String name;  }  public class Person { private String id; private String email; private String firstName; private String lastName;  } 

该模型通过CompanyResource公开, CompanyResource是典型的JAX-RS资源类,带有@Path注释,此外还带有OpenAPI的@Tag注释。

 @Component  @Path ( "/companies" )  @Tag (name = "companies" )  public class CompanyResource { @Autowired private CompanyService service;  } 

很好,资源类尚未定义端点,因此让我们加强一下。 我们的第一个端点将通过标识符查找公司,并以JSON格式返回其表示形式。 但是,由于我们没有包含任何与员工相关的细节,因此提示消费者(Web UI或任何其他客户端)在哪里查找真是太棒了。 有多种方法可以执行此操作,但是由于我们坚持使用JAX-RS ,因此可以使用开箱即用的Web链接 ( RFC-5988 )。 该代码段包含数千个单词。

 @Produces (MediaType.APPLICATION_JSON)  @GET  @Path ( "{id}" )  public Response getCompanyById( @Context UriInfo uriInfo, @PathParam ( "id" ) String id) { return service .findCompanyById(id) .map(company -> Response .ok(company) .links( Link.fromUriBuilder(uriInfo .getRequestUriBuilder()) .rel( "self" ) .build(), Link.fromUriBuilder(uriInfo .getBaseUriBuilder() .path(CompanyResource. class )) .rel( "collection" ) .build(), Link.fromUriBuilder(uriInfo .getBaseUriBuilder() .path(CompanyResource. class ) .path(CompanyResource. class , "getStaff" )) .rel( "staff" ) .build(id) ) .build()) .orElseThrow(() -> new NotFoundException( "The company with id '" + id + "' does not exists" ));  } 

这里发生的事情很少。 我们关心的是使用ResponseBuilder :: links方法,其中提供了三个链接。 第一个是self ,它本质上是链接上下文(定义为RFC-5988的一部分)。 第二个集合collection ,它指向CompanyResource端点,该端点返回公司列表(也包含在标准关系注册表中)。 最后,第三个是我们自己的员工关系,我们通过一个名为getStaff的方法实现的另一CompanyResource端点组装(我们将看到它不久)。 这些链接将在“ 链接”响应标头中传递,并指导客户端下一步去向。 让我们通过运行该应用程序来实际查看它。

 $ mvn clean package  $ java -jar target/jax-rs- 2.1 -hateaos- 0.0 . 1 -SNAPSHOT.jar 

然后使用curl检查来自此资源端点的响应(不必要的详细信息已被滤除)。

 $ curl -v http: //localhost:8080/api/companies/1  > GET /api/companies/ 1 HTTP/ 1.1  > Host: localhost: 8080  > User-Agent: curl/ 7.47 . 1  > Accept: */*  >  < HTTP/ 1.1 200  < Link: <http: //localhost:8080/api/companies/1>;rel="self"  < Link: <http: //localhost:8080/api/companies/1/staff>;rel="staff"  < Link: <http: //localhost:8080/api/companies>;rel="collection"  < Content-Type: application/json  < Transfer-Encoding: chunked  <  { "id" : "1" , "name" : "HATEOAS, Inc."  } 

链接头在那里,指的是其他感兴趣的端点。 从客户的角度来看,事情看起来也很简单。 Response类提供专用的getLinks方法来包装对Link响应标头的访问,例如:

 final Client client = ClientBuilder.newClient();  try ( final Response response = client .target( " http://localhost:8080/api/companies/ {id}" ) .resolveTemplate( "id" , "1" ) .request() .accept(MediaType.APPLICATION_JSON) .get()) {             final Optional staff = response .getLinks() .stream() .filter(link -> Objects.equals(link.getRel(), "staff" )) .findFirst();             staff.ifPresent(link -> { // follow the link here });  } finally { client.close();  } 

到目前为止,一切都很好。 展望未来,由于HATEOAS本质上是Web API合同的一部分,因此让我们在桌上找出OpenAPI规范所具有的内容。 不幸的是, 到目前为止 尚不支持 HATEOAS ,但是从好的方面来说,存在链接的概念(尽管不应将它们与Web链接混淆,它们有些相似,但并不相同)。 为了说明链接在OpenAPI规范中的用法 ,让我们用Swagger注释装饰端点。

 @Operation ( description = "Find Company by Id" , responses = { @ApiResponse ( content = @Content (schema = @Schema (implementation = Company. class )), links = { @io .swagger.v3.oas.annotations.links.Link( name = "self" , operationRef = "#/paths/~1companies~1{id}/get" , description = "Find Company" , parameters = @LinkParameter (name = "id" , expression = "$response.body#/id" ) ), @io .swagger.v3.oas.annotations.links.Link( name = "staff" , operationRef = "#/paths/~1companies~1{id}~1staff/get" , description = "Get Company Staff" , parameters = @LinkParameter (name = "id" , expression = "$response.body#/id" ) ), @io .swagger.v3.oas.annotations.links.Link( name = "collection" , operationRef = "#/paths/~1companies/get" , description = "List Companies" ) }, description = "Company details" , responseCode = "200" ), @ApiResponse ( description = "Company does not exist" , responseCode = "404" ) }  )  @Produces (MediaType.APPLICATION_JSON)  @GET  @Path ( "{id}" )  public Response getCompanyById( @Context UriInfo uriInfo, @PathParam ( "id" ) String id) { // ...  } 

如果我们运行该应用程序并浏览到浏览器中的http:// localhost:8080 / api / api-docs (这是Swagger UI的托管位置),我们将能够看到每个响应的链接部分。

超媒体API

但是除此之外……您可以使用那里的链接做很多事情(如果您对此主题感兴趣,请注意此问题 )。 吸引公司员工的资源终点看起来非常相似。

 @Operation ( description = "Get Company Staff" , responses = { @ApiResponse ( content = @Content (array = @ArraySchema (schema = @Schema (implementation = Person. class ))), links = { @io .swagger.v3.oas.annotations.links.Link( name = "self" , operationRef = "#/paths/~1companies~1{id}~1staff/get" , description = "Staff" , parameters = @LinkParameter (name = "id" , expression = "$response.body#/id" ) ), @io .swagger.v3.oas.annotations.links.Link( name = "company" , operationRef = "#/paths/~1companies~1{id}/get" , description = "Company" , parameters = @LinkParameter (name = "id" , expression = "$response.body#/id" ) ) }, description = "The Staff of the Company" , responseCode = "200" ), @ApiResponse ( description = "Company does not exist" , responseCode = "404" ) }  )  @Produces (MediaType.APPLICATION_JSON)  @GET  @Path ( "{id}/staff" )  public Response getStaff( @Context UriInfo uriInfo, @PathParam ( "id" ) String id) { return service .findCompanyById(id) .map(c -> service.getStaff(c)) .map(staff -> Response .ok(staff) .links( Link.fromUriBuilder(uriInfo .getRequestUriBuilder()) .rel( "self" ) .build(), Link.fromUriBuilder(uriInfo .getBaseUriBuilder() .path(CompanyResource. class ) .path(id)) .rel( "company" ) .build() ) .build()) .orElseThrow(() -> new NotFoundException( "The company with id '" + id + "' does not exists" ));  } 

如您所料,除了指向self的链接之外,它还包括指向公司的链接。 当我们使用curl尝试时,预期的响应头将返回。

 $ curl -v http: //localhost:8080/api/companies/1/staff  > GET /api/companies/ 1 /staff HTTP/ 1.1  > Host: localhost: 8080  > User-Agent: curl/ 7.47 . 1  > Accept: */*  >  < HTTP/ 1.1 200  < Link: <http: //localhost:8080/api/companies/1/staff>;rel="self"  < Link: <http: //localhost:8080/api/companies/1>;rel="company"  < Content-Type: application/json  < Transfer-Encoding: chunked  <  [ { "id" : "1" , "email" : "john@smith.com" , "firstName" : "John" , "lastName" : "Smith" }, { "id" : "2" , "email" : "bob@smith.com" , "firstName" : "Bob" , "lastName" : "Smith" }  ] 

那么我们可以得出什么样的结论呢? HATEOAS实际上通过动态地驱动对话来统一Web API提供者和使用者之间的交互模型。 这是非常强大的功能,但是其中的大多数框架和工具都没有对HATEOAS的相当基本的支持(例如Web Linking ),或者根本没有。

在很多情况下,使用Web链接就足够了(到目前为止,我们已经看到了示例,例如分页,导航等),但是假设创建,编辑或修补现有资源又如何呢? 如何用超媒体丰富集合中返回的各个元素(在RFC-6537中进行描述)? HATEOAS是否值得所有这些努力?

与往常一样,答案是“取决于”,也许我们应该超越JAX-RS ? 在下一篇文章中(s_,我们将继续解决问题。

完整的源代码可在Github上找到 。

翻译自: https://www.javacodegeeks.com/2019/02/hypermedia-apis-support-jax-rs-openapi.html

jax-ws和jax-rs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/332864.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何下载MySQL的JDBC驱动包

1.打开 MySQL 官网&#xff1a;https://www.mysql.com/ 2.点击 DOWNLOADS&#xff0c;把页面滚动到最下面&#xff0c;点击 MySQL Community (GPL) Downloads 3.点击 Connector/J 4.点击 General Availability(GA) Releases&#xff0c;在 Select Operating System 下拉列表选…

IntelliJ IDEA for Mac如何添加jar包/如何导入jar包/导包

文章目录如何将 jar 包添加到已存在的资源库中将 jar 包添加到项目级别的资源库中将 jar 包添加到全局级别的资源库中将 jar 包添加到模块级别的资源库中创建资源库创建模块级别的资源库创建项目级别的资源库创建全局级别的资源库给某个模块单独添加 jar 包模块的依赖标签页&am…

r2dbc_使用Spring Data R2DBC进行异步RDBMS访问

r2dbc不久之前&#xff0c;发布了JDBC驱动程序的React性变体。 称为R2DBC。 它允许将数据异步流传输到已预订的任何端点。 通过将R2DBC之类的React性驱动程序与Spring WebFlux结合使用&#xff0c;可以编写一个完整的应用程序&#xff0c;以异步方式处理数据的接收和发送。 在本…

python中布尔类型是特殊的_Python中的特殊方法以及应用详解

前言Python 中的特殊方法主要是为了被解释器调用的&#xff0c;因此应该尽量使用 len(my_object) 而不是 my_object.__len__() 这种写法。在执行 len(my_object) 时&#xff0c;Python 解释器会自行调用 my_object 中实现的 __len__ 方法。除非有大量的元编程存在&#xff0c;直…

IntelliJ IDEA 自动补全变量名称和变量类型(自动补全变量的声明内容)

文章目录varOption EnterOption Command Vvar // 如下声明定义一个字符串变量 String s new String(); // 1 先编写 new String() // 2 在 new String() 后面输入 .var 直接回车&#xff0c;即可得到我们想要的变量了 // 3 上面生成的变量名可能不是我们想要的&#xff0c…

jdk11 jdk12_JDK 12附带紧凑数字格式

jdk11 jdk12JDK 12 Early Access Build 24引入了对紧凑数字格式的支持 。 JDK-8188147&#xff08;紧凑数字格式支持&#xff09;CSR的“摘要”是简单的句子&#xff0c;“添加了对JDK中的紧凑/短数字格式的支持。” 同一CSR还提供了详细的“解决方案”部分&#xff0c;该部分提…

全连接层 时间复杂度_神经网络全连接层(3)

CNN网络基础结构神经网络-全连接层(3)上一回我们聊完了算法&#xff0c;这回我们正式开始写代码。上回在做公式推导的时候&#xff0c;我们实际上只是针对一个数据样本进行推导&#xff0c;而实际中&#xff0c;计算和训练都是一批一批完成的。大多数机器学习训练都有batch的概…

line和spline_探索适用于Apache Spark的Spline Data Tracker和可视化工具(第1部分)

line和spline最近引起我注意的一个有趣且很有希望的开源项目是Spline &#xff0c;它是由Absa维护的Apache Spark数据沿袭跟踪和可视化工具。 该项目由两部分组成&#xff1a;一个在驱动程序上工作的Scala库&#xff0c;该库通过分析Spark执行计划来捕获数据沿袭&#xff0c;以…

MacBook如何快速显示桌面

1.触控板中张开拇指和其它三指 2.通过触发角来快速显示桌面

怎么把word里面虚线变成实线_弱电不会制作cad图,花3分钟看完,只要会用WORD保证你能画出来...

今天我要给你介绍的就是Microsoft Office Visio是Microsoft Office 套件之一。安装Visio之后&#xff0c;可以类比Word的操作方法一样来使用&#xff0c;不过&#xff0c;就是比在Word里画图、修改更方便&#xff0c;功能更强大。特别是在做技术路线图、各种图表的绘图&#xf…

MacBook如何设置分屏浏览的快捷键

MacBook的系统自身无法设置&#xff0c;必须安装第三方软件才能设置&#xff0c;例如&#xff1a;BetterAndBetter、Magnet、BetterSnapTool 等。 BetterAndBetter 的设置&#xff0c;如下图所示&#xff1a;

api自动化测试_API测试和自动化101:基本指南

api自动化测试API代表A pplication P AGC软件我覆盖整个院落。 通常&#xff0c;API用于通过使用任何通信方式来促进两个不同应用程序之间的交互。 在网络上使用API​​时&#xff0c;我们将其称为“ Web服务”。 近年来&#xff0c;API已成为编程的Struts。 与在应用程序中一样…

web.config连接mysql_web.config中配置数据库连接的方式

在网站开发中&#xff0c;数据库操作是经常要用到的操作&#xff0c;ASP.NET中一般做法是在web.config中配置数据库连接代码&#xff0c;然后在程序中调用数据库连接代码&#xff0c;这样做的好处就是当数据库连接代码需要改变的时候&#xff0c;我们只要修改web.config中的数据…

BetterAndBetter(BAB)的使用详解

文章目录多指轻点时防止左键点击规则管理重置全部设置和规则多指轻点时防止左键点击 在正常情况下按下触控板的左键&#xff0c;使用鼠标选择好文本后&#xff0c;松开触控板的左键&#xff0c;就已经退出文本选择模式了&#xff0c;此时移动鼠标应该是不会影响到已经选择的文…

gradle使用maven_使用Gradle – 2019版从Travis可靠发布到Maven Central

gradle使用maven得益于在2018年和2019年末实现的显式登台存储库创建功能集&#xff0c;使您&#xff08;自动&#xff09;从Travis&#xff08;不仅是&#xff09;发布到Maven Central更加可靠。 背景 如果您仅想获取有关如何使工件从Travis发行的信息更可靠的信息&#xff0c…

mysql 事件 day hour_Mysql事件调度器(Event Scheduler)

Mysql中的事件调度器Event Scheduler类似于linux下的crontab计划任务的功能,它是由一个特殊的时间调度线程执行的一、查看当前是否开启了event scheduler三种方法:1) SHOW VARIABLES LIKE ‘event_scheduler’;2) SELECT event_scheduler;3) SHOW PROCESSLIST;(是否有State为&a…

2020年全国儿童青少年总体近视率为52.7%,比上年上升2.5%播

2021年7月13日&#xff0c;国家卫健委召开新闻发布会介绍儿童青少年近视防控和暑期学生健康有关情况。国家卫健委疾控局副局长再那吾东玉山介绍&#xff0c;2020年上半年全民居家抗疫减少了户外活动和放松眼睛的时间&#xff0c;对近视防控工作带来了挑战。为全面评估近视率的情…

精简jdk包_在JDK 12精简数字格式中使用最小分数数字

精简jdk包帖子“ 紧凑数字格式出现在JDK 12中 ”演示了对JDK 12中 NumberFormat的支持&#xff0c;以支持紧凑数字格式 。 该帖子中显示的示例仅使用NumberFormat的实例&#xff0c;这些实例是通过调用NumberFormat的新重载getCompactNumberInstance(-)方法返回的&#xff0c;因…

mysql insert limit_Mysql Limit 调优

建表与插入数据SQL对比基本数据创建表CREATE TABLE student(id int(10) NOT NULL AUTO_INCREMENT,name varchar(25) DEFAULT NULL,age tinyint(2) DEFAULT NULL,live varchar(255) DEFAULT NULL,PRIMARY KEY (id)) ;批量插入1百万条数据DROP PROCEDURE IF EXISTS insert_Stu;DE…

预防近视的方法

推荐的方法里面有角膜塑形镜&#xff0c;也就是OK镜&#xff0c;还有低浓度阿托品、补光仪、离焦软镜、离焦框架眼镜等&#xff0c;阿托品离焦眼镜