Управление версиями API для маршрутов Rails


Я пытаюсь версию моего API, как полоса имеет. Ниже приведена последняя версия API - 2.

/api/users возвращает 301 в /api/v2/users

/api/v1/users возвращает индекс 200 пользователей в версии 1

/api/v3/users возвращает 301 в /api/v2/users

/api/asdf/users возвращает 301 в /api/v2/users

так что в основном все, что не указывает ссылки на последнюю версию, если только указанная версия не существует, перенаправляется на оно.

это то, что у меня есть до сих пор:

scope 'api', :format => :json do
  scope 'v:api_version', :api_version => /[12]/ do
    resources :users
  end

  match '/*path', :to => redirect { |params| "/api/v2/#{params[:path]}" }
end
6 137

6 ответов:

оригинальная форма этого ответа дико отличается, и ее можно найти здесь. Просто доказательство того, что есть более чем один способ освежевать кошку.

я обновил ответ, так как использовать пространства имен и использовать 301 редирект, а не по умолчанию 302. Спасибо pixeltrix и Bo Jeanes за подсказку по этим вещам.


вы можете носить действительно крепкий шлем, потому что это будет удар ваш ум.

Rails 3 routing API-это супер злой. Чтобы написать маршруты для вашего API, в соответствии с вашими требованиями выше, вам нужно только это:

namespace :api do
  namespace :v1 do
    resources :users
  end

  namespace :v2 do
    resources :users
  end
  match 'v:api/*path', :to => redirect("/api/v2/%{path}")
  match '*path', :to => redirect("/api/v2/%{path}")
end

если ваш ум все еще цел после этого момента, позвольте мне объяснить.

во-первых, мы называем namespace что очень удобно, когда вы хотите, чтобы куча маршрутов была привязана к определенному пути и модулю с аналогичным именем. В этом случае, мы хотим, чтобы все маршруты внутри блока для наших namespace Для быть ограничен для контроллеров в пределах Api модуль и все запросы к путям внутри этого маршрута будут иметь префикс api. Такие запросы, как /api/v2/users, Я знаю?

внутри пространства имен мы определяем еще два пространства имен (woah!). На этот раз мы определяем пространство имен "v1", поэтому все маршруты для контроллеров здесь будут находиться внутри V1 модуль Api модуль: Api::V1. Определением resources :users внутри этого маршрута, контроллер будет находиться в Api::V1::UsersController. Это версия 1, и вы получаете там, делая запросы, как /api/v1/users.

Версия 2-это только крошечные несколько иначе. Вместо контроллера, обслуживающего его, он находится в Api::V1::UsersController, сейчас на Api::V2::UsersController. Вы получаете там, делая запросы, как /api/v2/users.

далее, a это. Это будет соответствовать всем маршрутам API, которые идут к таким вещам, как /api/v3/users.

это та часть,которую я должен был посмотреть. Элемент :to => опция позволяет указать, что конкретный запрос должен быть перенаправлено куда-то еще-я знал это много-но я не знал, как заставить его перенаправить куда-то еще и передать часть исходного запроса вместе с ним.

чтобы сделать это, мы называем redirect метод и передать ему строку с интерполированными . Когда приходит запрос, который соответствует этому финалу match, он будет интерполировать path параметр в расположение %{path} внутри строки и перенаправить пользователя туда, куда им нужно идти.

наконец, мы используем другой match для маршрутизации всех оставшихся путей с префиксом /api и перенаправить их в /api/v2/%{path}. Это означает запросы типа /api/users пойдет /api/v2/users.

я не мог понять, как получить /api/asdf/users чтобы соответствовать, потому что, как вы определяете, если это должен быть запрос /api/<resource>/<identifier> или /api/<version>/<resource>?

в любом случае, это было интересно и я надеюсь, что это поможет вам!

несколько вещей, чтобы добавить:

ваш редирект матч не будет работать для определенных маршрутов-the *api парам жаден и проглотит все, например /api/asdf/users/1 перенаправит к /api/v2/1. Вам было бы лучше использовать обычный парам, как :api. По общему признанию, это не будет соответствовать случаи, как /api/asdf/asdf/users/1 но если у вас есть вложенные ресурсы в вашем api, это лучшее решение.

Райан почему тебе не нравится namespace? :-), электронной.г:

current_api_routes = lambda do
  resources :users
end

namespace :api do
  scope :module => :v2, &current_api_routes
  namespace :v2, &current_api_routes
  namespace :v1, &current_api_routes
  match ":api/*path", :to => redirect("/api/v2/%{path}")
end

это преимущество версионных и универсальных именованных маршрутов. Еще одно примечание-конвенция при использовании :module использовать подчеркивание, например:api/v1 не 'Api:: V1'. В какой-то момент последний не работал, но я считаю, что это было исправлено в Rails 3.1.

кроме того, при выпуске v3 вашего API маршруты будут обновлены следующим образом:

current_api_routes = lambda do
  resources :users
end

namespace :api do
  scope :module => :v3, &current_api_routes
  namespace :v3, &current_api_routes
  namespace :v2, &current_api_routes
  namespace :v1, &current_api_routes
  match ":api/*path", :to => redirect("/api/v3/%{path}")
end

конечно, вполне вероятно, что ваш API имеет разные маршруты между версиями, и в этом случае вы можете сделать это:

current_api_routes = lambda do
  # Define latest API
end

namespace :api do
  scope :module => :v3, &current_api_routes
  namespace :v3, &current_api_routes

  namespace :v2 do
    # Define API v2 routes
  end

  namespace :v1 do
    # Define API v1 routes
  end

  match ":api/*path", :to => redirect("/api/v3/%{path}")
end

Если это вообще возможно, я бы предложил переосмыслить ваши URL-адреса, чтобы версия не была в url-адресе, а помещалась в заголовок accepts. Этот ответ переполнения стека входит в него хорошо:

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

и эта ссылка показывает, как именно это сделать с маршрутизацией rails:

http://freelancing-gods.com/posts/versioning_your_ap_is

Я не большой поклонник версий по маршрутам. Мы построили VersionCake для поддержки более простой формы управления версиями API.

включив номер версии API в имя файла каждого из наших соответствующих представлений (jbuilder, RABL и т. д.), Мы сохраняем версию ненавязчивой и допускаем легкую деградацию для поддержки обратной совместимости (например, если v5 представления не существует, мы визуализируем v4 представления).

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

бесстыдный плагин: Versionist поддерживает эти варианты использования (и более.)

https://github.com/bploetz/versionist

Райан Бигг ответ работал для меня.

Если вы также хотите сохранить параметры запроса через перенаправление, вы можете сделать это следующим образом:

match "*path", to: redirect{ |params, request| "/api/v2/#{params[:path]}?#{request.query_string}" }