Rails ---> routes.rb 詳解

理解路由的目的javascript

看懂routes.rb文件中的代碼html

使用經典的hash風格或者如今比較流行的Restful風格構造你本身的路徑java

判定一個路徑會映射到哪個controller和actiongit

路由的雙重做用github

Rails的路由是一個雙重的機制 - 你既能把樹木變成紙張,也能把紙張變成樹木.更準確的說,它既能將進入服務器的HTTP請求鏈接到你的controller,也能幫助你(在View內)生成URL而不須要使用硬編碼的字符串.web

從URL到代碼

當你的Rails應用接收到HTTP請求後,好比:數據庫

GET /patients/17安全

Rails的路由引擎就是把請求分發到你的應用中合適點的那些代碼.具體到這個例子,應用程序比較可能會運行patients controller中的show action,並展現id是17的patient的詳細信息.服務器

從代碼到URL

路由也能夠反過來做用.若是你的應用包含了如下代碼session

@patient = Patient.find(17)

<%= link_to 「Patient Record」, patient_path(@patient) %>

那麼,路由引擎就會把這段代碼解釋成這樣的URL: http://example.com/patients/17.這樣使用路由,比起硬編碼URL,能夠下降你的應用程序的脆弱程度(增長程序健壯性),而且能夠增長代碼的可讀性,使你的程序更容易被理解.

Patient須要被聲明成爲resource纔可使用這種形式的路徑轉換

快速瀏覽Routes.rb

Rails的路由有兩個組陳部分:做爲Rails一部分的路由引擎,還有包含有你的應用程序真正路徑的config/routes.rb文件.講授能夠把什麼東西放到routes.rb裏面去是本文的目的,不過在此以前咱們先概覽一下.

處理文件

從格式上講,routes.rb只不過是被傳遞給ActionController::Routing:routes.draw的一個大大的 block. 這個文件裏也能夠有註釋,不過大多數狀況下,應該是移行移行的代碼,每一行表明你應用程序的一條路徑.路徑有五種主要類型:

RESTful路徑 命名路徑 嵌套路徑 常規路徑 默認路徑

每個類型的路徑都會在隨後詳細闡述. 當有請求進入時, routes.rb文件自頂向下出路.請求會被分發到第一個匹配的路徑.若是沒有匹配的路徑,Rails會返回給調用者一個HTTP 404.

RESTful路徑

RESTful路徑沾了Rails內建REST機制的光,只用一句單獨的聲明就包含了不少路由信息.一個RESTful路徑看起來是這樣的:

map.resources :books

命名路徑

命名路徑在處理請求的同時,也能夠給你的代碼提供很是具備可讀性的連接.這是一個典型的命名路徑:

map.login ‘/login’, :controller => ’sessions’, :action => ‘new’

嵌套路徑

嵌套路徑可讓你在一個resource裏面包含另外一個resource.你待會兒就會知道它是怎麼被轉化成URL(絕對路徑 http://niuwa.org/index.html)和path(相對路徑 /index.html)的.舉例來講,若是你的應用包含不少部分,每一個部分屬於一個包(assembly),你也許能夠這麼寫:

map.resources :assemblies do |assemblies| assemblies.resources :parts end

常規路徑

在不少應用中,你會看到非RESTful的路由,它們顯式的連接URL和action.例如:

map.connect ‘parts/:number’, :controller => ‘inventory’, :action => ’show’

默認路徑

默認路徑十一個安全網,抓住那些沒有匹配上其餘路徑的請求.不少Rails應用都會包含這兩條默認路徑:

map.connect ‘:controller/:action/:id’ map.connect ‘:controller/:action/:id.:format’

這兩個默認路徑是你建立Rails應用的時候自動生成的.你能夠爲你的應用裏面的全部東西使用RESTful的路徑,那你就有可能想去掉這兩條默認路徑.在你去掉它們以前請確保你沒有用到它們.

`
RESTful路由:Rails的默認方式

RESTful路由是目前Rails的標準路由方式,而且它也是你在新建的應用中首先使用的方式.要理解RESTful路有可能要花上一點時間,不 過這樣 的努力是值得的;你的代碼會變得更容易閱讀,而且當你使用這種路由方式的時候,你會跟Rails配合的很好,而不是跟Rails對着幹.

什麼是REST?

RESTful 路由的基礎是由Roy Fielding的博士論文提出的,參見: Architectural Styles and the Design of Network-based Software Architectures. 幸運的是,你不須要看懂整篇博士論文就能夠理解Rails中的REST是怎麼回事.REST是Representational State Transfer(基於表現的狀態轉移)的縮寫.對咱們來講,主要有兩點:

使用資源定位符(對咱們來講就是URL)來表示資源 在系統的不一樣組件之間轉移表現狀態

舉例來講,對於Rails應用中這樣的一個請求:

DELETE /phote/17

會被理解成是操做ID是17的photo,而且指示出了指望的操做 - 刪除該資源.REST是web應用架構的一種天然的方式,而且Rails進一步使用約定(convention)隱藏了RESTful的某些複雜性使之更加天然(大概是指用起來更簡單).

CRUD,動詞和action

在RAils中,一個RESTful路徑提供了HTTP動詞(也就是GET, PUT等等),controller actions和(隱含的)CRUD數據庫操做之間的的映射.路由文件中一個單獨的入口以下所示:

map.resources :photos

在你的應用中建立了七個不一樣的路徑:






GET /photos Photos index display a list of all photos
GET /photos/new Photos new return anHTMLform for creating a new photo
POST /photos Photos create create a new photo
GET /photos/1 Photos show display a specific photo
GET /photos/1/edit Photos edit return anHTMLform for editing a photo
PUT /photos/1 Photos update update a specific photo
DELETE /photos/1 Photos destroy delete a specific photo

對這些路徑(由以前的resource定義生成的),resource的id能夠在相應的controller action中使用params[:id]獲得.

若是你一直在你的應用程序裏面使用RESTful路徑,你應該刪除routes.rb中的默認路徑,這樣就會迫使Rails使用HTTP動詞和路徑間的映射.

URL和Path

建立RESTful路徑會在你的應用程序裏面生成一堆helper:

photos_url 和photos_path映射到index和create兩個action new_photo_url和new_photo_path映射到new action edit_photo_url和edit_photo_path映射到edit action photo_url和photo_path映射到show, update和destroy三個action

由於路由同時使用HTTP動詞和path兩者分發請求,所以這七個RESTful路由生成的路徑只有4對helper.

在這裏,以_url結尾的helper生成應用程序能理解的整個URL(包含主機名),而以_path結尾的helper僅生成從應用程序根目錄開始的path(不包含主機名).例如:

photos_url# => 「http://www.example.com/photos」

photos_path# => 「/photos」

同時定義多個資源

若是你須要爲多個資源建立路徑,你可使用一次map.resources調用就定義它們:

map.resources :photos, :books, :videos

跟這樣定義的效果是同樣的:

map.resources :photos map.resources :books map.resources :videos

單數形式資源

你也能夠應用RESTful路由定義單數形式的資源.這是,你須要使用map.resource代替剛纔的map.resources,這時生成的路徑會略有不一樣:

map.resource :geocoder

建立六個不一樣的路徑:






GET /geocoder/new Geocoders new return anHTMLform for creating the new geocoder
POST /geocoder Geocoders create create the new geocoder
GET /geocoder Geocoders show display the one and only geocoder resource
GET /geocoder/edit Geocoders edit return anHTMLform for editing the geocoder
PUT /geocoder Geocoders update update the one and only geocoder resource
DELETE /geocoder Geocoders destroy delete the geocoder resource

雖然routes.rb中的資源名稱是單數,可是對應的controller名稱依然是複數.

一個單數形式的RESTful路徑生成以下的helper:

new_geocoder_url 和new_geocoder_path映射到new action edit_geocoder_url和edit_geocoder_path映射到edit action geocoder_url和geocode_path映射到create, update和destroy三個action 自定義資源

雖然使用RESTful路由約定對不少應用來講基本就足夠了,不過仍是有一些其餘的方法自定義RESTful路徑的工做方式.這些選項包括:

:controller :singular :requirements :conditions :as :path_names :path_prefix :name_prefix :only :except

你也可使用:member和:collection選項添加路徑,這個在本文中稍後討論.

使用:controller

:controller選項使你可使用和公開的資源名稱不一樣的controller名稱,例如:

map.resources :photos, :controller => 「images」

生成helper時會依據資源的名稱, 而不是controller的名稱.所以,在這個例子裏,就會獲得photos_path,new_photo_path等等.

controller命名空間和路由


默認路由:

Ruby代碼
# Rails3:   match '/:controller(/:action(/:id))'   # Rails2:   map.connect ':controller/:action/:id'  
# Rails3: match '/:controller(/:action(/:id))' # Rails2: map.connect ':controller/:action/:id' 

正則路由:
Ruby代碼
# Rails3:   match 'products/:id':to => 'catalog#view'   # Rails2:   map.connect 'products/:id':controller => 'catalog':action => 'view'  
# Rails3: match 'products/:id', :to => 'catalog#view' # Rails2: map.connect 'products/:id', :controller => 'catalog', :action => 'view'

命名路由:
Ruby代碼
# Rails3:   match 'logout':to => 'sessions#destroy':as => 'logout'   # Rails2:   map.logout 'logout':controller => 'sessions':action => ''  
# Rails3: match 'logout', :to => 'sessions#destroy', :as => 'logout' # Rails2: map.logout 'logout', :controller => 'sessions', :action => ''

根路由:
Ruby代碼
# Rails3:   root => 'welcome#show'   # Rails2:   map.root :controller => 'welcome':action => 'show'  
# Rails3: root => 'welcome#show' # Rails2: map.root :controller => 'welcome', :action => 'show'

路由簡寫技巧:
:to 鍵的省略: 
Ruby代碼
match 'account' => 'account#index'   # 至關於:   match 'account':to => 'account#index'      match 'info' => 'projects#info':as => 'info'  
match 'account' => 'account#index' # 至關於: match 'account', :to => 'account#index' match 'info' => 'projects#info', :as => 'info'
注意: 
:as 在rails3中是改變 helper, 在rails2中是改變 path


當路徑和控制器(及action)一至時,可省略指派控制器部分 
Ruby代碼
match 'account/overview'   # 至關於:   match 'account/overview':to => 'account#overview'  
match 'account/overview' # 至關於: match 'account/overview', :to => 'account#overview' 

Verb路由
當須要限制http請求方法的時候經過鍵  :via ,也能夠直接把方法寫在最前面: 
Ruby代碼
get 'account/overview'   # 至關於:   match 'account/overview':via => 'get'      match 'account/setup':via => [:get:post]   # 支持get\post\put\delete四種HTTP方法  
get 'account/overview' # 至關於: match 'account/overview', :via => 'get' match 'account/setup', :via => [:get, :post] # 支持get\post\put\delete四種HTTP方法 

resources路由:
Ruby代碼
resources :posts:except => [:index]   resources :posts:only => [:new:create]      # edit_post GET    /posts/:id/modify(.:format) {:controller=>"posts", :action=>"edit"}   resources :posts:path_names => { :edit => 'modify' }       resources :projects do     resources :tasks:people   end      resources :products do     collection do       get  :sold       post :on_offer:search     end     get :buy:on => :member     post :batch:on => :collection   end      resource :session do     get :create   end  
resources :posts, :except => [:index] resources :posts, :only => [:new, :create] # edit_post GET /posts/:id/modify(.:format) {:controller=>"posts", :action=>"edit"} resources :posts, :path_names => { :edit => 'modify' } resources :projects do resources :tasks, :people end resources :products do collection do get :sold post :on_offer, :search end get :buy, :on => :member post :batch, :on => :collection end resource :session do get :create end 

:shallow用法:
Rails3中的shallow用法與Rails2中一致 
Ruby代碼
resources :blogs:shallow => true do     resources :comments   end  
resources :blogs, :shallow => true do resources :comments end 
使用:shallow先後相同部分:
blog_comments GET /blogs/:blog_id/comments(.:format) {:controller=>"comments", :action=>"index"}
blog_comments POST /blogs/:blog_id/comments(.:format) {:controller=>"comments", :action=>"create"}
new_blog_comment GET /blogs/:blog_id/comments/new(.:format) {:controller=>"comments", :action=>"new"}
blogs GET /blogs(.:format) {:controller=>"blogs", :action=>"index"}
blogs POST /blogs(.:format) {:controller=>"blogs", :action=>"create"}
new_blog GET /blogs/new(.:format) {:controller=>"blogs", :action=>"new"}
edit_blog GET /blogs/:id/edit(.:format) {:controller=>"blogs", :action=>"edit"}
blog GET /blogs/:id(.:format) {:controller=>"blogs", :action=>"show"}
blog PUT /blogs/:id(.:format) {:controller=>"blogs", :action=>"update"}
blog DELETE /blogs/:id(.:format) {:controller=>"blogs", :action=>"destroy"}


使用:shallow先後不一樣部分:
不使用shallow選項:
edit_blog_comment GET /blogs/:blog_id/comments/:id/edit(.:format) {:controller=>"comments", :action=>"edit"}
blog_comment GET /blogs/:blog_id/comments/:id(.:format) {:controller=>"comments", :action=>"show"}
blog_comment PUT /blogs/:blog_id/comments/:id(.:format) {:controller=>"comments", :action=>"update"}
blog_comment DELETE /blogs/:blog_id/comments/:id(.:format) {:controller=>"comments", :action=>"destroy"}


使用shallow選項後:
edit_comment GET /comments/:id/edit(.:format) {:controller=>"comments", :action=>"edit"}
comment GET /comments/:id(.:format) {:controller=>"comments", :action=>"show"}
comment PUT /comments/:id(.:format) {:controller=>"comments", :action=>"update"}
comment DELETE /comments/:id(.:format) {:controller=>"comments", :action=>"destroy"}

能夠看出使用shallow選項後,對於已經存在的資源使用簡化方式操做,具體行爲涉及到 edit\show\update\destroy 四種 
另外,shallow選項的有效範圍是對自身及嵌套的資源都有效,以下面這個例子: 
Ruby代碼
resources :publishers do     resources :magazines do       resources :albums:shallow => true do         resources :photos do           resources :images         end       end     end   end  
resources :publishers do resources :magazines do resources :albums, :shallow => true do resources :photos do resources :images end end end end 
這個例子中 albums、photos、images 都會使用簡化方式,而 magazines 不會。特別注意:這種嵌套方式極不推薦,通常嵌套的層級最好不要超過一級 

scope路由
:path 改變Path,:module 改變Controller, :name_prefix || :as 改變  helper
Ruby代碼

scope 'admin' do    resources :posts  end  # 行當於:  scope :path => 'admin' do    resources :posts  end  

scope 'admin' do resources :posts end # 行當於: scope :path => 'admin' do resources :posts end

Ruby代碼
scope :module => 'admin' do     resources :posts   end   # 至關於:   resources :posts:module => 'admin'  
scope :module => 'admin' do resources :posts end # 至關於: resources :posts, :module => 'admin' 

Ruby代碼
scope :name_prefix => 'admin' do     resources :posts   end   # 至關於:   resources :posts:name_prefix => 'admin'  
scope :name_prefix => 'admin' do resources :posts end # 至關於: resources :posts, :name_prefix => 'admin' 

Ruby代碼
scope 'admin':module => 'admin':name_prefix => 'admin' do     resources :posts   end   # 至關於:   namespace 'admin' do      resources :posts   end  
scope 'admin', :module => 'admin', :name_prefix => 'admin' do resources :posts end # 至關於: namespace 'admin' do resources :posts end 


在路由中定義跳轉:
Ruby代碼
match "/posts/github" => redirect("http://github.com/rails.atom")      # 地址 /foo/1 會自動跳轉到 /bar/1s   match "/foo/:id":to => redirect("/bar/%{id}s")        # /account/proc/inosin 會自動跳轉到 /inosins   match 'account/proc/:name':to => redirect {|params|    "/#{params[:name].pluralize}" }      match "/stories" => redirect {|p, req| "/posts/#{req.subdomain}" }   
match "/posts/github" => redirect("http://github.com/rails.atom") # 地址 /foo/1 會自動跳轉到 /bar/1s match "/foo/:id", :to => redirect("/bar/%{id}s") # /account/proc/inosin 會自動跳轉到 /inosins match 'account/proc/:name', :to => redirect {|params| "/#{params[:name].pluralize}" } match "/stories" => redirect {|p, req| "/posts/#{req.subdomain}" } 

路由中的限制:
Ruby代碼
# 限制 id 只能爲數字   match "/posts/show/:id":to => "posts#index":id => /\d+/   match "/posts/show/:id":to => "posts#index":constraints => {:id => /\d+/}      # 限制子域名   match "photos":constraints => {:subdomain => "admin"}       # 限制訪問者 IP   constraints(:ip => /127.0.0.1/) do     match  '/questions':to => redirect("http://www.stackoverflow.com/")   end      # 當訪問者 ip 是 192.168.1.* 的來訪者訪問 子域名爲 "test"   match "/ttt" => proc{|env| [200, {}, ["hello test"]]}, \       :constraints => {:subdomain => "test":ip => /192\.168\.1\.\d+/}   
# 限制 id 只能爲數字 match "/posts/show/:id", :to => "posts#index", :id => /\d+/ match "/posts/show/:id", :to => "posts#index", :constraints => {:id => /\d+/} # 限制子域名 match "photos", :constraints => {:subdomain => "admin"} # 限制訪問者 IP constraints(:ip => /127.0.0.1/) do match '/questions', :to => redirect("http://www.stackoverflow.com/") end # 當訪問者 ip 是 192.168.1.* 的來訪者訪問 子域名爲 "test" match "/ttt" => proc{|env| [200, {}, ["hello test"]]}, \ :constraints => {:subdomain => "test", :ip => /192\.168\.1\.\d+/} 

路由通配符:
Ruby代碼
resources :photos:id => /\d+/   match 'photos/*other' => 'photos#unknown'   #上面這兩行路由則會把不符合7種path的其餘url所有解析到PhotoController#unknown中去處理,params[:other]可獲得path中/photos/以後的部分,注意這兩行的順序不能顛倒      match 'books/*section/:title' => 'books#show'    # 例如:books/some/section/last-words-a-memoir 中 params[:section] = "some/section", params[:title] = "last-words-a-memoir".      match '*a/foo/*b' => 'test#index'    # 例如:zoo/woo/foo/bar/baz 中 params[:a] = "zoo/woo", params[:b] = "bar/baz"  
resources :photos, :id => /\d+/ match 'photos/*other' => 'photos#unknown' #上面這兩行路由則會把不符合7種path的其餘url所有解析到PhotoController#unknown中去處理,params[:other]可獲得path中/photos/以後的部分,注意這兩行的順序不能顛倒 match 'books/*section/:title' => 'books#show' # 例如:books/some/section/last-words-a-memoir 中 params[:section] = "some/section", params[:title] = "last-words-a-memoir". match '*a/foo/*b' => 'test#index' # 例如:zoo/woo/foo/bar/baz 中 params[:a] = "zoo/woo", params[:b] = "bar/baz" 

Rack:
Ruby代碼
match "/foo":to => proc {|env| [200, {}, ["Hello world"]] }       match 'rocketeer.js' => ::TestRoutingMapper::RocketeerApp       RocketeerApp = lambda { |env|     [200, {"Content-Type" => "text/html"}, ["javascripts"]]   }  
match "/foo", :to => proc {|env| [200, {}, ["Hello world"]] } match 'rocketeer.js' => ::TestRoutingMapper::RocketeerApp RocketeerApp = lambda { |env| [200, {"Content-Type" => "text/html"}, ["javascripts"]] }
相關文章
相關標籤/搜索