Перейти к основному содержимому

Пользовательский тип носителя для Spring REST API

· 3 мин. чтения

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 .