Вступление к весне HATEOAS
1. обзор
В этой статье описывается процесс создания веб-службы REST на основе гипермедиа с использованием проекта Spring HATEOAS.
2. Весна-HATEOAS
Проект Spring HATEOAS - это библиотека API-интерфейсов, которую мы можем использовать для простого создания REST-представлений, соответствующих принципу HATEOAS (гипертекст как движок состояния приложения).
Вообще говоря, принцип подразумевает, что API должен направлять клиента через приложение, возвращая соответствующую информацию о следующих возможных шагах вместе с каждым ответом.
В этой статье мы собираемся создать пример с использованием Spring HATEOAS с целью разделения клиента и сервера и теоретического разрешения API изменять свою схему URI без нарушения работы клиентов.
3. подготовка
Во-первых, давайте добавим зависимость Spring HATEOAS:
org.springframework.boot
spring-boot-starter-hateoas
2.1.4.RELEASE
Если мы не используем Spring Boot, мы можем добавить в наш проект следующие библиотеки:
org.springframework.hateoas
spring-hateoas
0.25.1.RELEASE
org.springframework.plugin
spring-plugin-core
1.2.0.RELEASE
Как всегда, мы можем искать последние версии зависимостейstarter HATEOAS,spring-hateoas иspring-plugin-core в Maven Central.
Далее у нас есть ресурсCustomer без поддержки Spring HATEOAS:
public class Customer {
private String customerId;
private String customerName;
private String companyName;
// standard getters and setters
}
И у нас есть класс контроллера без поддержки Spring HATEOAS:
@RestController
@RequestMapping(value = "/customers")
public class CustomerController {
@Autowired
private CustomerService customerService;
@GetMapping("/{customerId}")
public Customer getCustomerById(@PathVariable String customerId) {
return customerService.getCustomerDetail(customerId);
}
}
Наконец, представление ресурсаCustomer:
{
"customerId": "10A",
"customerName": "Jane",
"customerCompany": "ABC Company"
}
4. Добавление поддержки HATEOAS
В проекте Spring HATEOAS нам не нужно ни искать контекст сервлета, ни связывать переменную пути с базовым URI.
Вместо этогоSpring HATEOAS offers three abstractions for creating the URI – ResourceSupport, Link, and ControllerLinkBuilder. Мы можем использовать их для создания метаданных и привязки их к представлению ресурса.
4.1. Добавление поддержки гипермедиа к ресурсу
Проект предоставляет базовый классResourceSupport для наследования при создании представления ресурса:
public class Customer extends ResourceSupport {
private String customerId;
private String customerName;
private String companyName;
// standard getters and setters
}
The Customer resource extends from the ResourceSupport class to inherit the add() method. Поэтому, как только мы создадим ссылку, мы можем легко установить это значение для представления ресурса, не добавляя в него никаких новых полей.
4.2. Создание ссылок
Spring HATEOAS предоставляет объектLink для хранения метаданных (расположение или URI ресурса).
Сначала создадим простую ссылку вручную:
Link link = new Link("http://localhost:8080/spring-security-rest/api/customers/10A");
ОбъектLink следует синтаксису ссылкиhttps://en.wikipedia.org/wiki/Atom (стандартный) [Atom] _ и состоит изrel, который определяет отношение к ресурсу, и атрибутаhref, который является фактической ссылкой. сам.
Вот как выглядит ресурсCustomer теперь, когда он содержит новую ссылку:
{
"customerId": "10A",
"customerName": "Jane",
"customerCompany": "ABC Company",
"_links":{
"self":{
"href":"http://localhost:8080/spring-security-rest/api/customers/10A"
}
}
}
URI, связанный с ответом, квалифицируется как ссылкаself. Семантика отношенияself ясна - это просто каноническое местоположение, из которого можно получить доступ к Ресурсу.
4.3. Создание лучших ссылок
Другая очень важная абстракция, предлагаемая библиотекой, -the ControllerLinkBuilder – which simplifies building URIs, позволяющая избегать жестко заданных ссылок.
В следующем фрагменте показано построение само-ссылки клиента с использованием классаControllerLinkBuilder:
linkTo(CustomerController.class).slash(customer.getCustomerId()).withSelfRel();
Давайте посмотрим:
-
методlinkTo() проверяет класс контроллера и получает его корневое отображение
-
методslash() добавляет значениеcustomerId в качестве переменной пути ссылки
-
наконец,withSelfMethod() квалифицирует отношение как самосвязь
5. связи
В предыдущем разделе мы показали отношение ссылки на себя. Однако более сложные системы могут включать и другие отношения.
Например,customer может иметь отношение к заказам. Давайте также смоделируем классOrder как ресурс:
public class Order extends ResourceSupport {
private String orderId;
private double price;
private int quantity;
// standard getters and setters
}
На этом этапе мы можем расширитьCustomerController с помощью метода, который возвращает все заказы конкретного клиента:
@GetMapping(value = "/{customerId}/orders", produces = { "application/hal+json" })
public Resources getOrdersForCustomer(@PathVariable final String customerId) {
List orders = orderService.getAllOrdersForCustomer(customerId);
for (final Order order : orders) {
Link selfLink = linkTo(methodOn(CustomerController.class)
.getOrderById(customerId, order.getOrderId())).withSelfRel();
order.add(selfLink);
}
Link link = linkTo(methodOn(CustomerController.class)
.getOrdersForCustomer(customerId)).withSelfRel();
Resources result = new Resources(orders, link);
return result;
}
Наш метод возвращает объектResources в соответствии с типом возврата HAL, а также ссылку «_self” для каждого из заказов и полного списка.
Здесь важно отметить, что гиперссылка для заказов клиентов зависит от сопоставления методаgetOrdersForCustomer(). Мы назовем эти типы ссылок ссылками на методы и покажем, какControllerLinkBuilder могут помочь в их создании.
6. Ссылки на методы контроллера
ControllerLinkBuilder предлагает богатую поддержку контроллеров Spring MVC. В следующем примере показано, как создавать гиперссылки HATEOAS на основе методаgetOrdersForCustomer() классаCustomerController:
Link ordersLink = linkTo(methodOn(CustomerController.class)
.getOrdersForCustomer(customerId)).withRel("allOrders");
The methodOn() obtains the method mapping by making dummy invocation of the target method на прокси-контроллере и устанавливаетcustomerId в качестве переменной пути в URI.
7. Spring HATEOAS в действии
Давайте объединим само-ссылку и создание ссылки на метод в методgetAllCustomers():
@GetMapping(produces = { "application/hal+json" })
public Resources getAllCustomers() {
List allCustomers = customerService.allCustomers();
for (Customer customer : allCustomers) {
String customerId = customer.getCustomerId();
Link selfLink = linkTo(CustomerController.class).slash(customerId).withSelfRel();
customer.add(selfLink);
if (orderService.getAllOrdersForCustomer(customerId).size() > 0) {
Link ordersLink = linkTo(methodOn(CustomerController.class)
.getOrdersForCustomer(customerId)).withRel("allOrders");
customer.add(ordersLink);
}
}
Link link = linkTo(CustomerController.class).withSelfRel();
Resources result = new Resources(allCustomers, link);
return result;
}
Затем вызовем методgetAllCustomers():
curl http://localhost:8080/spring-security-rest/api/customers
И рассмотрим результат:
{
"_embedded": {
"customerList": [{
"customerId": "10A",
"customerName": "Jane",
"companyName": "ABC Company",
"_links": {
"self": {
"href": "http://localhost:8080/spring-security-rest/api/customers/10A"
},
"allOrders": {
"href": "http://localhost:8080/spring-security-rest/api/customers/10A/orders"
}
}
},{
"customerId": "20B",
"customerName": "Bob",
"companyName": "XYZ Company",
"_links": {
"self": {
"href": "http://localhost:8080/spring-security-rest/api/customers/20B"
},
"allOrders": {
"href": "http://localhost:8080/spring-security-rest/api/customers/20B/orders"
}
}
},{
"customerId": "30C",
"customerName": "Tim",
"companyName": "CKV Company",
"_links": {
"self": {
"href": "http://localhost:8080/spring-security-rest/api/customers/30C"
}
}
}]
},
"_links": {
"self": {
"href": "http://localhost:8080/spring-security-rest/api/customers"
}
}
}
В каждом представлении ресурса есть ссылкаself и ссылкаallOrders для извлечения всех заказов клиента. Если у клиента нет заказов, ссылка на заказы не появится.
Этот пример демонстрирует, как Spring HATEOAS способствует обнаружению API в остальном веб-сервисе. If the link exists, the client can follow it and get all orders for a customer:с
curl http://localhost:8080/spring-security-rest/api/customers/10A/orders
{
"_embedded": {
"orderList": [{
"orderId": "001A",
"price": 150,
"quantity": 25,
"_links": {
"self": {
"href": "http://localhost:8080/spring-security-rest/api/customers/10A/001A"
}
}
},{
"orderId": "002A",
"price": 250,
"quantity": 15,
"_links": {
"self": {
"href": "http://localhost:8080/spring-security-rest/api/customers/10A/002A"
}
}
}]
},
"_links": {
"self": {
"href": "http://localhost:8080/spring-security-rest/api/customers/10A/orders"
}
}
}
8. Заключение
В этом руководстве мы обсудили, какbuild a hypermedia-driven Spring REST web service using the Spring HATEOAS project.
В этом примере мы видим, что клиент может иметь единую точку входа в приложение, и дальнейшие действия могут быть предприняты на основе метаданных в ответном представлении.
Это позволяет серверу изменять свою схему URI, не прерывая работу клиента. Кроме того, приложение может рекламировать новые возможности, помещая новые ссылки или URI в представление.
Наконец, полную реализацию этой статьи можно найти вthe GitHub project.