1. Обзор
LinkRest
— это платформа с открытым исходным кодом для создания управляемых данными веб-сервисов REST. Он построен на основе JAX-RS
и Apache Cayenne ORM
и использует протокол сообщений на основе HTTP/JSON.
По сути, эта структура предназначена для предоставления простого способа предоставления нашего хранилища данных в Интернете.
В следующих разделах мы рассмотрим, как мы можем создать веб-службу REST для доступа к модели данных с помощью LinkRest
.
2. Зависимости Maven
Чтобы начать работу с библиотекой, сначала нам нужно добавить зависимость link-rest :
<dependency>
<groupId>com.nhl.link.rest</groupId>
<artifactId>link-rest</artifactId>
<version>2.9</version>
</dependency>
Это также приводит к артефакту cayenne-server .
Кроме того, мы будем использовать Jersey
в качестве реализации JAX-RS
, поэтому нам нужно добавить зависимость jersey-container-servlet
, а также jersey-media-moxy
для сериализации ответов JSON:
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.25.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.25.1</version>
</dependency>
В нашем примере мы будем работать с базой данных H2
в памяти , поскольку ее проще настроить; как следствие, мы также добавим h2
:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version>
</dependency>
3.
Модель данных Кайенна
Модель данных, с которой мы будем работать, содержит сущности « Отдел
» и « Сотрудник
» , представляющие отношение «один ко многим»:
Как уже упоминалось, LinkRest
работает с объектами данных, сгенерированными с помощью Apache Cayenne ORM
. Работа с Cayenne
не является основной темой этой статьи, поэтому для получения дополнительной информации ознакомьтесь с документацией по Apache Cayenne
.
Мы сохраним проект Cayenne
в файле cayenne-linkrest-project.xml
.
После запуска cayenne-maven-plugin
будут сгенерированы два абстрактных класса _Department
и _Employee
, которые расширят класс CayenneDataObject
, а также два конкретных производных от них класса: Department
и Employee
.
Эти последние классы мы можем настраивать и использовать с LinkRest
.
4. Запуск приложения LinkRest
В следующем разделе мы собираемся написать и протестировать конечные точки REST, поэтому, чтобы иметь возможность их запускать, нам нужно настроить нашу среду выполнения.
Поскольку мы используем Джерси
в качестве реализации JAX-RS
, давайте добавим класс, который расширяет ResourceConfig
и указывает пакет, который будет содержать классы, в которых мы определяем конечные точки REST:
@ApplicationPath("/linkrest")
public class LinkRestApplication extends ResourceConfig {
public LinkRestApplication() {
packages("com.foreach.linkrest.apis");
// load linkrest runtime
}
}
В том же конструкторе нам нужно собрать и зарегистрировать LinkRestRuntime
в контейнере Jersey .
Этот класс основан на первой загрузке CayenneRuntime
:
ServerRuntime cayenneRuntime = ServerRuntime.builder()
.addConfig("cayenne-linkrest-project.xml")
.build();
LinkRestRuntime lrRuntime = LinkRestBuilder.build(cayenneRuntime);
super.register(lrRuntime);
Наконец, нам нужно добавить класс в web.xml
:
<servlet>
<servlet-name>linkrest</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.foreach.LinkRestApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>linkrest</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
5. Ресурсы REST
Теперь, когда у нас есть классы моделей, мы можем приступить к написанию ресурсов REST.
Конечные точки REST создаются с использованием стандартных аннотаций JAX-RS
, а ответ строится с использованием класса LinkRest
.
Наш пример будет состоять из написания серии конечных точек CRUD, которые обращаются к URL-адресу /department
с использованием различных методов HTTP.
Во-первых, давайте создадим класс DepartmentResource
, который сопоставляется с /department
:
@Path("department")
@Produces(MediaType.APPLICATION_JSON)
public class DepartmentResource {
@Context
private Configuration config;
// ...
}
Классу LinkRest
требуется экземпляр класса конфигурации JAX-RS
, который вводится с помощью аннотации Context
, также предоставляемой JAX-RS
.
Далее давайте продолжим писать каждую из конечных точек, которые обращаются к объектам отдела .
5.1. Создание сущностей с помощью POST
Для создания объекта класс LinkRest
предоставляет метод create()
, который возвращает объект UpdateBuilder
:
@POST
public SimpleResponse create(String data) {
return LinkRest.create(Department.class, config).sync(data);
}
Параметр данных может быть либо одним объектом JSON, представляющим отдел
, либо массивом объектов. Этот параметр отправляется в UpdateBuilder
с помощью метода sync()
для создания одного или нескольких объектов и вставки записей в базу данных, после чего метод возвращает SimpleResponse
.
Библиотека определяет 3 дополнительных формата ответов:
DataResponse<T>
— ответ, представляющий наборT
MetadataResponse<T>
— содержит метаданные о типе.SimpleResponse
— объект, содержащий два атрибутауспеха
исообщения .
Далее давайте используем curl
для добавления записи отдела
в базу данных:
curl -i -X POST -H "Content-Type:application/json"
-d "{"name":"IT"}" http://localhost:8080/linkrest/department
В результате команда возвращает статус 201 Created
и признак успеха :
{"success":true}
Мы также можем создать несколько объектов, отправив массив JSON:
curl -i -X POST -H "Content-Type:application/json"
-d "[{"name":"HR"},{"name":"Marketing"}]"
http://localhost:8080/linkrest/department
5.2. Чтение сущностей с помощью GET
Основным методом запроса объектов является метод select () класса
LinkRest
. Это возвращает объект SelectBuilder
, который мы можем использовать для цепочки дополнительных методов запроса или фильтрации.
Давайте создадим конечную точку в классе DepartmentResource
, которая возвращает все объекты отдела в базе данных:
@GET
public DataResponse<Department> getAll(@Context UriInfo uriInfo) {
return LinkRest.select(Department.class, config).uri(uriInfo).get();
}
Вызов uri()
задает информацию о запросе для SelectBuilder
, а get() возвращает коллекцию отделов
, обернутую как объект DataResponse<Department>
.
Давайте посмотрим на отделы, которые мы добавили перед использованием этой конечной точки:
curl -i -X GET http://localhost:8080/linkrest/department
Ответ принимает форму объекта JSON с массивом данных и
общим
свойством:
{"data":[
{"id":200,"name":"IT"},
{"id":201,"name":"Marketing"},
{"id":202,"name":"HR"}
],
"total":3}
В качестве альтернативы, чтобы получить коллекцию объектов, мы также можем получить один объект, используя getOne()
вместо get()
.
Давайте добавим конечную точку, сопоставленную с /department/{departmentId}
, которая возвращает объект с заданным идентификатором. Для этого отфильтруем записи с помощью метода byId()
:
@GET
@Path("{id}")
public DataResponse<Department> getOne(@PathParam("id") int id,
@Context UriInfo uriInfo) {
return LinkRest.select(Department.class, config)
.byId(id).uri(uriInfo).getOne();
}
Затем мы можем отправить запрос GET на этот URL:
curl -i -X GET http://localhost:8080/linkrest/department/200
В результате получается массив данных
с одним элементом:
{"data":[{"id":200,"name":"IT"}],"total":1}
5.3. Обновление сущностей с помощью PUT
Для обновления записей мы можем использовать метод update()
или createOrUpdate()
. Последний обновит записи, если они существуют, или создаст их, если их нет:
@PUT
public SimpleResponse createOrUpdate(String data) {
return LinkRest.createOrUpdate(Department.class, config).sync(data);
}
Как и в предыдущих разделах, аргумент данных
может быть одним объектом или массивом объектов.
Обновим один из ранее добавленных отделов:
curl -i -X PUT -H "Content-Type:application/json"
-d "{"id":202,"name":"Human Resources"}"
http://localhost:8080/linkrest/department
Это возвращает объект JSON с сообщением об успехе или ошибке. После этого мы можем проверить, было ли изменено название отдела с ID 202:
curl -i -X GET http://localhost:8080/linkrest/department/202
Разумеется, эта команда возвращает объект с новым именем:
{"data":[
{"id":202,"name":"Human Resources"}
],
"total":1}
5.4. Удаление объектов с помощью DELETE
А чтобы удалить объект, мы можем вызвать метод delete()
, который создает DeleteBuilder
, а затем указать первичный ключ объекта, который мы хотим удалить, с помощью метода id()
:
@DELETE
@Path("{id}")
public SimpleResponse delete(@PathParam("id") int id) {
return LinkRest.delete(Department.class, config).id(id).delete();
}
Затем мы можем вызвать эту конечную точку с помощью curl
:
curl -i -X DELETE http://localhost:8080/linkrest/department/202
5.5. Работа с отношениями между сущностями
LinkRest
также содержит методы, облегчающие работу со связями между объектами.
Поскольку у отдела
отношение «один ко многим» к Employee
, давайте добавим конечную точку /department/{departmentId}/employees
, которая обращается к классу EmployeeSubResource :
@Path("{id}/employees")
public EmployeeSubResource getEmployees(
@PathParam("id") int id, @Context UriInfo uriInfo) {
return new EmployeeSubResource(id);
}
Класс EmployeeSubResource
соответствует отделу, поэтому у него будет конструктор, который задает идентификатор отдела, а также экземпляр Configuration :
@Produces(MediaType.APPLICATION_JSON)
public class EmployeeSubResource {
private Configuration config;
private int departmentId;
public EmployeeSubResource(int departmentId, Configuration configuration) {
this.departmentId = departmentId;
this.config = config;
}
public EmployeeSubResource() {
}
}
Обратите внимание, что конструктор по умолчанию необходим для сериализации объекта как объекта JSON.
Далее давайте определим конечную точку, которая извлекает всех сотрудников из отдела:
@GET
public DataResponse<Employee> getAll(@Context UriInfo uriInfo) {
return LinkRest.select(Employee.class, config)
.toManyParent(Department.class, departmentId, Department.EMPLOYEES)
.uri(uriInfo).get();
}
В этом примере мы использовали метод toManyParent()
класса SelectBuilder
для запроса только объектов с заданным родителем.
Аналогичным образом можно создать конечные точки для методов POST, PUT, DELETE.
Чтобы добавить сотрудников в отдел, мы можем вызвать конечную точку Departments /{departmentId}/employees
с помощью метода POST:
curl -i -X POST -H "Content-Type:application/json"
-d "{"name":"John"}" http://localhost:8080/linkrest/department/200/employees
Затем отправим GET-запрос на просмотр сотрудников отдела:
curl -i -X GET "http://localhost:8080/linkrest/department/200/employees
Это возвращает объект JSON с массивом данных::
{"data":[{"id":200,"name":"John"}],"total":1}
6. Настройка ответа с параметрами запроса
LinkRest
предоставляет простой способ настроить ответ, добавив в запрос определенные параметры. Их можно использовать для фильтрации, сортировки, разбиения на страницы или ограничения набора атрибутов набора результатов.
6.1. Фильтрация
Мы можем отфильтровать результаты на основе значений атрибутов, используя параметр cayenneExp
. Как следует из названия, это соответствует формату выражений Cayenne
.
Давайте отправим запрос, который возвращает только отделы с названием «IT»:
curl -i -X GET http://localhost:8080/linkrest/department?cayenneExp=name='IT'
6.2. Сортировка
Параметры, которые необходимо добавить для сортировки набора результатов, — это sort
и dir
. Первый из них указывает атрибут для сортировки, а второй — направление сортировки.
Давайте посмотрим все отделы, отсортированные по имени:
curl -i -X GET "http://localhost:8080/linkrest/department?sort=name&dir=ASC"
6.3. Пагинация
Библиотека поддерживает разбиение на страницы, добавляя параметры start
и limit
:
curl -i -X GET "http://localhost:8080/linkrest/department?start=0&limit=2
6.4. Выбор атрибутов
Используя параметры включения
и исключения
, мы можем контролировать, какие атрибуты или отношения возвращаются в результате.
Например, давайте отправим запрос, который отображает только названия отделов:
curl -i -X GET "http://localhost:8080/linkrest/department?include=name
Чтобы показать имена, а также сотрудников отдела только с их именем, мы можем дважды использовать атрибут include :
curl -i -X GET "http://localhost:8080/linkrest/department?include=name&include=employees.name
7. Заключение
В статье мы показали, как мы можем быстро предоставить модель данных через конечные точки REST с помощью платформы LinkRest
.
Полный исходный код примеров можно найти на GitHub .