# method in block
namespace
# verb options
to: <String || Function> # controller#action, redirect
controllers: <String>
as: <String> # prefix
path: <String> # uri
on: <Symbol> # :collection | :member
path_names: <Hash>
# special verb
resources <:table_names>, <:table_names>
resource <:table_name>, <:table_name>
root: <String> # controller#action
# resource options
shallow: <Boolean>
param: <Symbol>
only: [:action, :action]
except: [:action, :action]
routes 的檔案位置在 config/routes.rb
。
顯示路由
透過網址:http://localhost:3000/rails/info/routes
,或在終端機輸入:
bin/rails routes # 列出所有路由
bin/rails routes -g admin # 搜尋特定路由 (grep)
bin/rails routes -c admin/users # 搜尋路由中特定 controller
目錄
[TOC]
路由設定
基本設定
<verb> <resources>, <options>
Rails.application.routes.draw do
# <verb> <resources> <to: controller#action, as: prefix, path: uri>
get '/posts', to: 'posts#index' # 當網址傳到 /posts 時,使用 PostsController 裡面的 index action
get '/profile', to: :show, controller: 'users'
get '/posts/:id/edit', to: 'pages#edit', as: 'edit_page' # :id 到 C 後會變成 params[:id],把這個路徑設定成 Prefix
get '/members', to: 'posts#edit', path: '/admin/edit_members/members_is' # 透過 path 可以改變 URI,一般是搭配 resources 使用
end
如此我們會得到以下路由:
# 若輸入 /admin/edit_members/members_is 會被帶到 posts#edit ,而原本的 /members 則沒有作用了。
Prefix Verb URI Pattern Controller#Action
posts GET /posts(.:format) posts#index
edit_page GET /posts/:id/edit(.:format) pages#edit
members GET /admin/edit_members/members_is(.:format) posts#edit
resources
url_path
(String)
options
to
: 此路由要對應到哪個controller#action
或redirect
(Function)as
: 改變 rails helper 中使用的 prefix,例如foo_path
,foo_url
path
: 瀏覽器上顯示的 url(顯式出來的網址)
# redirect 使用
get '/stories', to: redirect('/articles') # 直接代入 url
get '/stories/:name', to: redirect('/articles/%{name}') # url 中可代入參數
get '/stories/:name', to: redirect('/articles/%{name}', status: 302) # 可以代入 status code
params
在路由中使用 /:params_name/
時,可以在 controller 的 params
取得 hash { id: '17' }
。
get "/posts/:id/edit", to: "pages#edit", as: "edit_page" # :id 到 Controller 後會變成 params[:id],把這個路徑設定成 Prefix
搭配 path 快速變更路徑
# 搭配 path 快速更改路徑,所有原本 /products/ 的 URI 都會變成 /admin/products/。
Rails.application.routes.draw do
resources :products, path: "/admin/products"
end
重覆設定相同路由時
如果我們不小心設定了兩條相同的 routes 時,會以前者為主,後者會被覆蓋:
# 設定了兩條相同的 routes 會以前者為主,後者被覆蓋
Rails.application.routes.draw do
get "/about", to: "pages#about"
get "/about", to: "products#about"
end
使用 resource 快速設定
在 Rails 中我們可以使用 resources
或 resource
搭配 only:
或 except:
自動幫我們產生一系列的路由。
💡PATCH 和 PUT 的差別:在 Restful API 中,
PATCH
和PUT
的差別在於,PATCH
目的是用來修改某一 record 中的部分欄位,沒有修改到的欄位值會保留下來;而PUT
則是用來修改某一 record 中的所有欄位,因此沒有修改的欄位值則會全部被改為null
。
複數的 resources
resources :[table_names], :[table_names]
使用複數的 resources
會自動產生 [:index, :show, :new, :create, :edit, :update, :destroy]
:
# 複數的 resources [:index, :show, :new, :create, :edit, :update, :destroy]
# 通常用在管理員可以檢視、新增、修改、刪除會員資料
Rails.application.routes.draw do
resources :users # 透過 resources 快速產生一系列的 CRUD routes 設定,詳細內容見下方
resources :products, only: [:index, :show] # 只需要顯示資料
resources :products, except: [:new, :create, :edit, :update, :destroy] # 不需要新增、修改、刪除
end
會自動產生如下的 routes:
一次定義多個 resources:
resources :photos, :books, :videos
# 等同於
resources :photos
resources :books
resources :videos
單數的 resource
resource :[table_name], :[table_name]
有些時候我們不需要參照到參數 :id
,這時候,可以使用單數的 resource
,通常會搭配單數的 table_name
使用
# 單數的 resource [:show, :new, :create, :edit, :update, :destroy]
# 通常用在使用者可以檢視、新增、修改、刪除單筆資訊
Rails.application.routes.draw do
resource :profile
end
產生的路由配置如下:
槽狀式路由
Routing: Nested Resources @ RailsGuides
單純槽狀式
當某一個 resources 是另一個 resources 的孩子時,例如:
# app/controllers/users_controller.rb
class User < ApplicationRecord
has_many :posts
end
# app/controllers/posts_controller.rb
class Post < ApplicationRecord
belongs_to :uses
end
那麼在路由可以設定成在 resources
中在包一層 resources
:
# 槽狀式,建立 /users/posts/ 並包含所有功能
Rails.application.routes.draw do
resources :users do # 在 users 網址後又加入 posts,詳細內容見下方
resources :posts
end
end
顯示的 routes 如 /users/:user_id/posts/:id
,我們多了 users_posts_path
或 users_posts_url
這類路由可用,其中可以帶入參數會是 :user_id
,例如 users_posts_path(@user)
:
❗ Resources should never be nested more than 1 level deep.
限定式槽狀
在 resources
中在包一層 resources
,並在外層多一個 resources
,但透過 only:
限定特定的方法:
# 限定槽狀式,使用者可以檢視和新增自己所發表的文章
Rails.application.routes.draw do
# 管理者可以檢視、新增、刪除 users 資料
resources :users do
# 使用者可以去檢視、新增 posts
resources :posts, only: [:index, :new, :create]
end
# 管理者可以顯示、編輯、刪除 posts
resources :posts, only: [:show, :edit, :update, :destroy]
end
使用 shallow
Routing: shallow-nesting @ RailsGuides
或者我們可以使用 shallow: true
會產生和上面(限定式槽狀)一樣的結果:
# 使用 shallow: true 可以產生一樣的結果
Rails.application.routes.draw do
resources :users do
resources :posts, shallow: true
end
end
產生的路由如 /users/:user_id/posts
, /users/:id
, /posts/:id
collection 和 member 的使用
如果覺得內建的 Resources 產生的 CRUD 不夠用,我們也可以使用 collection
或 member
的方法。
- 使用 collection 方法做出來的路徑不會帶有參數。
- 使 用 member 方式產生的路徑,會帶有
:id
在裡面,這個 :id 會傳到 Controller 裡變成 params 這個變數的一部份。
Routing: Adding Member Routes @ RailsGuides
使用 collection:不需要參數時
假設希望可以檢視所有已經刪除的訂單,與其在網址的最後方使用 query(?)
來篩選:
# 在後面加參數的方式
GET /orders?type=cancelled
比較好的作法是給它一個獨立的路徑:
# 給它一個獨立的路徑
GET /orders/cancelled
雖然第一種做法也可以,但這樣表示必須在原來的 Action 裡多判斷、處理傳進來的 type=cancelled 參數;第二種做法則是另外開一個 Action 專門做這件事。
你可以使用 collection
方法來做出這個效果:
# 目標在 /orders 後多一個 cancelled,產生 /orders/cancelled
# 寫法一:使用 collection + block
Rails.application.routes.draw do
resources :orders do
collection do
get :cancelled
end
end
end
# 寫法二:使用 on: :collection
Rails.application.routes.draw do
resources :orders do
get :cancelled, on: :collection
end
end
產生結果如下:
使用 member:需要代入參數時
如果我想要「確認第 2 號訂單 」或是「取消第 3 號訂單」,網址可能需要這樣設計:
# 確認 2 號訂單
POST /orders/2/confirm
# 取消 3 號訂單
DELETE /orders/3/cancel
這時可以使用 member
來設定:
# 寫法一:使用 member + block
Rails.application.routes.draw do
resources :orders do
member do
post :confirm
delete :cancel
end
end
end
# 寫法二:使用 on: :member
Rails.application.routes.draw do
resources :orders do
post :confirm, on: :member
delete :cancel, on: :member
end
end
產生結果如下:
namespaces 和 scope 的使用
Routing: Controller Namespace and Routing @ RailsGuides
使用 namespace:檔案全部放在某一子資料夾中
雖然可以用 "resources :products, path: "admin/products/"
來改變 URI 路徑,但做後台的時候,會更常使用 namespace
方法來把 resources 包起來,隔出後台專屬的路徑:
Rails.application.routes.draw do
namespace :admin do
resources :products
resources :articles
end
end
如此,原來的路由在前面通通加上了 admin,同時對應的 Controller 也變得不一樣了。會產生下列 routes 設定:
搭配 path 使用
為了讓後台網址不要太好猜,通常建議不要直接用 admin 或 backend 這樣的後台網址,透過 path
,我們可以修改網址路徑,但依然可以正確對應到 action:
Rails.application.routes.draw do
namespace :admin, path: "cs19oq1ej30" do
resources :products
end
end
會得到如下的 routes 設定:
使用 scope:檔案不一定存在子資料夾中
namespace 和 scope 類似,但有更大的彈性。
透過 namespace 如同上面所述,會隔出一個特定 namespace 的路由,並且把 controllers 和對應的 view 放到名為 namespace 的這個資料夾內。
單純使用 scope
當我們單純使用 scope
時,它會改變全部的 path,但不會改變對應到的 controller(controller 不用放在 Admin 資料夾內):
# config/routes.rb
scope :admin do
resources :users
end
Prefix Verb URI Pattern Controller#Action
users GET /admin/users(.:format) users#index
POST /admin/users(.:format) users#create
user GET /admin/users/:id(.:format) users#show
PATCH /admin/users/:id(.:format) users#update
PUT /admin/users/:id(.:format) users#update
DELETE /admin/users/:id(.:format) users#destroy
我們可搭配其他的選項。
module
module
讓我們可以 controller 是否要被放在特定的 module(資料夾)下:
# ./config/routes.rb
# 對應到 Admin::UsersController
scope module: 'admin' do
resources :users
end
# 等同於
resources :users, module: 'admin'
Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) admin/users#index
POST /users(.:format) admin/users#create
user GET /users/:id(.:format) admin/users#show
PATCH /users/:id(.:format) admin/users#update
PUT /users/:id(.:format) admin/users#update
DELETE /users/:id(.:format) admin/users#destroy
path
透過 path
可以讓我們設定 URI 在 resource name 的前綴:
# ./config/routes.rb
scope module: 'admin', path: 'fu' do
resources :users
end
Prefix Verb URI Pattern Controller#Action
users GET /fu/users(.:format) admin/users#index
POST /fu/users(.:format) admin/users#create
user GET /fu/users/:id(.:format) admin/users#show
PATCH /fu/users/:id(.:format) admin/users#update
PUT /fu/users/:id(.:format) admin/users#update
DELETE /fu/users/:id(.:format) admin/users#destroy
as
使用 as
可以改變在 helper 中使用的名稱:
# ./config/routes.rb
scope module: 'admin', path: 'fu', as: 'cool' do
resources :users
end
Prefix Verb URI Pattern Controller#Action
cool_users GET /fu/users(.:format) admin/users#index
POST /fu/users(.:format) admin/users#create
cool_user GET /fu/users/:id(.:format) admin/users#show
PATCH /fu/users/:id(.:format) admin/users#update
PUT /fu/users/:id(.:format) admin/users#update
DELETE /fu/users/:id(.:format) admin/users#destroy
Rails 5 Routes: Scope vs Namespace @ DevBlast
使用路由(link_to)
將參數代入路由中
# routes
/users/:id/edit
/users/:id
在 controller 中定義變數 @user
:
# ./app/controllers/
@user = User.find(3)
可以直接在 View 透過 link_to
的 path 中代入參數:
# ./app/views/
<%= link_to 'Edit', edit_user_path(@user) %> # /users/3/edit
<%= link_to 'Show', @user %> # 如果是直接連到 user_path,可以省略
最後會得到 /patients/17
的連結。
使用 prefix 搭配 _path 和 _url
我們可以在路由中設定 prefix
,其中的 Prefix
可以幫助我們使用 helper method,在 Prefix
後面接上 _path
或 _url
後可以變成「產生相對應的路徑或網址」的 View Helper:
_path
:產生站內相對路徑的連結
# 如果是站內連結,通常會使用 _path 寫法來產生站內的路徑,例如:
products + path = products_path => /products
new_product + path = new_product_path => /products/new
edit_product + path = edit_product_path(2) => /products/2/edit
_url
:產生完整路徑的連結,包括主機網域名稱:
# 如果是使用 _url 則會產生完整的路徑,包括主機網域名稱:
products + url = products_url => http://kaochenlong.com/products
new_product + url = new_product_url => http://kaochenlong.com/products/new
edit_product + url = edit_product_url(2) => http://kaochenlong.com/products/2/edit
在 Controller 中使用 Routes 的 Helper
# Rails.application.routes.url_helpers
Rails.application.routes.url_helpers.posts_path
Rails.application.routes.url_helpers.posts_url(:host => "example.com")
其他設定
首頁網址設定(root)
使用 root
關鍵字 :
get "/", to: "welcome#index" # 一般寫法
root "welcome#index" # Rails 提供更簡便的寫法
轉址設定(redirect)
使用 redirect
關鍵字:
get '/users', to: redirect('/accounts') # 站內轉址:把連到 /users 轉到 /accounts
get '/5xruby', to: redirect('https://5xruby.tw') # 站外轉址:把連到 /5xruby 轉到 https://5xruby.tw
get 'tasks/:task_id/videos', to: redirect('/apps/%{app_id}/collects?task_id=%{task_id}')
public 不受 routes 控制
並不是所有的 HTTP Request 都會經過 Route,如果是放在專案的 public 目錄裡的檔案是不用經過 Route 的。例如在裡面放了一個檔名為 hello.html 的檔案,這樣一來只要使用者輸入網址 http://localhost:3000/hello.html
,即使 Route 裡沒有這條路徑,還是可以直接讀取到該頁面的內容。
如果你有翻一下 public 目錄的話,就會發現找不到頁面的 404.html 頁面跟伺服器錯誤的 500.html 頁面都在這裡面喔。
參考資料
- Routes @ 為你自己學 Ruby on Rails
- Rails Routing from the Outside In @ RailsGuides