В моем предыдущем сообщении в блоге я показал, как легко настроить Джерси с помощью Spring Boot. Мои исследования Spring Boot и Jersey не закончились, и я исследовал возможность использования Spring HATEOAS вместе с Jersey в приложении Spring Boot. Spring HATEOS позволяет создавать представления REST, которые следуют принципу HATEOAS и (на момент написания этой статьи) имеют базовую поддержку JAX-RS для работы со ссылками. В этом посте я поделюсь некоторыми примерами того, как я интегрировал Spring HATEOAS с Jersey в приложение Spring Boot.
Вступление
В качестве основы для этой статьи я использовал созданный ранее пример: ( https://github.com/kolorobot/spring-boot-jersey-demo ).
Чтобы начать работу с Spring HATEOAS, я добавил действительную зависимость в build.gradle
:
1
|
compile( "org.springframework.hateoas:spring-hateoas:0.16.0.RELEASE" ) |
Быстрый подход с помощником Resources
Самый быстрый подход к генерации представления объекта сущности ( Customer
) — использование помощников Spring HATEOAS Resource
and Resources
. Последние обертывают коллекцию сущностей, возвращаемых CustomerRepository
. Для генерации ссылки я использовал JaxRsLinkBuilder
который помогает JaxRsLinkBuilder
ссылки на ресурсы с ресурсами JAX-RS, обнаруживая пути на @Path
аннотации @Path
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
@Component @Path ( "/customer" ) @Produces (MediaType.APPLICATION_JSON) public class CustomerController { @Inject private CustomerRepository customerRepository; @GET public Response findAll() { Resources<Customer> resources = new Resources<>( customerRepository.findAll(), JaxRsLinkBuilder .linkTo(CustomerController. class ) .withSelfRel() ); return Response.ok(resources).build(); } |
Результатом вызова вышеупомянутого метода будет ресурс коллекции со ссылкой на себя:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
{ "links" : [ { "rel" : "self" , } ], "content" : [ { "id" : 1 , "firstname" : "Dave" , "lastname" : "Matthews" , "emailAddress" : { } } ] } |
Создание представлений с помощью класса ResourceAssemblerSupport
Помощники Resource
, Resources
, PagedResources
очень удобны, но в некоторых ситуациях требуется больший контроль над созданными ресурсами.
Для создания пользовательского объекта переноса из сущности можно использовать базовый класс ResourceSupport
:
1
2
3
4
5
6
|
public class CustomerResource extends ResourceSupport { private String fullName; private String email; } |
Чтобы собрать CustomerResource
из сущности и автоматически добавить к нему ссылку на себя, следует использовать класс ResourceAssemblerSupport
. В основном этот класс отвечает за создание экземпляра ресурса и добавление ссылки с rel self, указывающей на ресурс:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
public class CustomerResourceAssembler extends ResourceAssemblerSupport<Customer, CustomerResource> { public CustomerResourceAssembler() { super (CustomerController. class , CustomerResource. class ); } @Override public CustomerResource toResource(Customer entity) { CustomerResource resource = createResourceWithId( entity.getId(), entity ); // initialize the resource return resource; } } |
Проблема, с которой я столкнулся в приведенном выше коде, заключается в том, что класс ResourceAssemblerSupport
внутренне использует построитель ссылок для построения ссылок на контроллеры Spring MVC ( ControllerLinkBuilder
). Это приводит к тому, что ссылки являются недействительными.
Я не нашел другого способа, кроме создания нового класса поддержки, который расширяется из ResourceAssemblerSupport
и переопределяет поведение его родителя:
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
|
public abstract class JaxRsResourceAssemblerSupport<T, D extends ResourceSupport> extends ResourceAssemblerSupport<T, D> { private final Class<?> controllerClass; public JaxRsResourceAssemblerSupport( Class<?> controllerClass, Class<D> resourceType) { super (controllerClass, resourceType); this .controllerClass = controllerClass; } @Override protected D createResourceWithId(Object id, T entity, Object... parameters) { Assert.notNull(entity); Assert.notNull(id); D instance = instantiateResource(entity); instance.add( JaxRsLinkBuilder.linkTo(controllerClass, parameters) .slash(id) .withSelfRel()); return instance; } } |
Мне не очень нравится вышеуказанное решение, так как мне нужно было скопировать и вставить некоторый код, но я не нашел лучшего способа достичь того, чего хотел.
Мой ассемблер расширяется от недавно созданного JaxRsResourceAssemblerSupport
:
1
2
3
4
|
public class CustomerResourceAssembler extends JaxRsResourceAssemblerSupport<Customer, CustomerResource> { } |
Наконец, я мог изменить метод контроллера, чтобы он возвращал ресурсы, собранные моим ассемблером. Обратите внимание, что ResourceAssemblerSupport
предоставляет удобный метод для преобразования всех данных объектов в ресурсы:
01
02
03
04
05
06
07
08
09
10
|
@GET @Path ( "/resources" ) public Response findAll() { Iterable<Customer> customers = customerRepository.findAll(); CustomerResourceAssembler assembler = new CustomerResourceAssembler(); List<CustomerResource> resources = assembler.toResources(customers); return Response.ok(wrapped).build(); } |
Чтобы добавить ссылку с собственной ссылкой на ресурс коллекции, мне нужно было обернуть ее, используя ранее упомянутый класс Resources
:
1
2
3
4
5
6
7
|
// wrap to add link Resources<CustomerResource> wrapped = new Resources<>(resources); wrapped.add( JaxRsLinkBuilder .linkTo(CustomerController. class ) .withSelfRel() ); |
Теперь возвращаемое представление выглядит более HATEOAS:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
{ "links" : [ { "rel" : "self" , } ], "content" : [ { "fullName" : "Matthews, Dave" , "links" : [ { "rel" : "self" , } ] } ] } |
Использование LinksBuilder
Интерфейс EntityLinks
предоставляет API для создания ссылок на основе типа сущности и доступен для внедрения зависимостей, когда @EnableEntityLinks
или @EnableHypermadiaSupport
используются с @ExposesResourceFor
. @ExposesResourceFor
какой тип сущности управляется контроллером Spring MVC или ресурсом JAX-RS.
В классе конфигурации нам нужно активировать ссылки на сущности:
1
2
3
4
5
|
@SpringBootApplication @EnableEntityLinks public class Application { } |
Примечание. Обратите внимание, что при использовании ссылок на сущности и @EnableEntityLinks
на пути к классам должна быть @EnableEntityLinks
следующая зависимость:
1
|
compile( "org.springframework.plugin:spring-plugin-core:1.1.0.RELEASE" ) |
Любой ресурс JAX-RS, поддерживающий тип объекта, должен быть помечен @ExposesResourceFor
, чтобы можно было EntityLinks
:
1
2
3
4
5
|
@ExposesResourceFor (Customer. class ) public class CustomerController { @Inject private EntityLinks entityLinks; } |
По EntityLinks
интерфейс EntityLinks
предоставляет методы, возвращающие ссылки на ресурс коллекции или отдельный ресурс. Пример:
1
2
3
|
Link selfRel = entityLinks.linkToSingleResource( Customer. class , customer.getId() ).withSelfRel(); |
Резюме
Spring HATEOAS — это не единственный вариант создания HATEOAS API с использованием JAX-RS и Jersey, но с возможностью включения Jersey в приложение Spring Boot Spring HATEOAS может быть хорошим дополнением, особенно если он разработан с учетом JAX-RS.
Примечание: эта статья — только исследование, которое я провел относительно описанной темы. Я не использовал этот подход ни в одном проекте.
Ресурсы
- Исходный код проекта: https://github.com/kolorobot/spring-boot-jersey-demo
- Страница проекта Spring HATEOAS: https://github.com/spring-projects/spring-hateoas и образец: https://github.com/olivergierke/spring-hateoas-sample
Ссылка: | Создание HATEOAS API с помощью JAX-RS и Spring от нашего партнера по JCG Рафаля Боровца в блоге Codeleak.pl . |