На этот раз мы немного поговорим об API JAX-RS 2.0 и коснемся одного очень интересного аспекта спецификации: динамических функций и их полезности.
Традиционно, когда API -интерфейсы JAX-RS 2.0 конфигурируются и развертываются (с использованием класса Application , загружаемого из сервлета или создаваемого с помощью RuntimeDelegate ), существует возможность зарегистрировать дополнительных поставщиков и функции . Прекрасными примерами этого могут быть проверка бина (JSR 349) или поддержка Java API для обработки JSON (JSR-353) . Эти провайдеры и функции будут применяться ко всем ресурсам JAX-RS 2.0, и в большинстве случаев это желаемое поведение. Однако время от времени возникает необходимость включить определенного поставщика или функцию только для некоторых ресурсов, оставляя другие незатронутыми. Это как раз тот случай, когда нам очень помогут динамические функции .
В этом посте мы собираемся использовать последнюю версию 3.1.5 превосходной платформы Apache CXF, но динамические функции являются частью спецификации JAX-RS 2.0 и поддерживаются большинством (если не всеми) реализациями.
Давайте рассмотрим очень простой JAX-RS 2.0 API для управления людьми с единственным методом для обработки запросов HTTP GET . Предположим, что это версия 1 API, и хотя аннотация @Range указана для параметра запроса count , его поддержка никогда не реализовывалась и присутствует в коде только для целей документирования.
1
2
3
4
5
6
7
8
|
@Path ( "/v1/people" ) public class PeopleRestService { @Produces ( { MediaType.APPLICATION_JSON } ) @GET public List<Person> getAll( @Range (min = 1 , max = 10 ) @QueryParam ( "count" ) int count) { } } |
В этом случае передача недопустимого значения для параметра запроса количества приведет к внутренней ошибке сервера . Давайте удостоверимся, что это именно то, что происходит:
1
2
3
4
5
6
7
8
|
$ curl -i http: //localhost:8080/rest/api/v1/people?count=-1 HTTP/ 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 без изменений, поскольку ее клиенты не ожидают возврата каких-либо других кодов состояния HTTP, кроме 200 и 500 (к сожалению, в реальной жизни это случается чаще, чем нет). ,
Существует несколько различных подходов для реализации такой настройки для каждого API, но, вероятно, самый простой из них — это введение отдельной аннотации, например @EnableBeanValidation , и аннотирование класса ресурсов JAX-RS 2.0 вместе с ним:
1
2
3
4
5
6
7
8
9
|
@Path ( "/v2/people" ) @EnableBeanValidation public class ValidatingPeopleRestService { @Produces ( { MediaType.APPLICATION_JSON } ) @GET public @Valid List<Person> getAll( @Range (min = 1 , max = 10 ) @QueryParam ( "count" ) int count) { } } |
Чтобы включить Bean Validation 1.1 для всех API -интерфейсов JAX-RS 2.0, аннотированных @EnableBeanValidation, мы собираемся создать динамический класс пространственных объектов , BeanValidationDynamicFeature :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
@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); } @Override public void configure( final ResourceInfo resourceInfo, final FeatureContext context) { if (resourceInfo.getResourceClass().getAnnotation(EnableBeanValidation. class ) != null ) { context.register(inInterceptor); context.register(outInterceptor); } } } |
Его работа довольно проста, просто зарегистрируйте JAXRSBeanValidationInInterceptor и JAXRSBeanValidationOutInterceptor экземпляры-перехватчики в качестве дополнительных провайдеров для рассматриваемых API JAX-RS 2.0 . Одно небольшое, но важное замечание: средства отображения исключений не поддерживаются динамическими функциями , по крайней мере, в отношении реализации Apache CXF , и должны быть зарегистрированы как обычные поставщики (вместе с самими динамическими функциями ), например:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
@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 ), он будет применен ко всем соответствующим служебным компонентам. Давайте удостоверимся, что для версии 2 нашего API управления персоналом запускается правильная готовая проверка:
1
2
3
4
5
|
$ curl -i http: //localhost:8080/rest/api/v2/people?count=-1 HTTP/ 1.1 400 Bad Request Content-Length: 0 Server: Jetty( 9.3 . 7 .v20160115) |
На этот раз ответ отличается, что указывает на то, что клиент отправил неверный ввод (прямой результат проверки Bean 1.1 в действии): Bad Request .
Надеемся, что динамические функции станут еще одним полезным инструментом в вашем наборе инструментов. Пример, который мы рассмотрели здесь, несколько воображаем, но очень просто использовать динамические функции с безопасностью, трассировкой, ведением журналов, профилированием и т. Д. Кроме того, динамические функции можно применять даже к определенным методам ресурсов, что позволяет осуществлять точный контроль над вашими API. ,
- Полный исходный код проекта доступен на Github .
Ссылка: | Ваши API JAX-RS не были одинаковыми: с помощью динамических функций от нашего партнера по JCG Андрея Редько в блоге Андрея Редько (devmind) . |