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