Автор оригинала: 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 EntityModelgetDoctorById(@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 .