Автор оригинала: Eugen Paraschiv.
1. Проблема
Разработка REST API – сложная проблема, для которой доступно множество вариантов. В этой статье рассматриваются некоторые из этих вариантов.
Дальнейшее чтение:
Spring Boot Tutorial – Bootstrap простое приложение
Изучение тестовой панели Spring Boot TestRestTemplate
REST API с Джерси и весной
2. Что содержится в Договоре?
Прежде всего, нам нужно ответить на один простой вопрос: Каков контракт между API и Клиентом?
2.1. URIs является частью Договора?
Давайте сначала рассмотрим структуру URI API REST – это часть контракта? Должны ли клиенты добавлять закладки, жестко кодировать и вообще полагаться на URI API?
Если это так, то взаимодействие Клиента со Службой REST больше не будет зависеть от самой Службы, а от того, что Рой Филдинг называет | внеполосной информацией:
API REST должен быть введен без каких-либо предварительных знаний, кроме начального URI (закладки) и набора стандартизированных типов носителей, подходящих для целевой аудитории…Неудача здесь подразумевает, что внеполосная информация управляет взаимодействием, а не гипертекстом.
Так что ясно URI не являются частью контракта ! Клиент должен знать только один URI – точку входа в API. Все остальные URI должны быть обнаружены при использовании API.
2.2. Типы носителей являются частью Договора?
Как насчет информации о типе носителя, используемой для представления Ресурсов – являются ли они частью контракта между Клиентом и Сервисом?
Чтобы успешно использовать API, Клиент должен иметь предварительные знания об этих типах носителей . Фактически, определение этих типов носителей представляет собой весь контракт.
Таким образом, именно здесь Служба REST должна сосредоточиться больше всего:
API REST должен тратить почти все свои описательные усилия на определение типов носителей, используемых для представления ресурсов и управления состоянием приложения, или на определение расширенных имен отношений и/или разметки с поддержкой гипертекста для существующих стандартных типов носителей.
Таким образом, определения типов носителей являются частью контракта и должны быть предварительными знаниями для клиента, который использует API. Вот тут-то и вступает в дело стандартизация.
Теперь у нас есть хорошее представление о том, что такое контракт, давайте перейдем к тому, как на самом деле решить проблему управления версиями.
3. Опции Высокого Уровня
Теперь давайте обсудим высокоуровневые подходы к управлению версиями REST API:
- Управление версиями URI – версирование пространства URI с помощью индикаторов версий
- Управление версиями типа носителя – версия представления ресурса
Когда мы вводим версию в пространство URI, представления ресурсов считаются неизменяемыми. Поэтому, когда необходимо внести изменения в API, необходимо создать новое пространство URI.
Например, скажем, API публикует следующие ресурсы – пользователей и привилегии:
http://host/v1/users http://host/v1/privileges
Теперь давайте рассмотрим, что критическое изменение в users API требует введения второй версии:
http://host/v2/users http://host/v2/privileges
Когда мы изменяем тип носителя и расширяем язык, мы проходим согласование Контента на основе этого заголовка. API REST будет использовать пользовательские типы носителей MIME поставщика вместо общих типов носителей, таких как application/json . Мы собираемся версировать эти типы носителей вместо URI.
Например:
===> GET /users/3 HTTP/1.1 Accept: application/vnd.myname.v1+json <=== HTTP/1.1 200 OK Content-Type: application/vnd.myname.v1+json { "user": { "name": "John Smith" } }
Мы можем ознакомиться со статьей “Пользовательские типы носителей для API Rest” для получения дополнительной информации и примеров по этому вопросу.
Здесь важно понять, что клиент не делает никаких предположений о структуре ответа за пределами того, что определено в типе носителя.
Вот почему универсальные типы носителей не идеальны. Они не предоставляют достаточной семантической информации и заставляют клиента использовать дополнительные подсказки для обработки фактического представления ресурса.
Исключением из этого является использование какого – либо другого способа уникальной идентификации семантики содержимого-например, схемы XML.
4. Преимущества и недостатки
Теперь, когда у нас есть четкое представление о том, что является частью Контракта между Клиентом и Сервисом, а также обзор вариантов версии API на высоком уровне, давайте обсудим преимущества и недостатки каждого подхода.
Во-первых, введение идентификаторов версий в URL-адрес приводит к очень большому размеру URI. Это связано с тем, что любое критическое изменение в любом из опубликованных API приведет к появлению совершенно нового дерева представлений для всего API. Со временем это становится бременем для обслуживания, а также проблемой для клиента, у которого теперь есть больше вариантов на выбор.
Идентификаторы версий в URI также сильно негибки . Невозможно просто развить API одного ресурса или небольшого подмножества общего API.
Как мы уже упоминали ранее, это подход “все или ничего”. Если часть API переходит на новую версию, то вместе с ней должен перейти и весь API. Это также делает обновление клиентов с v1 до v2 серьезной задачей, что приводит к более медленным обновлениям и гораздо более длительным периодам завершения для старых версий.
Кэширование HTTP также является серьезной проблемой, когда дело доходит до управления версиями.
С точки зрения прокси-кэшей в середине, каждый подход имеет свои преимущества и недостатки. Если URI версионный, то в кэше необходимо будет хранить несколько копий каждого ресурса – по одной для каждой версии API. Это создает нагрузку на кэш и снижает частоту попадания в кэш, так как разные клиенты будут использовать разные версии.
Кроме того, некоторые механизмы недействительности кэша больше не будут работать. Если тип носителя является версионным, то и Клиент, и Служба должны поддерживать HTTP-заголовок Vary , чтобы указать, что кэшируется несколько версий.
С точки зрения кэширования клиента однако решение, которое версирует тип носителя, требует немного больше работы, чем решение, в котором URI содержат идентификатор версии. Это происходит потому, что просто проще кэшировать что-то, когда его ключом является URL-адрес, чем тип носителя.
Давайте закончим этот раздел определением некоторых целей (прямо из API Evolution ):
- не допускайте совместимых изменений в именах
- избегайте новых основных версий
- делает изменения обратно совместимыми
- подумайте о совместимости с форвардами
5. Возможные изменения в API
Далее давайте рассмотрим типы изменений в REST API – они представлены здесь:
- изменения формата представления
- изменения ресурсов
5.1. Добавление к представлению Ресурса
Документация формата типа носителя должна быть разработана с учетом прямой совместимости. В частности, клиент должен игнорировать информацию, которую он не понимает (что JSON делает лучше, чем XML).
Теперь, добавление информации в представление ресурса не приведет к нарушению существующих клиентов, если они правильно реализованы.
Чтобы продолжить наш предыдущий пример, добавление суммы в представление пользователя не будет критическим изменением:
{ "user": { "name": "John Smith", "amount": "300" } }
5.2. Удаление или Изменение Существующего представления
Удаление, переименование или вообще реструктуризация информации в дизайне существующих представлений-это кардинальное изменение для клиентов. Это потому, что они уже понимают и полагаются на старый формат.
Вот тут-то и вступают в силу переговоры по контенту. Для таких изменений/| мы можем добавить новый тип носителя MIME поставщика.
Давайте продолжим предыдущий пример. Допустим, мы хотим разбить имя пользователя на имя и фамилию :
===> GET /users/3 HTTP/1.1 Accept: application/vnd.myname.v2+json <=== HTTP/1.1 200 OK Content-Type: application/vnd.myname.v2+json { "user": { "firstname": "John", "lastname": "Smith", "amount": "300" } }
Таким образом, это представляет собой несовместимое изменение для Клиента, который должен будет запросить новое представление и понять новую семантику. Однако пространство URI останется стабильным и не будет затронуто.
5.3. Основные Семантические Изменения
Это изменения в значении ресурсов, отношениях между ними или в том, что отображается в бэкэнде. Для такого рода изменений может потребоваться новый тип носителя или публикация нового родственного ресурса рядом со старым и использование ссылок, указывающих на него.
Хотя это похоже на повторное использование идентификаторов версий в URI, важным отличием является то, что новый ресурс публикуется независимо от любых других ресурсов в API и не будет разветвлять весь API в корне.
API REST должен придерживаться ограничения HATEOAS. В соответствии с этим большинство URI должны быть ОБНАРУЖЕНЫ клиентами, а не жестко закодированы. Изменение такого URI не должно рассматриваться как несовместимое изменение. Новый URL-адрес может заменить старый, и клиенты смогут заново открыть URL-адрес и по-прежнему функционировать.
Однако стоит отметить, что, хотя использование идентификаторов версий в URI проблематично по всем этим причинам, это ни в коем случае не является не-RESTful .
6. Заключение
В этой статье мы попытались дать обзор очень разнообразной и сложной проблемы развития службы ОТДЫХА . Мы обсудили два общих решения, преимущества и недостатки каждого из них, а также способы рассуждения об этих подходах в контексте REST.
Статья завершается обоснованием второго решения – управление версиями типов носителей при рассмотрении возможных изменений в RESTful API.
Полную реализацию этого руководства можно найти в проекте GitHub .
7. Дальнейшее Чтение
Обычно эти ресурсы для чтения связаны по всей статье, но в этом случае их просто слишком много: