Rails のルーティング
ルーティング解析
- config/routes.rbの上から
ルーティングファイルの分割
- ルーティングファイルの分割機能は、本当に必要になるまでは使わない
- ルーティングが数百件にのぼる場合であっても)、ルーティングファイルを1つにまとめておく方が開発者にとって扱いやすい
- 基本的にnamespaceやscopeでルーティングを体系的に分割する
# config/routes.rb
Rails.application.routes.draw do
get "foo", to: "foo#bar"
draw(:admin) # `config/routes/admin.rb`にある別のルーティングファイルを読み込む
draw(:api)
end—
確認
-
ローカル
http://localhost:3000/rails/info/routes
bin/rails routes
bin/rails routes --expanded
bin/rails routes -g admin
bin/rails routes --unused- コンソール
irb> Rails.application.routes.url_helpers.users_path
=> "/users"
irb> user = User.first
=> #<User:0x00007fc1eab81628
irb> app.edit_user_path(user)
=> "/users/1/edit"ルートパスを指定する
root "pages#main"リソースフルルーティング
resources :photos| HTTP verb | パス | コントローラ#アクション | 目的 |
|---|---|---|---|
| GET | /photos | photos#index | すべての写真の一覧を表示 |
| GET | /photos/new | photos#new | 写真を1つ作成するためのHTMLフォームを返す |
| POST | /photos | photos#create | 写真を1つ作成する |
| GET | /photos/:id | photos#show | 特定の写真を表示する |
| GET | /photos/:id/edit | photos#edit | 写真編集用のHTMLフォームを1つ返す |
| PATCH/PUT | /photos/:id | photos#update | 特定の写真を更新する |
| DELETE | /photos/:id | photos#destroy | 特定の写真を削除する |
複数
resources :photos, :books, :videos単数
- indexアクションは不要なため作られない
- resolveは、Geocoderのインスタンスをレコード識別を介して単数形ルーティングに変換するために必要
resource :geocoder
resolve("Geocoder") { [:geocoder] }パスとコントローラーとルーティングヘルパーの構成
namespaceを分ける
- namespaceスコープを使うと、:moduleや:pathプレフィックスに加えて:asも自動的に追加される
namespace :admin do
resources :articles
endパスにはモジュール名を付けたくないが、コントローラーはディレクトリを分ける
scope module: "admin" do
resources :articles
end
OR
resources :articles, module: "admin"
OR
resources :user_permissions, controller: "admin/user_permissions"パスにはモジュール名を付けたいが、コントローラーはディレクトリを分けたくない場合
- namespaceスコープを使うと、:moduleや:pathプレフィックスに加えて:asも自動的に追加される
scope "/admin" do
resources :articles
end
OR
scope "admin", as: "admin" do
resources :photos
endパスを浅くする
- リソースのネスティングの深さは、1階層にとどめておくべき
- member -> 個別のリソース
- collection -> 複数のリソース
shallow do
resources :articles do
resources :comments
resources :quotes
end
end
OR
resources :articles, shallow: true do
resources :comments
resources :quotes
end
OR
resources :articles do
resources :comments, shallow: true
resources :quotes, shallow: true
end共通化
- 共通化しない方が見やすい場合もある
共通化前
resources :messages do
resources :comments
end
resources :articles do
resources :comments
resources :images, only: :index
end共通化後
concern :commentable do
resources :comments
end
concern :image_attachable do
resources :images, only: :index
end
resources :messages, concerns: :commentable
resources :articles, concerns: [:commentable, :image_attachable]scopeやnamespace内で使う場合
- concerns(複数形)を使う
namespace :messages do
concerns :commentable
end
namespace :articles do
concerns :commentable
concerns :image_attachable
endリンク生成
詳細ページ
- インスタンスのみを使う
- 内部的には@magazine.model_name.route_key, @magazine.to_param
<%= link_to 'Magazine details', @magazine %>
<%= link_to 'Ad details', [@magazine, @ad] %>アクションを指定する場合
<%= link_to 'Edit Ad', [:edit, @magazine, @ad] %>デフォルトの7つ以外のパスやアクションにする
- 文字列はパスとして推測されるが、シンボルはコントローラのアクションとして認識される
- 基本的にはリソースフルルーティングを使うべき
- リソースフルルーティングが適していない場合に、アプリケーションのあらゆる部分を無理にリソースフルなフレームワークに押し込める必要はない
- リソースフルでないルーティングが適しているユースケースの1つに、既存のレガシーURLを新しいRailsアクションに対応付ける場合
パスとアクション名を変えたい
- メンバーアクションを変える
resources :photos do
member do
get "preview"
end
end
OR
resources :photos do
get "preview", on: :member
end- コレクションアクションを変える
resources :photos do
collection do
get "search"
end
end
OR
resources :photos do
get "search", on: :collection
end- 特定のアクションを変える
- pathnamesで変えるとルーティングヘルパーやアクション名は変わらない
resources :photos do
get "search", on: :new
end同じアクションにする
- 一覧と詳細を同じアクションにする
get "photos(/:id)", to: "photos#display"- GETとPOSTは同じアクションにする
- GETアクションはCSRFトークンをチェックしないため、1つのアクションにGETリクエストとPOSTリクエストの両方をルーティングすると、セキュリティ上の問題が発生する
match "photos", to: "photos#show", via: [:get, :post]- すべてのHTTPメソッドを同じアクションにする
- GETアクションはCSRFトークンをチェックしないため、1つのアクションにGETリクエストとPOSTリクエストの両方をルーティングすると、セキュリティ上の問題が発生する
match "photos", to: "photos#show", via: :allパラメーター
- パスパラメーターとクエリパラメーターはどちらもコントローラーではparamsで取得できる
get "photos/:id/:user_id", to: "photos#show"デフォルトパラメーター
- params[:format]は常に”jpg”になる
- クエリパラメーターでformatを指定しても、セキュリティ上の理由でparams[:format]は”jpg”になる
- formatはすべてのパスに含まれる暗黙的なオプションのパラメータでもある
get "photos/:id", to: "photos#show", defaults: { format: "jpg" }制約
- :constraintsでは正規表現を使えるが、アンカー(
パラメーター
- 桁数の範囲を制限(bigintは19桁が最長、unsignedは最長で20桁)
get "photos/:id", to: "photos#show", constraints: { id: /\d[1,20]/ }サブドメイン
- https://admin.example.com/photos
- request.subdomainは文字列(URLに関連するものは文字列)のため、シンボルにはしない
namespace :admin do
constraints subdomain: "admin" do
resources :photos
end
endformat
- jsonのみ
constraints format: :json do
resources :photos
end
OR
resources :photos, constraints: { format: :json }
OR
get "foo", constraints: lambda { |req| req.format == :json }- formatを無視
constraints format: false do
resources :photos
resources :articles
resources :comments
end- formatを必須
constraints format: true do
resources :photos
endカスタム
class RestrictedListConstraint
def initialize
@ips = RestrictedList.retrieve_ips
end
def matches?(request)
@ips.include?(request.remote_ip)
end
end
Rails.application.routes.draw do
constraints(RestrictedListConstraint.new) do
get "*path", to: "restricted_list#index"
get "*other-path", to: "other_restricted_list#index"
end
endリダイレクト
- デフォルトは301(Moved Permanently)
get "/stories/:name", to: redirect("/articles/%{name}")
get "/stories", to: redirect { |path_params, req| "/articles/#{req.subdomain}" }
get "/stories/:name", to: redirect("/articles/%{name}", status: :found|:moved_permanently|:see_other|:temporary_redirect|:permanent_redirect)direct
外部URL
direct :homepage do
"http://www.rubyonrails.org"
endカスタムURLヘルパー
- ブロックの戻り値は、必ずurl_forメソッドで有効な1個の引数にしなければならない
- 有効な「文字列URL」「ハッシュ」「配列」「Active Modelインスタンス」「Active Modelクラス」のいずれか1つ
direct :commentable do |model|
[ model, anchor: model.dom_id ]
end
direct :main do
{ controller: "pages", action: "index", subdomain: "www" }
end