參考 RailsGuides中的Rails Routing from the Outside In
簡介
Rails 路由會通過你配置的路由規(guī)則將發(fā)送來的 URL 分發(fā)到對應(yīng)的 action 中。它同時會生成 paths 和 urls 來避免你在視圖中使用硬編碼。
Rails 的路由器是通過 HTTP 動詞和 url 地址來匹配路由的贺嫂。Rails 支持四種 HTTP 動詞悍缠,包括get,put姑荷,post盒延,delete。其中 post 和 delete 瀏覽器本身不支持鼠冕,是通過 js 來實現(xiàn)的添寺。Rails 路由器生成一個 params 的散列表其中的 :controller 和 :action 就決定了相應(yīng)的控制器和相應(yīng)的動作來處理該請求,其余的通常作為參數(shù)傳遞給該動作懈费,該動作使用计露。例如:
match ':controller/:action/:id/with_user/:user_id'
生成的 params 是:
{ :controller => “photos”, :action => “show”, :id => “1”, :user_id => “2” }
Rails 匹配地址時是在你指定的所有路由地址中從上倒下匹配的。如果存在如下代碼:
resources :pohots
get 'photos/poll'
當(dāng)傳來 'photos/poll' 時會先匹配到 resources 路由的 show 動作上憎乙,而不會匹配到 get 那個地址票罐。
其次,同時意味著你可以把 root 地址放在 rout 文件的開頭泞边,因為這是很可能匹配最多的地址该押。
資源路由
基本使用
Rails 資源路由有兩種,resources 和 resource阵谚。其中后者針對單件資源的路由蚕礼,前者更加常見。
其中使用方法如下:
resources :photos
resource :photo
分別生成七個路由地址和六個路由地址梢什。(很簡單奠蹬,不解釋)
對動作進(jìn)行限制
默認(rèn)地, Rails 會在應(yīng)用中為每一個 RESTful 路由建立7個動作嗡午。不過你可以用 :only 或 :except 選項來針對你的路由進(jìn)行調(diào)整:
resources :photos, :only => [:index, :show]
resources :photos, :except => :destroy
如果你的應(yīng)用程序有很多 RESTful 路由囤躁,使用 :only 和 :except 可以只生成有用的路徑,這能夠減少匹配所需時間和內(nèi)存翼馆。
添加額外的動作
成員路由
加入一個成員路由只需要在你的 resource 代碼塊中加入你一個 member 代碼塊就好:
resources :photos do
member do
get 'preview'
end
end
這樣將會把 /photos/1/preview 的 GET 動作識別出來割以,然后路由會匹配到 PhotosController 控制器下面的 preview 行為中。它同樣會建立兩個Helper:preview_photo_url 和 preview_photo_path应媚。
當(dāng)你使用了路由中的 Member 代碼塊時严沥,你可以任意指定一個HTTP動詞: get, patch/put, post, 或者 delete 。如果你并沒有太多的 Member 路由規(guī)則中姜,可以用 :on 作為后綴來把代碼塊替換掉:
resources :photos do
get 'preview', :on => :member
end
集合路由
加入集合路由和加入成員路由的格式完全一樣消玄,只不過是用 collection 的方法:
resources :photos do
collection do
get 'search'
end
end
上面這段代碼將會讓 Rails 用 GET 方法匹配路徑 /photos/search跟伏。并且這個 search 行為將會匹配到 PhotosController下,它將會創(chuàng)建 search_photos_url 和 search_photos_path 兩個路由Helper翩瓜。
同成員路由一樣受扳,你可以傳入一個 :on 選項。
resources :photos do
get 'search', :on => :collection
end
指定控制器
:controller 選項能夠讓你制定使用的控制器兔跌。例如:
resources :photos, :controller => "images"
它沒有改變匹配的 URL 和生成的 helper勘高,只是將控制器指定為 images。
指定格式限制
:constraints 選項可以讓你限定 id 的格式坟桅。例如:
resources :photos, :constraints => {:id => /[A-Z][A-Z][0-9]+/}
你可以用一個代碼塊來把單個正則限制運(yùn)用到多條規(guī)則上去华望。
constraints(:id => /[A-Z][A-Z][0-9]+/) do
resources :photos
resources :accounts
end
改變 Helper
:as 選項能讓你把默認(rèn)命名的路由 Helper 重命名成定制的字符串。例如:
resources :photos, :as => "images"
這樣就會生成像 new_image_path 這樣的 Helper仅乓。
改變匹配路徑動的動作
:path_name 選項能夠讓你把生成路徑中的 "new" 和 "edit" 覆蓋:
resources :photos, :path_names => { :new => 'make', :edit => 'change' }
這將會讓 Rails 能夠匹配這些請求:
/photos/make
/photos/1/change
這個選項并不會改變控制器中的行為名稱赖舟,這兩個路徑依然會被匹配到控制器中的 new 和 edit 路徑中去。
如果你想一次性地把一條設(shè)置應(yīng)用到多個規(guī)則上夸楣,你可以用 scope 宾抓。
scope :path_names => { :new => "make" } do
resources :photos
resources :accounts
end
改變匹配的路徑的資源名
:path 選項能夠讓你把生成路徑中的資源名稱覆蓋:
resources :categories, :path => "kategorien"
resources :posts, :path => "/admin/posts"
這樣路由就會匹配這樣的路徑:/kategorien/:id/edit,而不再是:/categories/:id/edit豫喧,但是并不會改變 Helper 石洗。
有時,你會把同類的控制器都放在同一個目錄下嘿棘,例如: app/controllers/admin 劲腿。這時甘凭,你可以這樣組織路由:
scope :path => "/admin" do
resources :posts, :comments
end
通常寫為:
scope "/admin" do
resources :posts, :comments
end
同樣弃鸦,你還可以用下面的控制器模塊和命名空間這兩種方法调限。
HTTP Verb Path action name helper
GET admin/photos posts#index photos_path
GET admin/photos/new posts#new new_photos_path
POST admin/photos posts#create photos_path
GET admin/photos/:id posts#show photo_path(:id)
GET admin/photos/:id/edit posts#edit edit_photo_path(:id)
PUT admin/photos/:id posts#update photo_path(:id)
DELETE admin/photos/:id posts#destroy photo_path(:id)
控制器模塊
:module 可以讓你的控制器匹配到一個特定的模塊下(可能包含有多個控制器)狞谱。
resources :posts, :module => "admin"
這樣把路由 /posts (不以 /admin 作前綴) 匹配到 Admin::PostsController载碌。
當(dāng)然也可以組織多個路由:
scope :module => "admin" do
resources :posts, :comments
end
其中 posts 的路由地址如下:
HTTP Verb Path action name helper
GET /photos admin/posts#index photos_path
GET /photos/new admin/posts#new new_photos_path
POST /photos admin/posts#create photos_path
GET /photos/:id admin/posts#show photo_path(:id)
GET /photos/:id/edit admin/posts#edit edit_photo_path(:id)
PUT /photos/:id admin/posts#update photo_path(:id)
DELETE /photos/:id admin/posts#destroy photo_path(:id)
控制器命名空間
namespace 將會自動為你添加 :as 僻弹, :module 和 :path 前綴莹痢。特別的用來處理下面提到的這種情況:
控制器命名空間可以幫助你通過一個命名空間管理多個控制器疲陕。例如房午,有多個 controllers 在同一個命名空間 Admin::namespace 下矿辽。 你或許是將這些 controllers 文件都放在了 app/controllers/admin 的目錄之下。這時郭厌,你可以這樣組織你的路由:
namespace :admin do
resources :photos, :comments
end
其中 photos 的路由地址如下:
HTTP Verb Path action name helper
GET /admin/photos admin/posts#index admin_photos_path
GET /admin/photos/new admin/posts#new new_admin_photos_path
POST /admin/photos admin/posts#create admin_photos_path
GET /admin/photos/:id admin/posts#show admin_photo_path(:id)
GET /admin/photos/:id/edit admin/posts#edit edit_admin_photo_path(:id)
PUT /admin/photos/:id admin/posts#update admin_photo_path(:id)
DELETE /admin/photos/:id admin/posts#destroy admin_photo_path(:id)
嵌套路由
通常袋倔,一個 Resource 常常有數(shù)個邏輯上的子 Resource 。例如折柠,你的應(yīng)用有這樣一個模型:
class Magazine < ActiveRecord::Base
has_many :ads
end
class Ad < ActiveRecord::Base
belongs_to :magazine
end
嵌套路由允許你這樣嵌套路由:
resources :magazines do
resources :ads
end
在這里宾娜,對于每一個 magazines 的路徑下面都需要能夠有 ad 作為 Resource 匹配到AdsController。 這時候每一組 ad 都需要指定一個 magzine 作為前綴扇售。
HTTP Verb Path action name helper
GET /magazines/:magazine_id/ads index magazine_ads_path
GET /magazines/:magazine_id/ads/new new new_magazine_ads_path
POST /magazines/:magazine_id/ads create magazine_ads_path
GET /magazines/:magazine_id/ads/:id show magazine_ad_path(:id)
GET /magazines/:magazine_id/ads/:id/edit edit edit_magazine_ad_path(:id)
PUT /magazines/:magazine_id/ads/:id update magazine_ad_path(:id)
DELETE /magazines/:magazine_id/ads/:id destory magazine_ad_path(:id)
嵌套路由允許你進(jìn)行多層的嵌套前塔,但實際上你的嵌套永遠(yuǎn)不要超過一層嚣艇。
對象和路徑URL
在 Rails 中你可以把對象的實例傳入作參數(shù)來作為ID。例如:
<%= link_to "Ad details", magazine_ad_path(@magazine, @ad) %>
你也可以用 url_for 帶上一組對象华弓, Rails 會自動生成正確的地址食零。
<%= link_to "Ad details", url_for([@magazine, @ad]) %>
在這里,Rails 將會把 @magazine 對應(yīng)到 Magazine寂屏, @ad 對應(yīng)到 Ad 上去贰谣,然后決定使用 magazine_ad_path 這個 Helper。而如果你用的是 link_to 這樣的Helper迁霎,你可以和調(diào)用 url_for 一樣用對象作參數(shù):
<%= link_to "Ad details", [@magazine, @ad] %>
如果你并沒有用嵌套式的路由冈爹,只要這樣就可以訪問一個對象的 show 方法:
<%= link_to "Magazine details", @magazine %>
如果你需要指定動作的對象的話,你也可以這樣:
<%= link_to "Edit Ad", [:edit, @magazine, @ad] %>
非資源路由
雖然 Resourceful 的路由規(guī)則功能強(qiáng)大欧引,但是在很多時候使用一個簡單路由規(guī)則更為合適。如果你覺得簡單路由規(guī)則更合適恳谎,你并沒有必要把應(yīng)用中的每個規(guī)則都往 Resourceful 框架上套芝此。
默認(rèn)路由
match 為 Rails 的默認(rèn)路由,你需要提供一組符號字面量來讓 Rails 匹配收到的 HTTP 請求因痛,其中有兩個符號量尤其的特別::controller 位置會匹配到你應(yīng)用中名字對應(yīng)的控制器婚苹,:action 有匹配到你應(yīng)用中對應(yīng)的控制器中的行為。例如:
match ':controller(/:action(/:id))'
如果收到的請求是 /photos/show/1 (前提是它還沒有被其他的前面的路由規(guī)則匹配)鸵膏,那么按照這個規(guī)則 Rails 將會調(diào)用 PhotosController 控制器中的 show 行為膊升,然后把后面的參數(shù) "1" 放到 params[:id] 中以供使用。因為 :action 和 :id 被圓括號包圍谭企,所以廓译,他倆可以被忽略,所以這條規(guī)則同樣能夠?qū)?/photos 匹配到 PhotosController#index 债查。
動態(tài)部分
你可以設(shè)置一條路由規(guī)則有任意多的動態(tài)部分非区,除了:controller 或 :action ,其他的片段都將會被轉(zhuǎn)換成 params 的一部分盹廷。如果你寫了這樣的一條路由:
match ':controller/:action/:id/:user_id'
如果收到了 /photos/show/1/2 這樣的請求征绸,將會分發(fā)到 PhotosController 下的 show 行為中,而 params[:id] 會是 "1", params[:user_id] 是 "2".
靜態(tài)部分
你可以在創(chuàng)建路由規(guī)則的時候指定一個靜態(tài)的部分:
match ':controller/:action/:id/with_user/:user_id'
這個路由將會生成像這樣的路徑 /photos/show/1/with_user/2俄占。在這個例子里管怠, params 是 { :controller => “photos”, :action => “show”, :id => “1”, :user_id => “2” }.
攜帶附加參數(shù)
你也可以在一個 urls 中攜帶任何的參數(shù),例如:
match ':controller/:action/:id'
如果接收到的路徑為 /photos/show/1?user_id=2 缸榄,那么 Rails 會把它分發(fā)到 Photos 控制器下面的 show 行為中去渤弛,params 會是 { :controller => “photos”, :action => “show”, :id => “1”, :user_id => “2” }.
設(shè)置默認(rèn)值
如果你需要一個默認(rèn)的匹配值,你可以用一個沒有指定 :controller 和 :action 的路由規(guī)則來實現(xiàn)碰凶。像這樣:
match 'photos/:id' => 'photos#show'
Rails 將會因為這條規(guī)則而把接收到的路徑 /photos/12 暮芭,匹配到 PhotosController 下面的 show 里面去鹿驼。
你還可以通過提供一個 :defaults 的散列來指定一個你默認(rèn)存在的動態(tài)片段。例如:
match 'photos/:id' => 'photos#show', :defaults => { :format => 'jpg' }
這樣 photos/12 的路徑就會匹配到 PhotosController 的 show 行為辕宏, params[:format] 會被設(shè)置為 "jpg"畜晰。
命名路由
你可以用 :as 參數(shù)來定義一個路由的名字。
match 'exit' => 'sessions#destroy', :as => :logout
這樣在應(yīng)用程序中產(chǎn)生的 Helper 就將會是 create logout_path 和 logout_url瑞筐。而調(diào)用了 logout_path 就會返回 /exit 路徑凄鼻。
HTTP 動詞約定
你可以用 :via 選項來限定一個請求能相應(yīng)的一個或者多個 HTTP 方法:
match 'photos/show' => 'photos#show', :via => :get
簡寫作:
get 'photos/show'
你也可以將多個動詞行為綁定到一條路由規(guī)則上去:
match 'photos/show' => 'photos#show', :via => [:get, :post]
部分正則約定
你可以使用 :constraints 選項來對動態(tài)路徑部分強(qiáng)制性的匹配:
match 'photos/:id' => 'photos#show', :constraints => { :id => /[A-Z]\d{5}/ }
這個路由將會匹配像 /photos/A12345 這樣的路由。你可以用如此更簡潔的方式來寫出這個規(guī)則:
match 'photos/:id' => 'photos#show', :id => /[A-Z]\d{5}/
在 :constraints 選項中使用正則表達(dá)式有一個限制聚假,就是在正則匹配中無法使用錨元素來匹配块蚌,像這樣的路由規(guī)則是無效的:
match '/:id' => 'posts#show', :constraints => {:id => /^\d/}
總之,確定你沒有在路由正則中使用錨元素膘格。因為每個路由規(guī)則在匹配的時候就已經(jīng)用到了錨元素峭范。
例如,下面的路由 posts 和 users 控制器共享了一個命名空間瘪贱,他們根據(jù) to_param 值的首字符是否是數(shù)字來區(qū)分纱控,像 1-hello-world 這樣的會匹配到 posts 下面去,而像 david 就會匹配到 users 下面去菜秦。
match '/:id' => 'posts#show', :constraints => { :id => /\d.+/ }
match '/:username' => 'users#show'
請求限制 && 高級限定
這里不是狠懂甜害,設(shè)計到了 Request 對象,暫時留下以后解決球昨。
通配路由匹配
通配路由規(guī)則能夠指定一個參數(shù)讓任意部分匹配尔店。例如:
match 'photos/*other' => 'photos#unknown'
這樣這個路由將會匹配 photos/12 或 /photos/long/path/to/12,并且把 params[:other] 設(shè)置成 "12" 或 "long/path/to/12".
通配字符串可以放在路由規(guī)則的任意部分,例如:
match 'books/*section/:title' => 'books#show'
這樣將會把像 books/some/section/last-words-a-memoir 這樣的字符串匹配后并將 params[:section] 設(shè)置為 "some/section", params[:title] 設(shè)置為 "last-words-a-memoir"主慰。
從技術(shù)上來說你是可以將超過一個統(tǒng)配的字符串加在你的路由規(guī)則中的嚣州,但是請記住,通配符總是會用貪婪地(盡可能多地匹配)將字符串匹配到路徑上河哑。例如:
match '*a/foo/*b' => 'test#index'
將會把 zoo/woo/foo/bar/baz 的參數(shù) params[:a] 設(shè)置成 "zoo/woo", 然后參數(shù) params[:b] 設(shè)置成 "bar/baz"避诽。
從 Rails 3.1 開始,通配路由總是會自動地將格式字符串默認(rèn)匹配璃谨,例如下面這個路由:
match '*pages' => 'pages#show'
如果你發(fā)起了'/foo/bar.json'這樣的一個請求沙庐,你的參數(shù) params[:pages] 將會是 "foo/bar" ,而你的請求格式會識別為 JSON 佳吞。 如果你想要在舊的 3.0.x 中有同樣的特性拱雏,你需要提供一個散列參數(shù):format => false,像這樣:
match '*pages' => 'pages#show', :format => false
如果你想要強(qiáng)制地被指定一種格式,(其無法被忽略)底扳,你可以像這樣加上 :format => true 參數(shù):
match '*pages' => 'pages#show', :format => true
重定向
你可以在路由規(guī)則中通過 redirect 指定一個路徑重定向到另一個路由中的路徑:
match "/stories" => redirect("/posts")
你同樣可以把一條 match 命令中的動態(tài)部分的錯誤處理(rescue)進(jìn)行重定向:
match "/stories/:name" => redirect("/posts/%{name}")
你同樣可以為你的重定向函數(shù)加入一個代碼塊铸抑,代碼塊接收的變量是 params 和 請求對象(后者是可選的):
match "/stories/:name" => redirect {|params| "/posts/#{params[:name].pluralize}" }
match "/stories" => redirect {|p, req| "/posts/#{req.subdomain}" }
請注意,這里的重定向都將是 301 標(biāo)志 “永久重定向”衷模。在某些瀏覽器或者代理服務(wù)器中這個標(biāo)識碼將會使舊的頁面失效鹊汛。
路由定向到 Rack Applications
你可以將 urls 的地址交給任何一個 Rack application 來處理蒲赂。只需要在 match 后面指定相應(yīng)的 Rack application 來取代一個類似與 "posts#index" 的字符串。
match "/application.js" => Sprockets
這樣 Sprockets 將會處理這個請求并且返回 [status, headers, body], 但是這對于路由器來說是完全透明的刁憋,它不能分辨兩者滥嘴。
值得一提的是,"post#index" 通常會自動的擴(kuò)展為 PostsController.action(:index)至耻,而它會返回一個可以有效的 Rack application若皱。
跟目錄
你可以用 root 方法來指定 Rails 如何匹配 "/" 路由:
root :to => 'pages#main'
簡寫為:
root 'pages#main'
你可以將 root 規(guī)則放在文件的最上方,這樣這條規(guī)則能第一個進(jìn)行匹配尘颓,因為這恐怕將會是最常用的的規(guī)則了走触。你同樣需要刪除文件 public/index.html 來讓這個規(guī)則發(fā)揮效用。
對路由進(jìn)行檢查和測試
Rails 提供了檢查和測試路由的工具疤苹。
通過 rake 查看存在的路由規(guī)則
如果你想要一整套應(yīng)用的完整可用的路由規(guī)則列表互广,運(yùn)行 rake routes 命令就好。
路由測試
你的測試中應(yīng)該加入路由測試(就和你應(yīng)用程序的其他部分一樣)卧土。Rails 提供三個 內(nèi)建的斷言 被設(shè)計來幫助你進(jìn)行路由的測試兜辞。
- assert_generates
- assert_recognizes
- assert_routing
assert_generates 能夠?qū)κ欠裆芍付ǖ穆窂竭M(jìn)行斷言:
assert_generates "/photos/1", { :controller => "photos", :action => "show", :id => "1" }
assert_generates "/about", :controller => "pages", :action => "about"
你可以提供一個 :method 參數(shù)來指定使用的 HTTP 行為:
assert_recognizes 是 assert_generates 的反向斷言. 它能夠通過指定的位置判斷是否與存在的路由規(guī)則匹配。
assert_recognizes({ :controller => "photos", :action => "show", :id => "1" }, "/photos/1")
你可以提供一個 :method 參數(shù)來指定使用的 HTTP 行為:
assert_recognizes({ :controller => "photos", :action => "create" }, { :path => "photos", :method => :post })
assert_routing 對一個路由雙向地進(jìn)行測試: 它測試的一條路徑是否與選項匹配夸溶,之后測試選項是否生成路徑。因此凶硅,這條斷言結(jié)合了前兩個測試方法 assert_generates 和 assert_recognizes.
assert_routing({ :path => "photos", :method => :post }, { :controller => "photos", :action => "create" })
其他
- 默認(rèn)情況下 :id 參數(shù)不接收 點 ———— 這是因為 點已經(jīng)被用作了格式分割符缝裁。如果你需要在 :id 中的正則中匹配出 點,你需要這樣復(fù)寫正則表達(dá)式足绅,例如::id => /[^/]+/ 捷绑,這樣會允許除了斜杠之外的任何字符。