1. Обзор
В этом руководстве мы рассмотрим определение пользовательских типов мультимедиа и их создание с помощью контроллера Spring REST.
Хорошим вариантом использования пользовательского типа мультимедиа является управление версиями API.
2. API — версия 1
Давайте начнем с простого примера — API, предоставляющий один ресурс по идентификатору.
Мы собираемся начать с версии 1 ресурса, который мы предоставляем клиенту. Для этого мы будем использовать собственный HTTP-заголовок — «application/vnd.foreach.api.v1+json»
.
Клиент запросит этот настраиваемый тип носителя через заголовок Accept .
Вот наша простая конечная точка:
@RequestMapping(
method = RequestMethod.GET,
value = "/public/api/items/{id}",
produces = "application/vnd.foreach.api.v1+json"
)
@ResponseBody
public ForEachItem getItem( @PathVariable("id") String id ) {
return new ForEachItem("itemId1");
}
Обратите внимание на параметр
«products», указывающий пользовательский тип носителя, который может обрабатывать этот API.
Теперь ресурс ForEachItem
, который имеет одно поле — itemId
:
public class ForEachItem {
private String itemId;
// standard getters and setters
}
И последнее, но не менее важное: давайте напишем интеграционный тест для конечной точки:
@Test
public void givenServiceEndpoint_whenGetRequestFirstAPIVersion_then200() {
given()
.accept("application/vnd.foreach.api.v1+json")
.when()
.get(URL_PREFIX + "/public/api/items/1")
.then()
.contentType(ContentType.JSON).and().statusCode(200);
}
3. API — версия 2
Теперь давайте предположим, что нам нужно изменить детали, которые мы предоставляем клиенту с нашим ресурсом.
Раньше мы выставляли необработанный идентификатор — скажем, теперь нам нужно скрыть это и вместо этого выставить имя, чтобы получить немного больше гибкости.
Важно понимать, что это изменение не имеет обратной совместимости; в основном – это переломное изменение.
Вот наше новое определение ресурса:
public class ForEachItemV2 {
private String itemName;
// standard getters and setters
}
Итак, что нам нужно сделать здесь — перенести наш API на вторую версию.
Мы собираемся сделать это, создав следующую версию нашего пользовательского типа носителя и определив новую конечную точку:
@RequestMapping(
method = RequestMethod.GET,
value = "/public/api/items/{id}",
produces = "application/vnd.foreach.api.v2+json"
)
@ResponseBody
public ForEachItemV2 getItemSecondAPIVersion(@PathVariable("id") String id) {
return new ForEachItemV2("itemName");
}
Итак, теперь у нас есть точно такая же конечная точка, но способная обрабатывать новую операцию V2.
Когда клиент запросит «application/vnd.foreach.api.v1+json»
— Spring делегирует старую операцию, и клиент получит ForEachItem
с полем itemId
(V1).
Но когда клиент теперь устанавливает заголовок Accept в
«application/vnd.foreach.api.v2+json» —
он правильно выполнит новую операцию и вернет ресурс с полем itemName
(V2):
@Test
public void givenServiceEndpoint_whenGetRequestSecondAPIVersion_then200() {
given()
.accept("application/vnd.foreach.api.v2+json")
.when()
.get(URL_PREFIX + "/public/api/items/2")
.then()
.contentType(ContentType.JSON).and().statusCode(200);
}
Обратите внимание, что тест похож, но использует другой заголовок Accept .
4. Пользовательский тип носителя на уровне класса
Наконец, давайте поговорим об определении типа носителя для всего класса — это тоже возможно:
@RestController
@RequestMapping(
value = "/",
produces = "application/vnd.foreach.api.v1+json"
)
public class CustomMediaTypeController
Как и ожидалось, аннотация @RequestMapping
легко работает на уровне класса и позволяет нам указывать значение
, производить
и потреблять
параметры.
5. Вывод
В этой статье приведены примеры того, как определение пользовательских типов мультимедиа может быть полезно при управлении версиями общедоступного API.
Реализацию всех этих примеров и фрагментов кода можно найти в проекте GitHub .