StripeのようにAPIをバージョン管理しようとしています。以下に、最新のAPIバージョンが2であることを示します。
/api/users
は/api/v2/users
に301を返します
/api/v1/users
は、バージョン1で200人のユーザーインデックスを返します
/api/v3/users
は/api/v2/users
に301を返します
/api/asdf/users
は/api/v2/users
に301を返します
基本的に、バージョンを指定しないものはすべて、指定されたバージョンが存在しない限り最新のものにリンクしてからリダイレクトします。
これは私がこれまでに持っているものです:
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
この回答の元の形式は大きく異なり、ここにあります 。猫の皮を剥ぐ方法が複数あることを証明してください。
名前空間を使用し、デフォルトの302ではなく301リダイレクトを使用するようになったため、答えを更新しました。それらのプロンプトを表示してくれたpixeltrixとBo Jeanesに感謝します。
本当に強いヘルメットを着用することもできます。これは、あなたの心を吹き飛ばすからです。
Rails 3ルーティング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
などのリクエスト、知ってる?
名前空間内で、さらに2つの名前空間を定義します(すごい!)。今回は「v1」名前空間を定義しているため、ここのコントローラーのすべてのルートは、Api
モジュール内のV1
モジュール内にあります:Api::V1
。このルート内でresources :users
を定義すると、コントローラーはApi::V1::UsersController
に配置されます。これはバージョン1であり、/api/v1/users
のようなリクエストを行うことでそこに到達します。
バージョン2は、tinyビットと異なるだけです。それを提供するコントローラーがApi::V1::UsersController
にあるのではなく、Api::V2::UsersController
にあります。 /api/v2/users
のようなリクエストを行うことでそこに到達します。
次に、match
が使用されます。これは、/api/v3/users
などに向かうすべてのAPIルートに一致します。
これは私が調べなければならなかった部分です。 :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>
への要求であるかどうかをどのように判断するのですか?
とにかく、これは研究するのが楽しかったし、それがあなたに役立つことを願っています!
追加することがいくつかあります:
リダイレクト一致は特定のルートでは機能しません-*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。
また、APIのv3をリリースすると、ルートは次のように更新されます。
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
可能な場合は、バージョンがURLに含まれないようにURLを再考することをお勧めしますが、Acceptsヘッダーに配置されます。このスタックオーバーフローの答えはうまくいきます:
このリンクは、Railsルーティングでこれを行う方法を正確に示しています。
私はルートによるバージョン管理の大ファンではありません。 VersionCake を構築して、より簡単な形式のAPIバージョン管理をサポートしました。
それぞれのビュー(jbuilder、RABLなど)の各ファイル名にAPIバージョン番号を含めることで、バージョン管理を控えめに保ち、後方互換性をサポートするために簡単に劣化できるようにします(たとえば、ビューのv5が存在しない場合、ビューのv4をレンダリングします)。
バージョンが明示的に要求されていない場合、特定のバージョンにredirectする理由がわかりません。バージョンが明示的に要求されていない場合に提供されるデフォルトのバージョンを単に定義したいようです。また、URL構造からバージョンを除外することは、バージョニングをサポートするよりクリーンな方法であるとDavid Bockに同意します。
恥知らずなプラグイン:Versionistはこれらのユースケース(およびその他)をサポートします。
これを今日実装し、 RailsCasts-REST API Versioning 。で '正しい方法'であると信じるものを見つけました。
追加 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バージョンを実装できます。
ライアン・ビッグの答えは私のために働いた。
リダイレクトを通じてクエリパラメータを保持したい場合は、次のようにできます。
match "*path", to: redirect{ |params, request| "/api/v2/#{params[:path]}?#{request.query_string}" }