Лучшие практики для управления версиями API? [закрыто]

877

Существуют ли какие-либо известные инструкции или рекомендации по управлению версиями API REST веб-службы?

Я заметил, что AWS осуществляет управление версиями по URL-адресу конечной точки . Это единственный способ или есть другие способы достижения той же цели? Если есть несколько способов, каковы преимущества каждого способа?

Swaroop CH
источник

Ответы:

682

Это хороший и сложный вопрос. Тема разработки URI в то же время является наиболее важной частью REST API и , следовательно, потенциально долгосрочным обязательством по отношению к пользователям этого API .

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

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

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

Этот метод применяется к семантике глаголов HTTP (например, PUT должен всегда обновляться / заменяться) и к кодам состояния HTTP, которые поддерживаются в более ранних версиях API (они должны продолжать работать, чтобы клиенты API, работавшие без вмешательства человека, могли продолжать работать как это).

Кроме того, поскольку встраивание версии API в URI нарушило бы концепцию гипермедиа как движка состояния приложения (о чем говорится в диссертации доктора Roy T. Fieldings) из-за наличия адреса ресурса / URI, который будет меняться со временем, я пришел бы к выводу, что API версии не должны храниться в URI ресурса в течение длительного времени, а это означает, что URI ресурса, от которого могут зависеть пользователи API, должны быть постоянными .

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

Несколько мыслей по поводу поддержки версий API, у которых есть срок годности. Все платформы / языки программирования, обычно используемые для реализации веб-сервисов (Java, .NET, PHP, Perl, Rails и т. Д.), Позволяют легко привязывать конечные точки веб-сервиса к базовому URI. Таким образом, можно легко собирать и хранить коллекцию файлов / классов / методов в разных версиях API .

От POV пользователей API также легче работать и связываться с конкретной версией API, когда это очевидно, но только в течение ограниченного времени, то есть во время разработки.

Из POV сопровождающего API проще поддерживать разные версии API параллельно, используя системы контроля версий, которые преимущественно работают с файлами как наименьшей единицей управления версиями (исходного кода).

Однако с версиями API, четко видимыми в URI, есть предостережение: можно также возразить против этого подхода, поскольку история API становится видимой / видимой в структуре URI и, следовательно, подвержена изменениям со временем, что противоречит рекомендациям REST. Согласен!

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

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

  • привязка к определенной версии API (которая становится очевидной), но только в течение ограниченного времени

Например, если API v3.0 является последней версией API, следующие два должны быть псевдонимами (т.е. вести себя одинаково для всех запросов API):

http: // shonzilla / api / Customers / 1234 
http: // shonzilla / api /v3.0 / Customers / 1234
http: // shonzilla / api / v3 / Customers / 1234

Кроме того, клиенты API, которые все еще пытаются указать на старый API, должны быть проинформированы об использовании последней предыдущей версии API, если используемая версия API устарела или больше не поддерживается . Таким образом, доступ к любому из устаревших URI, как эти:

http: // shonzilla / api /v2.2 / Customers / 1234
http: // shonzilla / api /v2.0 / Customers / 1234
http: // shonzilla / api / v2 / Customers / 1234
http: // shonzilla / api /v1.1 / Customers / 1234
http: // shonzilla / api / v1 / Customers / 1234

должен вернуть любой из 30x кодов состояния HTTP, которые указывают перенаправление , которые используются в сочетании с Locationзаголовком HTTP, который перенаправляет к соответствующей версии URI ресурса, который остается этим:

Http: // shonzilla / API / клиенты / 1234

Существует как минимум два кода состояния перенаправления HTTP, которые подходят для сценариев управления версиями API:

  • 301 Перемещено навсегда, указывая на то, что ресурс с запрошенным URI навсегда перемещен в другой URI (который должен быть постоянной ссылкой на экземпляр ресурса, который не содержит информацию о версии API). Этот код состояния может использоваться для указания устаревшей / неподдерживаемой версии API, сообщая клиенту API, что версионный URI ресурса был заменен на постоянную ссылку ресурса .

  • 302 Found указывает, что запрошенный ресурс временно находится в другом месте, хотя запрошенный URI все еще может поддерживаться. Этот код состояния может быть полезен, когда URI без версии временно недоступны и запрос должен повторяться с использованием адреса перенаправления (например, указание на URI со встроенной версией APi), и мы хотим сказать клиентам, чтобы они продолжали его использовать (т.е. Permalinks).

  • другие сценарии можно найти в главе Redirection 3xx спецификации HTTP 1.1

Shonzilla
источник
142
Использование номера версии в URL не должно считаться плохой практикой при изменении базовой реализации. «Когда интерфейс службы меняется не обратно-совместимым образом, в действительности создается совершенно новая служба ... С точки зрения клиента, служба - это не более чем интерфейс и некоторые нефункциональные качества. . Если интерфейс службы меняется не обратно-совместимым образом, он больше не представляет собой экземпляр исходной службы, а представляет собой совершенно новую услугу ». ibm.com/developerworks/webservices/library/ws-version
benvolioT
7
Есть ли у вас мысли по поводу добавления заголовка с номером версии, чтобы его могли проверить клиенты или разработчики?
webclimber
11
См. Также использование заголовка Accept для указания версии, которую ожидает клиент: blog.steveklabnik.com/2011/07/03/…
Уэстон Рутер,
52
Для последней части: я бы сказал, что API, который устарел и больше не поддерживается, должен возвращаться 410 Gone, поскольку перенаправление может указывать, что новое местоположение является совместимым, когда это не так. Если API просто устарел, но все еще существует, WarningHTTP-заголовок в ответе может быть вариантом.
Майкл Стум
22
Как вы имеете дело с клиентами, которые уже используют стабильный URL-адрес, например, shonzilla / api / Customers / 1234, и хотите обновить его до новой версии? Как вы можете заставить их добавить V2 (старый) к URL?
Дежелл
273

URL НЕ должен содержать версии. Версия не имеет ничего общего с «идеей» запрашиваемого вами ресурса. Вы должны попытаться представить URL как путь к понятию, которое вам нужно, а не к тому, как вы хотите вернуть элемент. Версия диктует представление объекта, а не концепцию объекта. Как говорили другие авторы, вы должны указать формат (включая версию) в заголовке запроса.

Если вы посмотрите на полный HTTP-запрос для URL-адресов, которые имеют версии, это выглядит так:

(BAD WAY TO DO IT):

http://company.com/api/v3.0/customer/123
====>
GET v3.0/customer/123 HTTP/1.1
Accept: application/xml

<====
HTTP/1.1 200 OK
Content-Type: application/xml
<customer version="3.0">
  <name>Neil Armstrong</name>
</customer>

Заголовок содержит строку, которая содержит представление, которое вы запрашиваете («Accept: application / xml»). Вот где версия должна идти. Кажется, что каждый скрывает тот факт, что вы можете хотеть одну и ту же вещь в разных форматах и ​​что клиент должен иметь возможность спросить, чего он хочет. В приведенном выше примере клиент запрашивает ЛЮБОЕ XML-представление ресурса - не совсем истинное представление о том, что он хочет. Теоретически сервер может вернуть что-то совершенно не связанное с запросом, если это был XML, и его нужно будет проанализировать, чтобы понять, что это неправильно.

Лучший способ это:

(GOOD WAY TO DO IT)

http://company.com/api/customer/123
===>
GET /customer/123 HTTP/1.1
Accept: application/vnd.company.myapp.customer-v3+xml

<===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.myapp-v3+xml
<customer>
  <name>Neil Armstrong</name>
</customer>

Далее, скажем, клиенты думают, что XML слишком многословен, и теперь им нужен JSON. В других примерах у вас должен был бы быть новый URL для того же клиента, так что вы должны были бы получить:

(BAD)
http://company.com/api/JSONv3.0/customers/123
  or
http://company.com/api/v3.0/customers/123?format="JSON"

(или что-то подобное). На самом деле, каждый HTTP-запрос содержит формат, который вы ищете:

(GOOD WAY TO DO IT)
===>
GET /customer/123 HTTP/1.1
Accept: application/vnd.company.myapp.customer-v3+json

<===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.myapp-v3+json

{"customer":
  {"name":"Neil Armstrong"}
}

Используя этот метод, вы получаете гораздо больше свободы в дизайне и фактически придерживаетесь оригинальной идеи REST. Вы можете изменять версии, не нарушая работу клиентов, или постепенно изменять клиентов по мере изменения API. Если вы решите прекратить поддержку представления, вы можете отвечать на запросы с помощью кода состояния HTTP или пользовательских кодов. Клиент также может проверить, что ответ имеет правильный формат, и проверить XML.

Есть много других преимуществ, и я обсуждаю некоторые из них здесь, в моем блоге: http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html

Последний пример, показывающий, как плохо указывать версию в URL. Допустим, вы хотите получить некоторую информацию внутри объекта, и вы версировали свои различные объекты (клиенты v3.0, заказы v2.0 и shipto object v4.2). Вот неприятный URL, который вы должны указать в клиенте:

(Another reason why version in the URL sucks)
http://company.com/api/v3.0/customer/123/v2.0/orders/4321/
jeremyh
источник
10
Работа с независимой версией контракта на данные и версиями сервисного контракта в заголовке Accept выглядит грязной, так же как и в URL. Есть ли другие варианты ? Кроме того, если у меня есть несколько конечных точек (soap, rest), должно ли это также указываться в Accepts и позволить службе маршрутизации на стороне сервера определять направление к правильной конечной точке ИЛИ допустимо ли кодирование конечной точки в URL?
ideafountain
117
Я не могу согласиться с этим, по крайней мере, с точки зрения вашей последней причины. Кажется, это говорит о том, что разные части URI имеют разные версии. Но дело не в версии API. Дело в том, чтобы иметь одну версию для всего ресурса. Если вы меняете версии, это другой ресурс API. Вот почему не имеет смысла видеть company.com/api/v3.0/customer/123/v2.0/orders/4321, а скорее company.com/api/v3.0/customer/123/orders/4321 Вы не управляете версией какой-либо отдельной части ресурса, вы делаете версионирование ресурса в целом.
fool4jesus
90
Семантически использование номера версии в заголовке выглядит лучше. Но гораздо удобнее использовать URL: меньше подвержен ошибкам, лучше отлажен, легко виден разработчикам, легко модифицируется в остальных клиентских тестах.
Даниэль Сереседо
7
Я думаю, что ПЛОХО / ХОРОШО упрощает вопрос. API расшифровывается как «Интерфейс прикладного программирования», и интерфейсы управления версиями кажутся очень хорошей идеей API на самом деле не только для обслуживания ресурсов. Необходимо отделить то, что некоторые люди говорят об интерфейсах, а другие - о ресурсах. Если вы внимательно посмотрите на API Google Maps на вкладке сети, то увидите, что они включают номер версии API в URL. Например: maps.google.com/maps/api/jsv2 во время аутентификации. Jsv2 - это номер API.
Том Грунер
6
@ Гили: На самом деле, вы больше не должны использовать, -xпоскольку это устарело RFC6648 .
Джонатан W
98

Мы нашли практичным и полезным поместить версию в URL. Это позволяет легко определить, что вы используете, с первого взгляда. Мы используем псевдоним / foo для / foo / (последние версии) для простоты использования, для более коротких / более чистых URL и т. Д., Как следует из принятого ответа.

Сохранение обратной совместимости всегда является дорогостоящим и / или очень трудным. Мы предпочитаем заблаговременно уведомлять об устаревании, о перенаправлениях, как предлагается здесь, документах и ​​других механизмах.

Йоав Шапира
источник
5
Принятый ответ может быть правильным и максимально чистым. Однако для разработчика и повседневного пользователя API это, безусловно, самый простой в использовании и настройке. Самый прагматичный подход. Как указывают другие Google и Amazon, также используют этот подход.
Мухаммед Рехан Саид
46

Я согласен, что управление версиями представления ресурса лучше следует подходу REST ... но одна большая проблема с пользовательскими типами MIME (или типами MIME, которые добавляют параметр версии) - плохая поддержка записи в заголовки Accept и Content-Type в HTML и JavaScript.

Например, невозможно создать IMO для POST со следующими заголовками в формах HTML5, чтобы создать ресурс:

Accept: application/vnd.company.myapp-v3+json
Content-Type: application/vnd.company.myapp-v3+json 

Это происходит потому , что HTML5 enctypeатрибут является перечислением, поэтому ничего, кроме обычных application/x-www-formurlencoded, multipart/form-dataи text/plainявляются недействительными.

... и я не уверен, что он поддерживается во всех браузерах в HTML4 (который имеет более слабый атрибут encytpe, но будет проблемой реализации браузера относительно того, был ли передан тип MIME)

Из-за этого я теперь чувствую, что наиболее подходящий путь для версии - через URI, но я признаю, что это не «правильный» путь.

Kevsy
источник
14
Предполагая маршрут, в котором в заголовках определено управление версиями, можно сказать, что HTML-формы, использующие отправку собственных форм, всегда будут использовать самую последнюю версию API, поскольку они не будут передавать конкретную версию, которой хотят придерживаться. Тем не менее, запросы XHR действительно позволяют вам изменять принимаемые и читать заголовки типа содержимого. Так что основные формы действительно единственная проблема.
Кайл Хейс
Я не уверен, что согласен с тем, что URI является наиболее подходящим, но тот факт, что Content-Type не работает с формами, действительно очень важен.
апреля
2
@Kyle, я видел, как где-то в блоге говорилось, что если вы не укажете версию в заголовке запроса, лучше вернуться с первой версией API, а не самой последней, для лучшей совместимости.
Энди
Это действительно имеет большой смысл для меня сейчас, когда я думаю.
Кайл Хейс
@KyleHayes не забывайте о фреймах, видео / встраивании и других тегах типа "src / href".
pllee
21

Поместите вашу версию в URI. Одна версия API не всегда поддерживает типы из другой, поэтому аргумент, что ресурсы просто переносятся из одной версии в другую, просто неверен. Это не то же самое, что переключение формата с XML на JSON. Типы могут не существовать или могут быть изменены семантически.

Версии являются частью адреса ресурса. Вы переходите от одного API к другому. Неразумно скрывать адресацию в заголовке.

Шон О'Делл
источник
13

Есть несколько мест, где вы можете создавать версии в REST API:

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

  2. В заголовке Accepts: версия находится в типе файла. Как 'mp3' против 'mp4'. Это также будет работать, хотя IMO это работает немного менее приятно, чем ...

  3. В самом ресурсе. Многие форматы файлов имеют свои номера версий, как правило, в заголовке; это позволяет более новому программному обеспечению «просто работать», понимая все существующие версии типа файла, в то время как более старое программное обеспечение может работать, если указана неподдерживаемая (более новая) версия. В контексте REST API это означает, что ваши URI никогда не должны изменяться, только ваш ответ на конкретную версию данных, которую вам передали.

Я вижу причины использовать все три подхода:

  1. если вам нравится делать новые чистые API-интерфейсы, или для значительных изменений версий, где вы хотите такой подход.
  2. если вы хотите, чтобы клиент знал перед тем, как выполнить PUT / POST, будет ли он работать или нет.
  3. если все в порядке, если клиент должен выполнить PUT / POST, чтобы выяснить, будет ли он работать.
pjz
источник
8

Управление версиями вашего REST API аналогично управлению версиями любого другого API. Незначительные изменения могут быть сделаны на месте, серьезные изменения могут потребовать совершенно нового API. Самое простое для вас - это начинать с нуля каждый раз, когда наиболее целесообразно указывать версию в URL. Если вы хотите облегчить жизнь клиенту, вы пытаетесь поддерживать обратную совместимость, что вы можете сделать с устареванием (постоянное перенаправление), ресурсами в нескольких версиях и т. Д. Это более трудоемко и требует больше усилий. Но это также то, что REST поощряет в "Cool URI не меняются".

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

Александр Торстлинг
источник