Исходная форма этого ответа сильно отличается, и ее можно найти здесь . Просто доказательство того, что есть несколько способов снять шкуру с кошки.
С тех пор я обновил ответ, чтобы использовать пространства имен и редирект 301, а не 302 по умолчанию. Спасибо pixeltrix и Bo Jeanes за подсказки по этим вопросам.
Возможно, вы захотите надеть действительно прочный шлем, потому что он взорвет ваш мозг .
API маршрутизации Rails 3 супер злой. Чтобы написать маршруты для вашего 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
, знаете?
Внутри пространства имен мы определяем еще два пространства имен (воах!). На этот раз мы определяем в «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 match
. Это будет соответствовать всем маршрутам API, которые относятся к таким вещам, как /api/v3/users
.
Это та часть, которую я должен был найти. Эта :to =>
опция позволяет вам указать, что конкретный запрос должен быть перенаправлен куда-то еще - я это знал, но я не знал, как заставить его перенаправить куда-то еще и передать вместе с ним часть исходного запроса. .
Для этого мы вызываем redirect
метод и передаем ему строку со специально-интерполированным %{path}
параметром. Когда приходит запрос, соответствующий этому финалу match
, он интерполирует path
параметр в местоположение %{path}
внутри строки и перенаправляет пользователя туда, куда ему нужно перейти.
Наконец, мы используем другой match
для маршрутизации всех оставшихся путей с префиксом /api
и перенаправления на них /api/v2/%{path}
. Это означает, что такие запросы /api/users
будут отправлены в /api/v2/users
.
Я не мог понять, как добиться /api/asdf/users
совпадения, потому что как определить, должен ли это быть запрос к /api/<resource>/<identifier>
или /api/<version>/<resource>
?
В любом случае, это было интересно исследовать, и я надеюсь, что это поможет вам!
Please note that this redirection is a 301 “Moved Permanently” redirect. Keep in mind that some web browsers or proxy servers will cache this type of redirect, making the old page inaccessible.
Еще пара вещей:
Ваше совпадение перенаправления не будет работать для определенных маршрутов -
*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, ¤t_api_routes namespace :v2, ¤t_api_routes namespace :v1, ¤t_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, ¤t_api_routes namespace :v3, ¤t_api_routes namespace :v2, ¤t_api_routes namespace :v1, ¤t_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, ¤t_api_routes namespace :v3, ¤t_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
источник
/api/asdf/users?
как и/api/users/1
? Я не мог понять этого в моем обновленном ответе, поэтому решил, что вы можете знать способЕсли это вообще возможно, я бы предложил переосмыслить ваши URL-адреса, чтобы версия не была в URL-адресе, а была помещена в заголовок accept. Этот ответ о переполнении стека хорошо подходит:
Лучшие практики для управления версиями API?
и эта ссылка показывает, как именно это сделать с маршрутизацией рельсов:
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}" }
источник
Реализовал это сегодня и нашел то, что я считаю «правильным» на RailsCasts - управление версиями REST API . Так просто. Так что ремонтопригодный. Так эффективно.
Добавить
lib/api_constraints.rb
(даже не нужно изменять vnd.example.)class ApiConstraints def initialize(options) @version = options[:version] @default = options[:default] end def matches?(req) @default || req.headers['Accept'].include?("application/vnd.example.v#{@version}") end end
Настроить
config/routes.rb
такrequire 'api_constraints' Rails.application.routes.draw do # Squads API namespace :api do # ApiConstaints is a lib file to allow default API versions, # this will help prevent having to change link names from /api/v1/squads to /api/squads, better maintainability scope module: :v1, constraints: ApiConstraints.new(version:1, default: true) do resources :squads do # my stuff was here end end end resources :squads root to: 'site#index'
Отредактируйте свой контроллер (т.е.
/controllers/api/v1/squads_controller.rb
)module Api module V1 class SquadsController < BaseController # my stuff was here end end end
Затем вы можете изменить все ссылки в своем приложении с
/api/v1/squads
на,/api/squads
и вы сможете ЛЕГКО реализовать новые версии API, даже не меняя ссылки.источник