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 .