Как управлять версиями REST URI

Каков наилучший способ версии REST URI? В настоящее время у нас есть версия # в самом URI, т.е.

http://example.com/users/v4/1234/

для версии 4 этого представления.

Принадлежит ли версия к queryString? т.е.

http://example.com/users/1234?version=4

Или управление версиями лучше всего выполнять другим способом?


person Mike Pone    schedule 09.06.2009    source источник
comment
Возможный дубликат Рекомендации по управлению версиями API?   -  person Helen    schedule 11.10.2017


Ответы (11)


Я бы сказал, что лучше сделать его частью самого URI (вариант 1), потому что v4 идентифицирует ресурс, отличный от v3. Параметры запроса, подобные вашему второму варианту, лучше всего использовать для передачи дополнительной (запросной) информации, связанной с запросом, а не с ресурсом.

person Zef Hemel    schedule 09.06.2009
comment
Вопрос в том, обсуждаем ли мы другой РЕСУРС? Или другое представление этого ресурса? Делает ли REST различие между представлением и ресурсом? - person Cheeso; 10.06.2009
comment
@Cheeso - OP указывает, что это другое представление, а не другой ресурс, отсюда и мой ответ. - person Greg Beech; 10.06.2009
comment
На это был дан более подробный ответ, прежде чем здесь stackoverflow.com/q/389169/104261 - person Taras Alenin; 04.05.2012
comment
+1 для параметров запроса, таких как ваш второй вариант, лучше всего использовать для передачи дополнительной (запросной) информации, связанной с запросом, а не с ресурсом. - person andy; 25.03.2014
comment
Я думаю, что для разных представлений вы должны использовать заголовки, такие как Accept, тогда клиент может указать серверу, что я принимаю только версию 4, и сервер может ответить с этим представлением. Если подтверждение не отправлено, предоставляется последняя версия. - person Carlos Verdes; 22.01.2017

Не версионируйте URL-адреса, потому что...

  • вы ломаете постоянные ссылки
  • Изменения URL будут распространяться через ваш интерфейс, как болезнь. Что вы делаете с репрезентациями, которые не изменились, но указывают на репрезентацию, которая изменилась? Если вы измените URL-адрес, вы сломаете старых клиентов. Если вы оставите URL-адрес, ваши новые клиенты могут не работать.
  • Версии типов носителей — гораздо более гибкое решение.

Предполагая, что ваш ресурс возвращает какой-то вариант application/vnd.yourcompany.user+xml, все, что вам нужно сделать, это создать поддержку для нового медиа-типа application/vnd.yourcompany.userV2+xml и с помощью магии согласования содержимого v1 и клиенты v2 могут мирно сосуществовать.

В интерфейсе RESTful самое близкое к контракту — это определение типов мультимедиа, которыми обмениваются клиент и сервер.

URL-адреса, которые клиент использует для взаимодействия с сервером, должны предоставляться сервером, встроенным в ранее извлеченные представления. Единственный URL-адрес, который должен быть известен клиенту, — это корневой URL-адрес интерфейса. Добавление номеров версий к URL-адресам имеет значение только в том случае, если вы создаете URL-адреса на клиенте, что вы не должны делать с интерфейсом RESTful.

Если вам нужно внести изменения в ваши медиа-типы, которые сломают ваших существующих клиентов, создайте новый и оставьте свои URL-адреса в покое!

И для тех читателей, которые в настоящее время говорят, что это не имеет смысла, если я использую application/xml и application/json в качестве медиа-типов. Как мы должны версионировать их? Вы не. Эти медиа-типы практически бесполезны для интерфейса RESTful, если вы не анализируете их с помощью загрузки кода, и в этот момент управление версиями является спорным вопросом.

person Darrel Miller    schedule 10.06.2009
comment
Для решения маркированных пунктов. 1. вы не ломаете постоянные ссылки, потому что постоянные ссылки ссылаются на конкретную версию 2. Если все версионировано, это не проблема. Старые URL-адреса все еще могут работать. В идеале вы бы не хотели, чтобы URL-адрес версии 4 возвращал ассоциацию с ресурсом версии 3. 3. Возможно - person Mike Pone; 12.06.2009
comment
Представьте, если бы вы перешли на новую версию веб-браузера, и все ваши избранные закладки сломались! Помните, что концептуально пользователь сохраняет ссылку на ресурс, а не на версию представления ресурса. - person Darrel Miller; 12.06.2009
comment
@Darrel, я согласен со всем, что вы написали, за исключением комментария о том, что медиа-типы xml/json бесполезны для интерфейсов RESTful. Не могли бы вы уточнить? Я не понимаю, что вы имеете в виду... - person Gili; 23.02.2010
comment
@Gili Чтобы удовлетворить требование, чтобы REST API был самоописательным, необходимо, чтобы заголовок типа содержимого предоставлял полное семантическое описание сообщения. Другими словами, ваш тип носителя — это ваш контракт данных. Если вы доставляете application/xml или application/json, вы ничего не сообщаете клиенту о том, что содержится в этом XML/Json. В тот момент, когда клиентское приложение достигает выпадающего списка /Customer/Name, вы создаете связь, основанную на информации, которой нет в сообщении. Устранение внеполосной связи имеет решающее значение для достижения RESTfulness. - person Darrel Miller; 23.02.2010
comment
@Darrel, разве комбинация URL и XML/JSON не говорит вам, какой точный формат ожидать? Если нет, то что посоветуете вместо этого? - person Gili; 23.02.2010
comment
@Gili Клиент не должен заранее знать URL-адреса API, кроме корневого URL-адреса. Не следует привязывать форматы представления к конкретным URL-адресам. Когда дело доходит до выбора типов мультимедиа, вам действительно нужно выбирать между конкретным форматом, таким как application/vnd.mycompany.myformat+xml, или стандартизированным, например, XHtml, Atom, RDF и т. д. - person Darrel Miller; 23.02.2010
comment
@Darrel, в спецификации указано, что страница A ссылается на страницу B, но не дает URL-адрес последней. Далее указывается точный формат, который примет представление JSON для страницы B. Разве это не полностью RESTful? - person Gili; 23.02.2010
comment
@Gili Проблема в том, что каждый запрос должен быть самоописательным. Если вы скопируете URL-адрес страницы B и отправите электронное письмо другу, вы потеряете контекст. Другая проблема заключается в том, что для того, чтобы определить спецификацию для страницы А, вам в любом случае потребуется создать собственный тип носителя. - person Darrel Miller; 24.02.2010
comment
Вот хорошая статья, в которой объясняется, как управлять версиями представления ресурсов: informit.com/ статьи/статьи.aspx?p=1566460 - person Gili; 01.10.2010
comment
@Darrel, независимо от того, используете ли вы URI или тип контента, новая версия не распространяется как болезнь? Например, если URI a версии 2 указывает на URI b, то последний также должен предоставить версию 2, иначе клиенты должны одновременно поддерживать форматы версии 1 и версии 2. Что ты посоветуешь? - person Gili; 01.10.2010
comment
@Gili Вы предполагаете, что служба возвращает только один тип мультимедиа. Представьте себе сервис, который поддерживает application/vnd.acme.document и 'application/vnd.acme.validationrules'. Каждый документ указывает на другой ресурс, содержащий правила проверки. Версионируя типы носителей, я могу обрабатывать критические изменения в правилах проверки, вводя application/vnd.acme.validationrulesV2 без создания новой версии «application/vnd.acme.document». - person Darrel Miller; 01.10.2010
comment
@Gili Вот еще одна статья на тему barelyenough.org/blog/2008 /05/управление версиями остальных веб-сервисов - person Darrel Miller; 01.10.2010
comment
@Darrel, ссылка, которую вы упомянули, отличная, но она по-прежнему не отвечает на вопрос, что произойдет, если ресурс A имеет версии 1 и 2 и указывает на ресурс B, у которого есть только версия 1. Действительно ли клиент должен помнить, какая версия он должен запрашивать какой ресурс? Каждый раз, когда кто-то ссылается на A, независимо от его версии, обязательно запрашивайте версию A 2. Это правильно? - person Gili; 01.10.2010
comment
@Gili На самом деле это то, что делает заголовок accept. Он объявляет серверу все типы мультимедиа, которые может поддерживать клиент. - person Darrel Miller; 01.10.2010
comment
@Darrel, скажем, вы пишете клиент, который поддерживает версию 2 протокола (решение во время компиляции), как поможет использование заголовка accept (решение во время выполнения)? Разве мне не нужно решать во время компиляции, какую версию каждой страницы я буду запрашивать? Я не думаю, что для клиентов версии 10 реалистично также поддерживать версии с 1 по 9 и делать все возможное в зависимости от того, что возвращает заголовок Accept во время выполнения. - person Gili; 03.10.2010
comment
@Gili Клиент отправляет заголовок Accept на сервер, чтобы объявить, какие версии он поддерживает. Именно сервер должен поддерживать все версии, а не клиент. Фактически заголовок Accept запекается в клиенте во время компиляции. - person Darrel Miller; 03.10.2010
comment
@Darrel, так что, если я вас правильно понял, вы согласны с тем, что клиент должен быть запечен во время компиляции с сопоставлением [URI, Version], чтобы во время выполнения он знал, что даже несмотря на то, что ресурс A имеет версию 2, когда он ссылается Ресурс B, последний должен быть получен с использованием версии 1. Когда он достигает ресурса A (независимо от того, откуда он получил URI), он использует Accept: application/vnd.ResourceAv2. Когда он достигает ресурса B (независимо от того, откуда он получил URI), он использует Accept: application/vnd.ResourceBv1. Это правильно? - person Gili; 04.10.2010
comment
@ Гили Не уверен, что понимаю. Никакие URI не запекаются. Клиент также не знает, какой тип носителя будет возвращен из любого URI. Клиент должен решить, что делать, основываясь на том, что возвращается. Все, что он знает, это то, что он знает, как обрабатывать application/vnd.ResourceAv2 и application/vnd.ResourceBv1, поэтому он отправляет Accept: application/vnd.ResourceAv2, application/vnd.ResourceBv1 на каждый URI, который он запрашивает. - person Darrel Miller; 04.10.2010
comment
@Darrel: Хорошо, это последнее предложение подтверждает то, о чем я спрашивал: клиент отправляет Accept: «список типов мультимедиа, о которых он знает» на каждый URI. Спасибо. - person Gili; 05.10.2010
comment
Есть ли смысл выносить версию API в отдельное поле заголовка? Вот так: Примите: application/com.example.myapp+json; версия=1.0 - person Erik; 29.04.2012
comment
Я не знал, что вы можете определять свои собственные типы мультимедиа; Я предполагал, что существуют стандарты (например, application/xml и application/json). И такие фреймворки, как Jersey, усугубляют это заблуждение благодаря таким вещам, как перечисления медиа-типов. - person smcg; 24.10.2012
comment
причиной добавления версии к URL является изменение представления ресурса, поэтому предпочтительнее использовать новый тип носителя. Однако на самом деле кажется, что многие компании помещают информацию о версии в URL-адрес. это может быть причина, предложенная dcerecedo в ответе на аналогичный вопрос. Возможно, версия в URL-адресе легко отлаживается и более удобочитаема для людей. - person andy; 25.11.2013
comment
Значит, Google принял неправильное решение при создании всех своих REST API? :/ Как упоминалось здесь и, вероятно, во многих других местах, не обязательно хороший и плохой способ управления версиями. Каждое решение имеет свои преимущества и недостатки, и в зависимости от целевых потребителей API и варианта использования одно может быть более подходящим, чем другое. - person Vincent Sels; 23.10.2015
comment
@VincentSels Если бы у меня был доллар каждый раз, когда «какое-то крупное предприятие» принимало неправильное решение, мне бы не пришлось работать. Кроме того, мне, вероятно, следует обновить этот ответ, поскольку моя позиция изменилась с тех пор, как я написал это 6 лет назад. - person Darrel Miller; 23.10.2015
comment
Я думаю, что сообщение в блоге Троя Ханта хорошо относительно три зла (url против параметра заголовка против Accept заголовка). В этой статье объясняется, почему автор считает Accept Headers лучшим. - person Avec; 29.11.2016

А, я снова надеваю свою старую сварливую шляпу.

С точки зрения ReST это вообще не имеет значения. Не колбаса.

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

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

Но в любом случае клиент может обрабатывать документ только в понятном ему формате и переходить по ссылкам в нем. Он должен знать о связях отношений (переходах). Так что то, что находится в URI, совершенно не имеет значения.

Лично я бы проголосовал за http://localhost/3f3405d5-5984-4683-bf26-aca186d21c04

Совершенно допустимый идентификатор, который не позволит любому разработчику клиента или человеку, касающемуся системы, задать вопрос, следует ли помещать v4 в начало или в конец URI (и я полагаю, что с точки зрения сервера вы не должны иметь 4 версии, но 4 типа носителя).

person SerialSeb    schedule 10.06.2009
comment
Что, если представление необходимо существенно изменить и оно не будет обратно совместимым? - person Mike Pone; 11.06.2009
comment
Создавая свой медиа-тип расширяемым образом, например, используя пространства имен и расширяемый xsd или существующие форматы xml, такие как атом, этого можно избежать. Если вам действительно нужно, другой тип носителя — это то, что вам нужно. - person SerialSeb; 16.06.2009
comment
Мне нравится этот полностью верный ответ, но я думаю, что предложенный URI больше для демонстрации сути, чем для реального сценария, в котором вам нужны «взламываемые» URI. - person Dave Van den Eynde; 31.10.2012

Вы НЕ должны указывать версию в URL-адресе, вы должны указать версию в заголовке Accept запроса - см. мой пост в этой теме:

Рекомендации по управлению версиями API?

Если вы начнете вставлять версии в URL-адрес, вы получите такие глупые URL-адреса: http://company.com/api/v3.0/customer/123/v2.0/orders/4321/

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

person jeremyh    schedule 19.07.2011
comment
Извините, но я не думаю, что вы закончите с такими глупыми URL-адресами. Вы привязываете номера версий к определенному ресурсу или (что еще хуже) к определенному представлению. Это было бы глупо, имхо. Скорее, вы управляете версиями API, поэтому у вас никогда не будет более одной версии в URI. - person fool4jesus; 08.01.2013

Эти (менее конкретные) вопросы SO о версиях REST API могут быть полезны:

person Pete TerMaat    schedule 09.06.2009

Существует 4 различных подхода к управлению версиями API:

  • #P2#
    http://example.com/api/v1/foo
    
    http://example.com/api/v2/foo
    
    <цитата> #P3# #P4#
    @RestController
    public class FooVersioningController {
    
    @GetMapping("v1/foo")
    public FooV1 fooV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping("v2/foo")
    public FooV2 fooV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • #P5#
    http://example.com/api/v2/foo/param?version=1
    http://example.com/api/v2/foo/param?version=2
    
    #P6#
    #P7#
    @GetMapping(value = "/foo/param", params = "version=1")
    public FooV1 paramV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping(value = "/foo/param", params = "version=2")
    public FooV2 paramV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • Передача пользовательского заголовка:

    http://localhost:8080/foo/produces
    

    С заголовком:

    headers[Accept=application/vnd.company.app-v1+json]
    

    or:

    headers[Accept=application/vnd.company.app-v2+json]
    

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

    Возможная реализация:

    @GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v1+json")
    public FooV1 producesV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v2+json")
    public FooV2 producesV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • #P12# <цитата> #P13# #P14#
person Javier C.    schedule 18.09.2018

Я хотел создать версионные API и нашел эту статью очень полезной:

http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http

Есть небольшой раздел «Я хочу, чтобы мой API был версионным». Я нашел его простым и понятным. Суть в том, чтобы использовать поле Accept в заголовке для передачи информации о версии.

person Asma Zubair    schedule 17.11.2013

Если службы REST требуют аутентификации перед использованием, вы можете легко связать ключ/токен API с версией API и выполнить внутреннюю маршрутизацию. Для использования новой версии API может потребоваться новый ключ API, связанный с этой версией.

К сожалению, это решение работает только для API на основе аутентификации. Однако он хранит версии вне URI.

person UberSteve    schedule 13.11.2013

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

Технически REST API не нарушается при изменении URL-адреса (результат ограничения единого интерфейса). Он ломается только тогда, когда связанная семантика (например, специфичный для API словарь RDF) изменяется без обратной совместимости (редко). В настоящее время многие люди не используют ссылки для навигации (ограничение HATEOAS) и словари для аннотирования своих ответов REST (ограничение самоописательного сообщения), поэтому их клиенты ломаются.

Пользовательские типы MIME и управление версиями типов MIME не помогают, поскольку помещение связанных метаданных и структуры представления в короткую строку не работает. офк. метаданные и структура будут часто меняться, так же как и номер версии...

Итак, чтобы ответить на ваш вопрос, лучше всего аннотировать ваши запросы и ответы словарями (Hydra, связанные данные) и забудьте о версиях или используйте их только для необратимых изменений словарного запаса (например, если вы хотите заменить словарь другим).

person inf3rno    schedule 23.05.2014

Я бы включил версию в качестве необязательного значения в конце URI. Это может быть суффикс, такой как /V4, или параметр запроса, как вы описали. Вы даже можете перенаправить /V4 в параметр запроса, чтобы поддерживать оба варианта.

person Paul Morgan    schedule 09.06.2009

Я голосую за то, чтобы сделать это в MIME-типе, но не в URL-адресе. Но причина не такая, как у других парней.

Я думаю, что URL-адрес должен быть уникальным (за исключением этих перенаправлений) для поиска уникального ресурса. Итак, если вы принимаете /v2.0 в URL-адресах, почему это не /ver2.0, /v2/ или /v2.0.0? Или даже -alpha и -beta? (тогда это полностью становится концепцией semver)

Таким образом, версия в MIME-типе более приемлема, чем URL-адрес.

person Yarco    schedule 27.03.2017