В моем предыдущем сообщении в блоге я показал, как легко настроить Джерси с помощью 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": { "value": "dave@dmband.com" } } ]} |
Создание представлений с помощью класса 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 linkResources<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", "email": "dave@dmband.com", "links": [ { "rel": "self", } ] } ]} |
Использование LinksBuilder
Интерфейс EntityLinks предоставляет API для создания ссылок на основе типа сущности и доступен для внедрения зависимостей, когда @EnableEntityLinks или @EnableHypermadiaSupport используются с @ExposesResourceFor . @ExposesResourceFor какой тип сущности управляется контроллером Spring MVC или ресурсом JAX-RS.
В классе конфигурации нам нужно активировать ссылки на сущности:
|
1
2
3
4
5
|
@SpringBootApplication@EnableEntityLinkspublic 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 . |