Как версировать REST URI

111

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

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

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

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

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

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

Майк Поун
источник
Возможное дублирование рекомендаций по управлению версиями API?
Хелен

Ответы:

34

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

Зеф Хемель
источник
11
Вопрос в том, обсуждаем ли мы другой РЕСУРС? Или другое представление об этом ресурсе? Делает ли REST различие между представлением и ресурсом?
Cheeso
1
@Cheeso - OP указывает, что это другое представление, а не другой ресурс, поэтому мой ответ.
Грег Бич,
На этот вопрос был дан более подробный ответ, прежде чем здесь stackoverflow.com/q/389169/104261
Тарас Аленин
+1 за «Параметры запроса, подобные вашему второму варианту, лучше всего использовать для передачи дополнительной информации (запроса), относящейся к запросу, а не к ресурсу»
Энди
Я думаю, что для разных представлений вам следует использовать заголовки вроде «Принять», тогда клиент может указать серверу «Я принимаю только версию 4», и сервер может ответить с этим представлением. Если подтверждение не отправлено, предоставляется последняя версия.
Карлос Вердес
191

Не обновляйте 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, если вы не проанализируете их с помощью загрузки кода, и в этот момент управление версиями является спорным вопросом.

Даррел Миллер
источник
66
Чтобы обратиться к пунктам маркированного списка. 1. вы не нарушаете постоянные ссылки, потому что постоянные ссылки ссылаются на конкретную версию 2. Если все версионировано, это не проблема. Старые URL-адреса могут работать. В идеале вам не нужно, чтобы URL-адрес версии 4 возвращал связь с ресурсом версии 3. 3. Возможно
Майк Поун
10
Представьте, что при обновлении до новой версии веб-браузера все ваши избранные закладки сломались! Помните, что концептуально пользователь сохраняет ссылку на ресурс, а не на версию представления ресурса.
Даррел Миллер
11
@Gili Чтобы удовлетворить требование, чтобы REST api был информативным, необходимо, чтобы заголовок типа содержимого предоставлял полное семантическое описание сообщения. Другими словами, ваш тип носителя - это ваш контракт данных. Если вы доставляете application / xml или application / json, вы ничего не говорите клиенту о том, что содержится в этом XML / Json. В тот момент, когда клиентское приложение достигает вытаскивания / клиента / имени, вы создаете связь, основанную на информации, которой нет в сообщении. Устранение внеполосной связи имеет решающее значение для достижения RESTfulness.
Даррел Миллер,
6
@Gili. Клиент не должен заранее знать URL-адреса API, кроме корневого URL. Не следует привязывать форматы представления к определенным URL-адресам. Когда дело доходит до выбора медиа-типов, вам действительно нужно выбирать между конкретным форматом, например application / vnd.mycompany.myformat + xml, или стандартизированным, например XHtml, Atom, RDF и т. Д.
Даррел Миллер
4
Есть ли смысл ставить версию API в отдельное поле заголовка? Примерно так: Принять: application / com.example.myapp + json; version = 1.0
Эрик,
21

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

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

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

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

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

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

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

SerialSeb
источник
Что, если представление необходимо существенно изменить и оно не будет обратно совместимым?
Mike Pone,
1
Этого можно избежать, создав свой тип мультимедиа расширяемым образом, например, используя пространства имен и расширяемый xsd, или существующие XML-форматы, например, Atom. Если вам действительно нужно, подойдет другой тип носителя.
SerialSeb
1
Мне нравится этот полностью верный ответ, но я думаю, что предлагаемый URI больше демонстрирует суть, чем для реального сценария, в котором вам действительно нужны «взломанные» URI.
Дэйв Ван ден Эйнде,
10

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

Лучшие практики для управления версиями 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

jeremyh
источник
11
Извините, но я не думаю, что у вас действительно получаются такие глупые URL-адреса. Вы привязываете номера версий к определенному ресурсу или (что еще хуже) к определенному представлению. Это было бы глупо, ИМО. Скорее, вы управляете версиями API, поэтому у вас никогда не будет более одной версии в URI.
fool4jesus 08
3

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

  • Добавление версии в путь URI:

    http://example.com/api/v1/foo
    
    http://example.com/api/v2/foo
    

    Если у вас критическое изменение, вы должны увеличить версию, например: v1, v2, v3 ...

    Вы можете реализовать контроллер в своем коде следующим образом:

    @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"));
    }
    
  • Управление версиями параметров запроса:

    http://example.com/api/v2/foo/param?version=1
    http://example.com/api/v2/foo/param?version=2
    

    Параметр версии может быть необязательным или обязательным в зависимости от того, как вы хотите использовать API.

    Реализация может быть похожей на это:

    @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]
    

    или:

    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"));
    }
    
  • Изменение имен хостов или использование шлюзов API:

    По сути, вы перемещаете API с одного имени хоста на другое. Вы можете даже назвать это построение новым API для тех же ресурсов.

    Также вы можете сделать это с помощью шлюзов API.

Хавьер К.
источник
2

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

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

УберСтив
источник
2

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

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

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

Асма Зубаир
источник
1

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

Пол Морган
источник
1

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

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

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

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

inf3rno
источник
0

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

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

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

Ярко
источник