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

Управление версиями REST API

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

1. Проблема

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

2. Что в договоре?

Прежде всего, нам нужно ответить на один простой вопрос: что такое контракт между API и клиентом?

2.1. URI являются частью контракта?

Давайте сначала рассмотрим структуру URI REST API — это часть контракта? Должны ли клиенты делать закладки, жестко кодировать и вообще полагаться на URI API?

Если это так, то взаимодействие Клиента со службой REST больше не будет управляться самой службой, а будет зависеть от того, что Рой Филдинг называет внеполосной информацией:

REST API следует вводить без каких-либо предварительных знаний, кроме исходного URI (закладки) и набора стандартизированных типов мультимедиа, подходящих для целевой аудитории… Неудача здесь означает, что внеполосная информация управляет взаимодействием, а не гипертекстом.

Таким образом, URI не являются частью контракта ! Клиент должен знать только один URI — точку входа в API. Все другие URI должны быть обнаружены при использовании API.

2.2. Типы носителей являются частью контракта?

Как насчет информации о Типе носителя, используемой для представления Ресурсов, — это часть договора между Клиентом и Сервисом?

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

Следовательно, именно здесь служба REST должна сосредоточиться больше всего:

API REST должен тратить почти все свои описательные усилия на определение типа (типов) мультимедиа, используемых для представления ресурсов и управления состоянием приложения, или на определение расширенных имен отношений и/или разметки с поддержкой гипертекста для существующих стандартных типов мультимедиа.

Таким образом, определения типа носителя являются частью контракта и должны быть заранее известны клиенту, использующему API. Здесь на помощь приходит стандартизация.

Теперь у нас есть хорошее представление о том, что такое контракт, давайте перейдем к тому, как на самом деле решить проблему управления версиями.

3. Опции высокого уровня

Давайте теперь обсудим высокоуровневые подходы к управлению версиями REST API:

  • Управление версиями URI — версия пространства URI с использованием индикаторов версии.
  • Media Type Versioning — версия представления ресурса

Когда мы вводим версию в пространстве URI, представления ресурсов считаются неизменяемыми. Поэтому, когда необходимо внести изменения в API, необходимо создать новое пространство URI.

Например, предположим, что API публикует следующие ресурсы — пользователей и привилегии:

http://host/v1/users
http://host/v1/privileges

Теперь давайте рассмотрим, что критическое изменение в пользовательском API требует введения второй версии:

http://host/v2/users
http://host/v2/privileges

Когда мы верифицируем Media Type и расширяем язык, мы проходим Content Negotiation на основе этого заголовка. REST API будет использовать типы носителей 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"
}
}

Мы можем ознакомиться с этой статьей «Пользовательские типы мультимедиа для Rest API» для получения дополнительной информации и примеров по этому вопросу.

Здесь важно понимать, что клиент не делает никаких предположений о структуре ответа , кроме того, что определено в типе носителя.

Вот почему общие типы носителей не идеальны. Они не предоставляют достаточно семантической информации и вынуждают клиента использовать дополнительные подсказки для обработки фактического представления ресурса.

Исключением является использование какого-либо другого способа уникальной идентификации семантики содержимого, например схемы XML.

4. Преимущества и недостатки

Теперь, когда у нас есть четкое представление о том, что является частью Контракта между Клиентом и Сервисом, а также общий обзор вариантов версии API, давайте обсудим преимущества и недостатки каждого подхода.

Во- первых, введение идентификаторов версии в URI приводит к очень большому объему URI. Это связано с тем, что любое критическое изменение в любом из опубликованных API приведет к созданию совершенно нового дерева представлений для всего API. Со временем это становится бременем для обслуживания, а также проблемой для клиента, у которого теперь есть больше возможностей для выбора.

Идентификаторы версий в URI также строго негибкие . Невозможно просто развить API отдельного ресурса или небольшого подмножества общего API.

Как мы упоминали ранее, это подход «все или ничего». Если часть API переходит на новую версию, то вместе с ней должен перейти и весь API. Это также делает обновление клиентов с версии 1 до версии 2 серьезной задачей, что приводит к более медленным обновлениям и гораздо более длительным периодам прекращения действия старых версий.

Кэширование 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 в корне.

REST API должен соответствовать ограничению HATEOAS. В соответствии с этим большинство URI должны быть ОБНАРУЖЕНЫ клиентами, а не жестко запрограммированы. Изменение такого URI не следует рассматривать как несовместимое изменение. Новый URI может заменить старый, и клиенты смогут повторно обнаружить URI и продолжать функционировать.

Однако стоит отметить, что, хотя использование идентификаторов версий в URI проблематично по всем этим причинам, оно никоим образом не является несовместимым с REST.

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

В этой статье была предпринята попытка дать обзор очень разнообразной и сложной проблемы развития REST-сервиса . Мы обсудили два распространенных решения, преимущества и недостатки каждого из них, а также способы анализа этих подходов в контексте REST.

Статья завершается аргументами в пользу второго решения — управления версиями типов мультимедиа при изучении возможных изменений в RESTful API.

Полную реализацию этого туториала можно найти в проекте GitHub .

7. Дополнительная литература

Обычно ссылки на эти ресурсы для чтения размещены на протяжении всей статьи, но в данном случае их просто слишком много: