web-dev-qa-db-ja.com

Rails RoutesのAPIバージョン管理

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
140
maletor

この回答の元の形式は大きく異なり、ここにあります 。猫の皮を剥ぐ方法が複数あることを証明してください。

名前空間を使用し、デフォルトの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>への要求であるかどうかをどのように判断するのですか?

とにかく、これは研究するのが楽しかったし、それがあなたに役立つことを願っています!

275
Ryan Bigg

追加することがいくつかあります:

リダイレクト一致は特定のルートでは機能しません-*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。

また、APIのv3をリリースすると、ルートは次のように更新されます。

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
37
pixeltrix

可能な場合は、バージョンがURLに含まれないようにURLを再考することをお勧めしますが、Acceptsヘッダーに配置されます。このスタックオーバーフローの答えはうまくいきます:

APIバージョン管理のベストプラクティス?

このリンクは、Railsルーティングでこれを行う方法を正確に示しています。

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

13
David Bock

私はルートによるバージョン管理の大ファンではありません。 VersionCake を構築して、より簡単な形式のAPIバージョン管理をサポートしました。

それぞれのビュー(jbuilder、RABLなど)の各ファイル名にAPIバージョン番号を含めることで、バージョン管理を控えめに保ち、後方互換性をサポートするために簡単に劣化できるようにします(たとえば、ビューのv5が存在しない場合、ビューのv4をレンダリングします)。

9
aantix

バージョンが明示的に要求されていない場合、特定のバージョンにredirectする理由がわかりません。バージョンが明示的に要求されていない場合に提供されるデフォルトのバージョンを単に定義したいようです。また、URL構造からバージョンを除外することは、バージョニングをサポートするよりクリーンな方法であるとDavid Bockに同意します。

恥知らずなプラグイン:Versionistはこれらのユースケース(およびその他)をサポートします。

https://github.com/bploetz/versionist

8
Brian Ploetz

これを今日実装し、 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バージョンを実装できます。

1
weteamsteve

ライアン・ビッグの答えは私のために働いた。

リダイレクトを通じてクエリパラメータを保持したい場合は、次のようにできます。

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