Spring Framework WebMVC позволяет разрабатывать не только классические веб-приложения, но и реализовывать REST API. В этой статье я опишу процесс разработки REST API простого проекта на Java с использованием Spring Boot и Spring Framework.
О REST и его поддержке в Spring
REST — это архитектурный стиль взаимодействия приложений в сети. Но в отличии от SOAP, у REST отсутствует какой-либо стандарт, а данные между клиентом и сервером могут предаваться в любом виде, будь то JSON, XML, YAML и т.д.
Spring Framework предоставляет богатый набор инструментов, упрощающий разработку REST API: инструменты для маршрутизации запросов, классы-кодеки для преобразования JSON/XML в объекты требуемых типов и т.д.
HTTP-методы
В отличии от классических веб-приложений, в которых используются только методы GET и POST, в REST API используются практически все HTTP-методы, например:
- GET — для получения объекта или списка объектов
- HEAD — проверка существования объекта
- OPTIONS — проверка доступных методов для указанного пути
- POST — для создания нового объекта
- PUT — для полного изменения объекта или помещения объекта в список
- PATCH — для частичного изменения объекта
- DELETE — для удаления объекта
Стоит помнить, что хорошей практикой является использование GET только для запросов, которые не изменяют состояние объекта. Если запрос должен изменить состояние объекта, то должен использоваться соответствующий HTTP-метод, хотя бы POST.
Для маршрутизации запросов в Spring Framework используется аннотация @RequestMapping с указанием HTTP-метода при помощи свойства method или более простые аннотации вроде
@GetMapping, @PostMapping, @DeleteMapping и т.д.
HTTP-заголовки
Очень важными при разработке REST API являются и HTTP-заголовки. Например, клиент может, указывать MIME-тип содержимого отправляемого запроса при помощи заголовка Content-type или ожидаемый MIME-тип ответа или даже его версию при помощи заголовка Accept.
Возможности Spring Framework WebMVC позволяют маршрутизировать запросы в зависимости не только от их пути или методов, но и в зависимости от заголовков:
Базовый проект
Проект будет реализовывать REST API списка задач. В проекте мы будем использовать Spring Boot, из которого нам понадобятся стартеры:
- Web — для поддержки веб
- JDBC — для работы с базой данных
- H2 — в качестве тестовой базы данных
Код pom.xml приведён ниже:
Поскольку в проекте будет использоваться реляционная база данных, нам потребуется описать SQL-схему таблицы, в которой будут храниться задачи:
Класс, описывающий эту структуру:
REST контроллер
Теперь, когда у нас есть вся необходимая структура, мы можем приступить непосредственно к разработке REST API. Для этого создадим заготовку класса TodoRestController:
- Контекст приложения создаст и зарегистрирует экземпляр класса, поскольку указана аннотация @RestController. Кроме этого аннотация подсказывает, что возвращаемые методами значения являются телом ответов на запросы. Эта аннотация заменяет собой пару @Controller + @ResponseBody.
- Аннотация @RequestMapping указывает на маршрутизацию запросов: все запросы, начинающиеся с api/todo будут обрабатываться этим контроллером.
- Объект типа JdbcOperations будет внедрён через конструктор.
Получение списка задач
Метод получения списка задач должен быть вызван при GET-запросе по пути /api/todo, он должен обратиться к базе данных и вернуть полученный список с HTTP-кодом 200 OK.
- @GetMapping указывает, что метод getTodoList должен обрабатывать GET-запросы с путём /api/todo. Альтернативно может быть использована аннотация @RequestMapping(method = RequestMethod.GET).
- При помощи объекта типа ResponseEntity, возвращаемого методом, можно тонко настроить ответ, включая коди заголовки ответа. Фактически можно просто вернуть список задач, не оборачивая его в ResponseEntity.
Получение одной задачи
Метод получения одной задачи по её идентификатору должен быть вызван при GET-запросе по пути /api/todo/{todoId}; он должен обратиться к базе данных и вернуть задачу с HTTP-кодом 200 OK или 404 Not Found, если задачи нет в БД.
- @GetMapping(«{todoId}») указывает, что метод getTodo должен обрабатывать GET-запросы с путём /api/todo/{todoId}. Фигурные скобки в данном случае указывают, что todoId — переменная пути (Path variable) и может быть использована в аргуметах метода. Можно задать более строгие правила валидации пути, добавив после todoId через двоеточие регулярное выражение, которое будет использоваться для валидации пути.
- @PathVariable указывает, что значение аргумента todoId должно браться из пути запроса. По умолчанию все значения являются строковыми, но при помощи классов-конвертеров строка преобразуется в объект класса java.util.UUID. Обратите внимание, что имя аргумента соответствует плейсхолдеру, указанному в @GetMapping, в противном случае нам пришлось бы указать плейсхолдер в свойстве name аннотации @PathVariable.
- Исключение IncorrectResultSizeDataAccessException выбрасывается в случае, если база данных вернула в ответ на запрос количество строк неравное 1. Если актуальное количество строк рано 0, то соответствующая запись в БД отсутствует, и мы можем вернуть ответ с HTTP-кодом 404 Not Found; если строк больше 1, то мы имеем дело с какой-то ошибкой и можем вернуть ответ с HTTP-кодом 500 Internal Server Error.
Создание задачи
Метод создания задачи должен быть вызван при POST-запросе по адресу /api/todo; он должен сохранить в БД полученные данные и вернуть сохранённую задачу в ответ.
- Аннотация @PostMapping указывает, что данный метод обрабатывает все POST-запросы с путём /api/todo. Свойство consumes задаёт список MIME-типов, которые может обрабатывать данный метод.
- Аннотация @RequestBody указывает, что отмеченный аргумент является телом запроса. Тело запроса преобразуется в объект нужного класса при помощи конвертеров.
- Тело запроса автоматически преобразуется в объект типа TodoPayload при помощи класса-кодека.
Изменение задачи
Метод изменения задачи должен быть вызван при PUT-запросе по адресу /api/todo/{todoId}; он должен сохранить изменения в БД и вернуть ответ со статусом 204 No Content, либо вернуть ответ со статусом 404 Not Found, если задача не найдена.
Этот метод может возвращать ответ, содержащий какую-то информацию, например, изменённую задачу, но клиент и так знает о внесённых изменениях.
Удаление задачи
Метод удаления задачи должен быть вызван при DELETE-запросе по адресу /api/todo/{todoId}; он должен удалить задачу из БД и вернуть ответ со статусом 204 No Content, либо вернуть ответ со статусом 404 Not Found, если задача не найдена.
Я рассмотрел в этой статье только базовые моменты разработки REST API при помощи Spring Framework WebMVC. В следующих постах я постараюсь описать тестирование, управление доступом, применение гипермедиа и реактивных API, поскольку все эти темы достаточно объёмны и требуют отдельных статей