jax-rs jax-ws
这次,我们将讨论一些有关JAX-RS 2.0 API的内容,并涉及规范的一个非常有趣的方面: 动态功能及其实用性。
传统上,当配置和部署JAX-RS 2.0 API(使用Application类,从servlet引导或通过RuntimeDelegate创建)时,可以选择注册其他提供程序和功能 。 其中一个很好的例子是bean验证(JSR 349)或用于JSON处理的Java API(JSR-353)支持。 这些提供程序和功能将被应用到所有JAX-RS 2.0资源,并且在大多数使用情况下,这是理想的行为。 但是,有时需要仅为某些资源启用特定的提供程序或功能 ,而使其他资源不受影响。 这正是动态功能将为我们带来很大帮助的用例。
在本文中,我们将使用出色的Apache CXF框架的最新版本3.1.5 ,但是动态功能是JAX-RS 2.0规范的一部分,并且大多数(如果不是全部)实现都支持这些功能。
让我们考虑一个非常简单的JAX-RS 2.0 API,用一种方法来处理HTTP GET请求来管理人员。 让我们假设这是API的版本1 ,尽管为count查询参数指定了@Range批注,但从未实现过对它的支持,并且它在代码中仅用于文档目的。
@Path("/v1/people")
public class PeopleRestService {@Produces( { MediaType.APPLICATION_JSON } )@GETpublic List<Person> getAll(@Range(min = 1, max = 10) @QueryParam("count") int count) {return Collections.nCopies(count, new Person("a@b.com", "A", "B"));}
}
在这种情况下,为count查询参数传递无效值将导致Internal Server Error 。 让我们确保这就是正在发生的事情:
$ curl -i http://localhost:8080/rest/api/v1/people?count=-1HTTP/1.1 500 Server Error
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html;charset=iso-8859-1
Content-Length: 377
Connection: close
Server: Jetty(9.3.7.v20160115)
一段时间后,我们意识到了该API的问题,并决定使用Bean Validation 1.1与JAX-RS 2.0的集成来实施适当的验证机制。 但是,我们决定创建API的版本2 ,并保持版本1不变,因为它的客户端不希望返回除200和500之外的任何其他HTTP状态代码(不幸的是,在现实生活中,它经常发生) 。
有两种不同的方法可以实现这种基于API的自定义,但是最简单的方法可能是引入专用的注释,例如@EnableBeanValidation ,并使用它来注释JAX-RS 2.0资源类:
@Path("/v2/people")
@EnableBeanValidation
public class ValidatingPeopleRestService {@Produces( { MediaType.APPLICATION_JSON } )@GETpublic @Valid List<Person> getAll(@Range(min = 1, max = 10) @QueryParam("count") int count) {return Collections.nCopies(count, new Person("a@b.com", "A", "B"));}
}
为了为所有使用@EnableBeanValidation注释的JAX-RS 2.0 API启用Bean验证1.1 ,我们将创建一个动态要素类BeanValidationDynamicFeature :
@Provider
public class BeanValidationDynamicFeature implements DynamicFeature {private final JAXRSBeanValidationInInterceptor inInterceptor;private final JAXRSBeanValidationOutInterceptor outInterceptor;public BeanValidationDynamicFeature(final BeanValidationProvider provider) {this.inInterceptor = new JAXRSBeanValidationInInterceptor();this.inInterceptor.setProvider(provider);this.outInterceptor = new JAXRSBeanValidationOutInterceptor();this.outInterceptor.setProvider(provider);}@Overridepublic void configure(final ResourceInfo resourceInfo, final FeatureContext context) {if (resourceInfo.getResourceClass().getAnnotation(EnableBeanValidation.class) != null) {context.register(inInterceptor);context.register(outInterceptor);}}
}
它的工作非常简单,只需将JAXRSBeanValidationInInterceptor和JAXRSBeanValidationOutInterceptor拦截器实例注册为有问题的JAX-RS 2.0 API的其他提供程序 。 不过,还有一个小小的但重要的注意事项: 动态功能 (至少就Apache CXF实现而言)不支持异常映射器,并且应将其注册为常规提供程序 (以及动态功能本身),例如:
@Bean @DependsOn("cxf")
public Server jaxRsServer() {final JAXRSServerFactoryBean factory = RuntimeDelegate.getInstance().createEndpoint( jaxRsApiApplication(), JAXRSServerFactoryBean.class );factory.setServiceBean(validatingPeopleRestService());factory.setServiceBean(peopleRestService());factory.setProvider(new JacksonJsonProvider());factory.setProvider(new BeanValidationDynamicFeature(new BeanValidationProvider()));factory.setProvider(new ValidationExceptionMapper());return factory.create();
}@Bean
public JaxRsApiApplication jaxRsApiApplication() {return new JaxRsApiApplication();
}@Bean
public ValidatingPeopleRestService validatingPeopleRestService() {return new ValidatingPeopleRestService();
}@Bean
public PeopleRestService peopleRestService() {return new PeopleRestService();
}
这基本上就是我们要做的。 一旦注册BeanValidationDynamicFeature (在本例中使用JAXRSServerFactoryBean ),它将被应用于所有匹配的服务Bean。 让我们确保对于人员管理API的版本2 ,触发了正确的即用型验证:
$ curl -i http://localhost:8080/rest/api/v2/people?count=-1HTTP/1.1 400 Bad Request
Content-Length: 0
Server: Jetty(9.3.7.v20160115)
这次响应是不同的,表明客户机已经提交了无效的输入(正在运行Bean Validation 1.1的直接结果): Bad Request 。
希望动态功能将成为工具箱中的另一个有用工具。 我们这里介绍的示例有些虚构,但是使用具有安全性,跟踪,日志记录,性能分析等动态功能非常容易。此外,即使在特定资源方法上也可以应用动态功能 ,从而可以对API进行细粒度的控制。
- 完整的项目源可在Github上获得 。
翻译自: https://www.javacodegeeks.com/2016/02/jax-rs-apis-not-born-equal-using-dynamic-features.html
jax-rs jax-ws