Автор оригинала: Taimoor Choudhary.
Вступление
API-интерфейсы REST являются гибкими и позволяют разработчикам создавать отдельные системы. С развитием архитектуры микросервисов – REST стал еще более зрелым, поскольку микросервисы могут создаваться независимо от языка или структуры, используемой в приложении.
Быть “в центре внимания” – это означает, что новые типы создаются или создаются на основе API REST, что приводит нас к HATEOAS .
Что такое HATEOAS?
Находясь в центре внимания, внедряются различные методы архитектуры, ориентированные на основы REST.
Гипермедиа как механизм состояния приложения (HATEOAS) – это архитектурный подход для повышения удобства использования API REST для приложений, использующих API.
Основная цель HATEOAS – предоставить дополнительную информацию в ответах REST API, чтобы пользователи API могли получать дополнительные сведения о конечных точках из одного вызова. Это позволяет пользователям создавать свои системы с помощью динамических вызовов API, перемещаясь от одной конечной точки к другой, используя информацию, полученную в результате каждого вызова.
Чтобы лучше понять это, взгляните на следующий ответ API:
{
"id": 1,
"name": "Dr. Sanders",
"speciality": "General",
"patientList": [
{
"id": 1,
"name": "J. Smalling",
"_links": {
"self": {
"href": "http://localhost:8080/patients/1"
}
}
}
],
"_links": {
"self": {
"href": "http://localhost:8080/doctors/1"
},
"patientList": {
"href": "http://localhost:8080/doctors/1/patients"
}
}
}
Помимо получения подробной информации о враче, ответ API также предоставляет дополнительную информацию в виде ссылок. Например, прикреплена ссылка для выборки всех пациентов одного врача.
То , что мы имеем здесь,-это обогащенный ресурсами ответ , где предоставленные ссылки являются ресурсами, которые обогащают наш ответ дополнительной информацией.
Весенняя НЕНАВИСТЬ
Spring HATEOAS предоставляет библиотеки для простой реализации архитектуры HATEOAS в приложении Spring. Используя API Spring HATEOAS, ссылки могут быть созданы и возвращены как часть объекта ответа API.
Весенняя НЕНАВИСТЬ К зависимостям
Используя Maven, добавить Spring HATEOAS так же просто, как включить зависимости:
org.springframework.plugin spring-plugin-core [2.0.0.RELEASE,) org.springframework.hateoas spring-hateoas [1.0.3.RELEASE,)
В качестве альтернативы, используя Gradle, вы можете добавить:
implementation 'org.springframework.plugin:spring-plugin-core:2.+' implementation 'org.springframework.hateoas:spring-hateoas:1.+'
Зависимости Spring Boot HATEOAS
Еще проще, для приложений Spring Boot вы можете использовать зависимость spring-boot-starter-hateoas Maven:
org.springframework.boot spring-boot-starter-hateoas [2.2.4.RELEASE,)
Аналогично, если вы используете Gradle, вы можете просто добавить:
implementation 'org.springframework.boot:spring-boot-starter-hateoas:2.+'
Использование spring-boot-starter-hateoas зависимости включает spring-hateoas и spring-boot-starter-web зависимости, поэтому, естественно, никакие другие стартеры не требуются.
Строительные блоки Spring HATEOAS
Основными строительными блоками для Spring HATEOAS являются Link s и Модель представления s (контейнер для коллекции Link s).
Модель представления затем расширяется в Модель сущности (для отдельных ресурсов) и Модель коллекции (для нескольких ресурсов), а также Модель страницы .
Давайте уделим немного времени объяснению каждого из них, прежде чем применять их в рабочей демонстрации.
Связи
Неизменяемый объект Link используется для хранения метаданных ресурса (URI или местоположения), и конечный пользователь может перейти к ресурсам, которые обогащают наш ответ API. Базовая ссылка с URI ресурса может выглядеть следующим образом:
"_links": {
"self": {
"href": "http://localhost:8080/doctors/1"
}
}
Ссылка содержит атрибут href , указывающий на URI ресурса. Атрибут href заключен в тег self , который идентифицирует связь с сущностью. Это означает, что ресурс, по сути, указывает на самого себя.
Почему ресурс указывает на себя?
Возвращенные ресурсы могут не быть полным представлением самих себя. У врача может быть список пациентов, но мы можем не захотеть возвращать его по умолчанию.
Если затем мы захотим взглянуть на список врачей, мы можем перейти к нему по ссылке.
Модели представления
Модель представления | действует как корневой класс для всех других классов моделей Spring HATEOAS. Он содержит коллекцию Ссылок и предоставляет метод для их добавления/удаления.
Создать собственную модель так же просто, как расширить класс Модель представления . В противном случае вы можете использовать любую из доступных моделей:
Модель сущности :
Модель сущностииспользуется для представления ресурса, соответствующего одному объекту. Вы можете обернуть свой ресурс модельюсущностии передать его вызывающей службе или вернуть его через конечную точку REST.Модель коллекции : Аналогично модели
Сущности, модельКоллекциииспользуется для обертывания ресурсов, хотя она обертывает ресурс, соответствующий коллекции объектов.Модель подкачки : Кроме того, поскольку многие конечные точки API REST возвращают ответы, которые являются коллекциями с возможностью просмотра страниц, Spring HATEOAS предоставляет
Модель страницыдля представления таких ресурсов.
Создание Ссылок
Давайте создадим образец ресурса, который расширяет Модель представления класса:
public class Doctor extends RepresentationModel{ private int id; private List patientList; }
На данный момент у нашей модели Doctor есть только свойство id и список пациентов. Далее мы добавим Ссылку на ресурс, которая укажет ресурс на самого себя.
Объект Связи
Spring HATEOAS Ссылка объекты принимают Строку аргументы для указания URI и связи между сущностями. В основном это атрибуты href и rel :
Link selfLink = new Link("http://localhost:8080/doctors/1", "self");
Doctor doctor = new Doctor();
doctor.add(selfLink);
Когда возвращается объект doctor (как показано в демонстрационном приложении в последующих разделах), тело ответа будет содержать:
"_links": {
"self": {
"href": "http://localhost:8080/doctors/1"
}
}
Компоновщик MVC
Git Essentials
Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!
Однако не рекомендуется жестко кодировать значения в конструкторе класса Link . Быстро становится трудно управлять ими и обновлять их по мере роста вашего приложения/API. Для борьбы с этим мы можем использовать WebMvcLinkBuilder , который позволяет нам создавать ссылки с использованием классов controller и указывать на их методы.
Давайте воссоздадим ссылку из предыдущего примера с помощью WebMvcLinkBuilder :
Link link = linkTo(methodOn(DoctorController.class).getDoctorById(id)).withSelfRel();
Здесь мы используем более программный подход к созданию ссылок. Он указывает на метод get Doctor By Id() внутри класса Контроллера Doctor. Поскольку он указывает на себя, мы используем метод withSelfRel() для указания связи.
В качестве альтернативы мы могли бы использовать метод with Real() и передать строку с другим отношением.
Spring HATEOAS переведет сведения о конечной точке из класса контроллера и метода, которые мы предоставили WebMvcLinkBuilder . Выходные данные этого объекта Link будут точно такими же, как и в предыдущем примере.
Реляционные Связи
Чтобы создать ссылки для ресурсов, которые имеют связь между собой или указывают на другой ресурс, мы бы использовали метод withRel () . Используя это, мы можем указать конечную точку, с которой можно получить доступ к связанному ресурсу:
Link link = linkTo(methodOn(DoctorController.class)
.getDoctorPatients(doctor.getId()))
.withRel("patientList");
Приведенный выше фрагмент кода указывает, что пользователь может получить список пациентов для объекта doctor , используя метод getDoctorPatients() внутри класса DoctorController . При добавлении в тело ответа создается следующая ссылка:
"_links": {
"patientList": {
"href": "http://localhost:8080/doctors/1/patients"
}
}
Обратите внимание, что мы не указали никакого URL-адреса при создании ссылки. Spring HATEOAS может извлекать информацию из конструктора ссылок и генерировать URL-адрес на основе используемых нами сопоставлений.
Конфигурация
Чтобы правильно отобразить различные Модели представления подтипы, вы можете включить представление гипермедиа с помощью @EnableHypermediaSupport аннотации. Вы можете передать Тип гипермедиа в качестве аргумента этой аннотации, что позволит вам указать тип гипермедиа, например, JSON, UBER , HAL и т. Д. Использование аннотации позволяет Spring настроить необходимые модули Джексона для правильной визуализации гипермедиа.
Как правило, Spring определяет используемый вами технологический стек и автоматически корректирует конфигурацию при добавлении аннотации. Хотя, если у вас есть какие-то особые требования, мы предлагаем вам ознакомиться с официальной документацией .
Демонстрационное приложение
Учитывая все сказанное, давайте напишем простое приложение Spring с поддержкой HATEOAS, перейдя в Spring Initializr и создав пустое приложение Spring Boot с зависимостью Spring HATEOAS ( spring-boot-hateoas-starter ):
Создание ресурса
Для любого ресурса, доступ к которому должен предоставляться через REST API, необходимо расширить Модель представления . Расширяя класс Модель представления , мы также наследуем метод add () , который используется для прикрепления к нему ссылок.
Давайте создадим модель для Врача :
public class Doctor extends RepresentationModel{ private int id; private String name; private String speciality; private List patientList; }
Поскольку класс Врач имеет отношения с пациентами, давайте также создадим модель Пациент :
public class Patient extends RepresentationModel{ private int id; private String name; }
Далее, в контроллере, в нашем случае Контроллере врача , мы автоматически подключим Службу врача :
@RestController
@RequestMapping(value = "/doctors")
public class DoctorController {
@Autowired
DoctorService doctorService;
}
Как и следовало ожидать, он содержит такие методы, как получить Врача () , получить Врача с пациентами () , получить Врачей () и т.д., Которые все возвращают Врача или Список<Доктор> . Реализация опущена для краткости – если вы хотите взглянуть, код находится на GitHub .
С помощью этого мы создали ресурс. При извлечении ресурсов мы будем ожидать либо один ресурс, либо набор ресурсов. Как указывалось ранее, мы завернем их в Модель сущности или Модель коллекции соответственно.
Получение одного ресурса
Давайте сначала реализуем функциональность выбора одного врача. Поскольку мы ожидаем, что вызов API вернет один ресурс, мы завернем наш ответ в модель сущности класс:
@GetMapping(value = "/{id}")
public EntityModel getDoctorById(@PathVariable int id) {
Doctor doctor = doctorService.getDoctorWithPatients(id);
for (final Patient patient : doctor.getPatientList()) {
Link selfLink = linkTo(methodOn(PatientController.class)
.getPatientById(patient.getId())).withSelfRel();
patient.add(selfLink);
}
doctor.add(linkTo(methodOn(DoctorController.class)
.getDoctorById(id)).withSelfRel());
doctor.add(linkTo(methodOn(DoctorController.class)
.getDoctorPatients(doctor.getId())).withRel("patientList"));
return new EntityModel<>(doctor);
}
После извлечения объекта Doctor мы просматриваем список связанных пациентов и добавляем ссылку для каждого из них. Каждая из этих ссылок может быть использована для получения каждого отдельного Пациента через Контроллер пациента .
Аналогично, мы добавляем self ссылку на Doctor , которая использовалась для выполнения вызовов API. Наряду со ссылкой на себя мы также добавляем реляционную ссылку, указывающую на список пациентов.
В конце метода мы завернули ваш Доктор объект в Модель сущности класс, и эта Модель сущности возвращается в качестве ответа:
{
"id": 1,
"name": "Dr. Sanders",
"speciality": "General",
"patientList": [
{
"id": 1,
"name": "J. Smalling",
"_links": {
"self": {
"href": "http://localhost:8080/patients/1"
}
}
},
{
"id": 2,
"name": "Samantha Williams",
"_links": {
"self": {
"href": "http://localhost:8080/patients/2"
}
}
}
],
"_links": {
"self": {
"href": "http://localhost:8080/doctors/1"
},
"patientList": {
"href": "http://localhost:8080/doctors/1/patients"
}
}
}
“Доктор Сандерс” имеет “Дж. Смоллинг” и “Саманту Уильямс” в качестве своих пациентов, и как конечная точка для врача, так и конечная точка для списка пациентов доктора добавляются в ответ, что делает его обогащенным ресурсами .
Получение Нескольких Ресурсов
Давайте создадим еще один вызов GET, который вернет всех доступных врачей в системе. Теперь, когда ожидаемый ответ будет представлять собой набор объектов Doctor , мы поместим ответ в CollectionModel :
@GetMapping public CollectionModelgetDoctors() { List doctors = doctorService.getDoctorsWithPatients(); for (final Doctor doctor : doctors) { doctor.add(linkTo(methodOn(DoctorController.class) .getDoctorById(doctor.getId())).withSelfRel()); doctor.add(linkTo(methodOn(DoctorController.class) .getDoctorPatients(doctor.getId())).withRel("patientList")); for (final Patient patient : doctor.getPatientList()) { Link selfLink = linkTo(methodOn(PatientController.class) .getPatientById(patient.getId())).withSelfRel(); patient.add(selfLink); } } Link link = linkTo(methodOn(DoctorController.class).getDoctors()).withSelfRel(); return new CollectionModel<>(doctors, link); }
В этом методе, наряду со ссылкой self для самого вызова REST, мы также добавляем ссылку self для получения каждого отдельного врача. У каждого врача есть реляционная связь, которая указывает на связанных с ним пациентов. Внутри списка пациентов у каждого пациента также есть ссылка self , которую также можно использовать для поиска конкретного пациента.
Как только все ссылки будут добавлены, мы завернем коллекцию объектов Doctor в модель Коллекции и вернем ее:
{
"_embedded": {
"doctorList": [
{
"id": 1,
"name": "Dr. Sanders",
"speciality": "General",
"patientList": [
{
"id": 1,
"name": "J. Smalling",
"_links": {
"self": {
"href": "http://localhost:8080/patients/1"
}
}
}
],
"_links": {
"self": {
"href": "http://localhost:8080/doctors/1"
},
"patientList": {
"href": "http://localhost:8080/doctors/1/patients"
}
}
},
{
"id": 2,
"name": "Dr. Goldberg",
"speciality": "General",
"patientList": [
{
"id": 4,
"name": "K. Oliver",
"_links": {
"self": {
"href": "http://localhost:8080/patients/4"
}
}
}
],
"_links": {
"self": {
"href": "http://localhost:8080/doctors/2"
},
"patientList": {
"href": "http://localhost:8080/doctors/2/patients"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/doctors"
}
}
}
Как вы можете видеть из выходных данных, просто сделав один звонок, пользователь может обнаружить дополнительную информацию, которой в противном случае не было бы.
Вывод
Spring HATEOAS предоставляет необходимые библиотеки и инфраструктуру для реализации архитектуры HATEOAS в приложениях на базе Spring.
Как видно из выходных данных, пользователи могут получить дополнительную информацию из одного вызова REST. Используя эту информацию, проще создавать динамические клиенты REST.
В этой статье мы обсудили, как работает HATEOAS, его реализацию Весной и закончили созданием простого приложения для демонстрации концепций.
Исходный код для примера кода можно найти здесь, на GitHub .