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

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

1. обзор

В этом руководстве мы рассмотрим определение пользовательских типов носителей и их создание с помощью контроллера Spring REST.

Хороший вариант использования пользовательского типа мультимедиа - создание версий API.

2. API - Версия 1

Начнем с простого примера - API, открывающего единственный ресурс по идентификатору.

Мы собираемся начать с версии 1 ресурса, который мы предоставляем клиенту. Для этого мы собираемся использовать собственный HTTP-заголовок -“application/vnd.example.api.v1+json”.

Клиент запросит этот настраиваемый тип мультимедиа через заголовокAccept.

Вот наша простая конечная точка:

@RequestMapping(
  method = RequestMethod.GET,
  value = "/public/api/items/{id}",
  produces = "application/vnd.example.api.v1+json"
)
@ResponseBody
public exampleItem getItem( @PathVariable("id") String id ) {
    return new exampleItem("itemId1");
}

Обратите внимание на параметрproduces здесь - указывающий настраиваемый тип мультимедиа, который этот API может обрабатывать.

Теперь ресурсexampleItem, который имеет одно поле -itemId:

public class exampleItem {
    private String itemId;

    // standard getters and setters
}

И последнее, но не менее важное: давайте напишем интеграционный тест для конечной точки:

@Test
public void givenServiceEndpoint_whenGetRequestFirstAPIVersion_then200() {
    given()
      .accept("application/vnd.example.api.v1+json")
    .when()
      .get(URL_PREFIX + "/public/api/items/1")
    .then()
      .contentType(ContentType.JSON).and().statusCode(200);
}

3. API - версия 2

Теперь предположим, что нам нужно изменить детали, которые мы предоставляем клиенту с помощью нашего ресурса.

Раньше мы предоставляли необработанный идентификатор - скажем, теперь нам нужно скрыть его и вместо этого предоставить имя, чтобы получить немного больше гибкости.

Важно понимать, что это изменение не имеет обратной совместимости; в основном - это критическое изменение.

Вот наше новое определение ресурса:

public class exampleItemV2 {
    private String itemName;

    // standard getters and setters
}

Итак, что нам здесь нужно сделать - это перенести наш API на вторую версию.

Мы собираемся сделать это кcreating the next version of our custom media type и определим новую конечную точку:

@RequestMapping(
  method = RequestMethod.GET,
  value = "/public/api/items/{id}",
  produces = "application/vnd.example.api.v2+json"
)
@ResponseBody
public exampleItemV2 getItemSecondAPIVersion(@PathVariable("id") String id) {
    return new exampleItemV2("itemName");
}

И теперь у нас точно такая же конечная точка, но она способна справиться с новой операцией V2.

Когда клиент запросит“application/vnd.example.api.v1+json” - Spring делегирует старую операцию, и клиент получитexampleItem с полемitemId (V1).

Но когда клиент теперь устанавливает заголовокAccept на“application/vnd.example.api.v2+json” –, он правильно выполнит новую операцию и вернет ресурс с полемitemName (V2):

@Test
public void givenServiceEndpoint_whenGetRequestSecondAPIVersion_then200() {
    given()
      .accept("application/vnd.example.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.example.api.v1+json"
)
public class CustomMediaTypeController

Как и ожидалось, аннотация@RequestMapping легко работает на уровне класса и позволяет нам указывать параметрыvalue,produces иconsumes.

5. Заключение

В этой статье проиллюстрированы примеры, когда определение пользовательских типов мультимедиа может быть полезно при создании версий публичного API.

Реализация всех этих примеров и фрагментов кода можно найти вthe GitHub project - это проект Maven, поэтому его должно быть легко импортировать и запускать как есть.