Spring: REST
Начать стоит с предтечья REST - с web-сервисов. Web-сервис - это способ коммуникации, реализованный на основе стандартов, где в качестве инструмента коммуникации выбраны сообщения (а не бинарные или текстовые объекты). Как правило, используются SOAP и WSDL.
REST - это аналог web-сервиса, созданный специально для коммуникации между приложениями (или слоями приложения). Зародился в 2000 году от Роя Филдинга (Roy Fielding) в качестве легковесной альтернативы RPC, Remote Procedure Call и web-службам. Использует паттерн CRUD (Create/Read/Update/Delete) для оперирования между узлами.
HTTP метод | Действие | Описание |
---|---|---|
GET | Read | Выполняет чтение существующего ресурса. Считается безопасной (safe) операцией, поскольку не меняет данные. Ответ от запроса к запросу не должен меняться (конечно, если не было изменений и если бизнес-логика удовлетворяет данное требование). |
POST | Create | Используется для создания нового ресурса. Для одинаковых POST запроса должны возвращать одинаковые результаты (конечно, если бизнес-логика удовлетворяет данное требование). |
PUT | Update | Изменяет существующий ресурс. Является не безопасной (unsafe) операцией. Так же как и POST должна выдавать одинаковые ответы при одинаковых запросах (конечно, если бизнес-логика удовлетворяет данное требование). |
DELETE | Delete | Удаляет существующий ресурс. |
Один ресурс должен быть идентифицирован уникальным URI. Для качественной передачи данных можно использовать все возможности HTTP протокола - заголовки, тело сообщения, статусы и т.д.
RESTful протокол считается stateless, т.е. соединение клиент-сервер не поддерживается активным, а разрывается после окончания передачи данных. Так же, нет требований к синхронизации - действия могут выполняться асинхронно.
Аннотации Spring 4.3
В версии 4.3 появилась укороченная версия аннотаций для контроллеров:
HTTP метод | До 4.3 | После 4.3 |
---|---|---|
GET | @RequestMapping(metod="GET") |
@GetMapping |
POST | @RequestMapping(metod="POST") |
@PostMapping |
PUT | @RequestMapping(metod="PUT") |
@PutMapping |
DELETE | @RequestMapping(metod="DELETE") |
@DeleteMapping |
@Controller в Spring REST
По ум. в Spring MVC @Controller
пытается найти представление (View
) если метод возвращает строку. Например:
1 |
|
Spring преобразует объекты, найденные в репозитории в JSON и попытается найти в представлениях View
с подобным именем. Есть вероятнгость, что данное представление найдено не будет и Spring выдаст клиенту соответствующую ошибку. Существует несколько способов исправить данную проблему, в частности:
- добавить аннотацию
@ResponseBody
в определение метода; - добавить аннотацию
@ResponseBody
в отпределение класса; - изменить аннотацию класса с
@Controller
на@RestController
(она представляет собой мета-аннотацию@Controller
+@ResponseBody
).
После данной правки метод станет возвращать JSON в качестве ответа на запрос. Но это не самая успешная реализация данной задачи. Ее можно упростить, переложив преобразование результата на сторону Spring:
1 |
|
Для того чтобы Spring корретно преобразовал объект (в примере вышел это List
) в JSON, потребуется определить для него конвертер. В Spring уже предусмотрено некоторое количество “стандартных” конвертеров:
Требуемый тип | Конвертер | Условие |
---|---|---|
StringHttpMessageConverter |
text/plain | |
MappingJackson2HttpMessageConverter |
application/+json | Наличие подключенной библиотеки Jackson2 |
MappingJackson2XmlMessageConverter |
application/+xml | Наличие подключенной библиотеки Jackson2 |
AtomFeedHttpMessageConverter |
application/atom+xml | Наличие подключенной библиотеки Rome |
RssChannelHttpMessageConverter |
application/rss+xml | Наличие подключенной библиотеки Rome |
В настоящем нет необходимости определять конвертеры вручную, Spring сам сделает в результате сканирования доступных бинов, реализующих HttpMessageConverter
. Но в предыдущих версиях, для добавления специфичесого конвертера или изменения существующей реализации, придется это сделать:
1 |
|
Стандартная конвертация дат
По ум. ObjectMapper
(от FasterXML) преобразует даты как набор значений объекта. Это не всегда удобно для клиента. Можно либо самостоятельно реализовать преобразование типа данных в нужное значение JSON используя @JsonFormat
на каждом требуемом поле объекта, либо воспользоваться готовой реализацией из библиотеки jackson-datatype-jsr310. Далее необходимо потребовать от Jackson2 выполнить сканирование на наличие модулей (конверетер является частью модуля).
1 | objectMapper.findAndRegisterModules(); |
Либо подключить только нужный модуль:
1 | objectMapper.registerModule(new JavaTimeModule()); |
После обнаружения, Jackson2 подключит конвертер ко всем типам с дат.
@JsonIgnore
Данная аннотация используется для игнорирования того или иного поля (или getter) при конвертации.
Различные представления для конвертации
JSON лишь один из способов отображения данных. Web-метод может определить как следует предоставить данные (а так же, получить их). Так, для определения предоставления данных используется:
1 |
Клиент со своей стороны так же может регулировать какий формат ответа он предпочитает. Он может передать это в заголовке Accept
.
Spring Boot REST
Для включения REST в StringBoot приложение следует добавить зависимость spring-boot-starter-web
, который включает в себя в т.ч. следующие:
spring-boot-starter-json
. Загружает Jackson2 зависимости и создает бинObjectMapper
;spring-boot-starter-validation
. Добавляет Hibernate валидации (типа@Min
,@NotEmpty
,@NotNull
и т.д.);spring-boot-starter-tomcat
. Выбирается как сервер по ум. Если необходимо заменить его, то следует исключить данную библиотеку из зависимостей. Замена возможна, например, наspring-boot-starter-jetty
.
HTTP Status code
Детальное описание кодов и их рекомендованное поведение представлено на отдельном ресурсе.
Некоторые из них:
- 200 / OK - GET запрос успешно выполнен;
- 201 / CREATED - POST или PUT (если ресурса ранее не было) успешно выполнены. Так же необходимо дополнительно вернуть заголовок, в котором будет ссылка на новый ресурс;
- 204 / NO_CONTENT - успешноре выполнение PUT (изменения) или DELETE. В теле ничего быть не должно;
- 404 / NOT_FOUND - ресурс не найден;
- 403 / FORBIDDEN - ползователь, выполняющий запрос не авторизован;
- 405 / METHOD_NOT_ALLOWED - HTTP-метод не допускается к выполнению для данного ресурса;
- 409 / CONFLICT -попытка добавить или изменить ресурс, который нарушает какое-либо правило уникальности. Актульано для POST и PUT;
- 415 / UNSUPPORTED_MEDIA_TYPE - не поддерживаемый формат данных в запросе. Или запрашиваемый формат ответа не поддерживается.
Успешный HTTP статус можно установить для метода аннотацией @ResponseStatus
. В случае ошибки обработка пойдет по стандартному Spring пути и выдаст код статуса, соответствующий ошибке.
Валидация данных на входе
Добавление аннотации @Validated(Class<?>[])
позволяет активизировать принудительную валидацию данных на входе в метод (только при условии запуска в контексте Spring). Параметр класса опциональный и его задача заключается в группировке типов проверок. Например, необходимо в разных методах выполнять разные проверки на длинну строки:
1 | public class MyObject { |
В примере выше, сработают 3 проверки - name
будет проверен на короткое имя; age
будет проверен на значение от 18; phone
будет проверен на не пустую строку. height
проверен не будет, поскольку класс проверки указан, но в @Validated
он не фигурирует.
Запрет на вывод
Ранее отмечалась аннотация @JsonIgnore
, но она запрещает чтение и запись в поле объекта. Если необходимо добавить только чтения или только записи для поля, то можно применить аннотацию @JsonProperty
:
1 |
Перехват ошибок
Правила такие же как и для MVC. В контроллере молжно указать перехватку ошибок, с указанием статуса, типа исключения и дополнительно обработать (если потребуется).
Но поскольку ранее была затронута тема валидации, следует добавить возможность перехвата подобных ошибок, вызванных действиями пользователя:
1 | public void create( { MyObjectDto obj, BindingResult result) |
В случае ошибки валидации данный метод вызовет исключение (текст можно записатьподробнее). Пользуясь возможностям MVC можно перехватить данное исключение:
1 |
|
Так же как и в MVC, удачной практикой является добавление класса с аннотацией @ControllerAdvice
, в котором будут содержаться перехваты ошибок. К @ControllerAdvice
можно добавить ограничение по классам (де-факто, контроллерам), к которым перехваты ошибок будут применяться:
1 |
|
RestTemplate
Класс используется для выполенения HTTP запросов к удаленным узлам и получения и обработки ответов. Фактически, это HTTP-клиент, являющийся частью Spring.
Его часто используют для тестирования приложения, поскольку он уже входит в состав Spring. При этом нельзя назвать его удобным инструментом!
Чем REST так хорош?
- можно подключать различные форматы для передачи данных (JSON, XML, PlainText и т.д.);
- применительно к Spring - поскольку в основе лежит MVC, доступны все инструменты MVC для распределения нагрузки;
- потребителем может выступать любой HTTP-клиент;
- существуют готовые инструменты для документации сервисов для потребителей (см. Swagger).