Rails其实很好懂,可视的源码和大量的注释,只看你有没有心去一窥究竟。今天就来看看貌似神秘的routes吧。
一个命令
首先,介绍一个rake命令。对于不了解routes定义规则的,或许看到routes.rb文件有点迷糊。不要紧,如果你想看看一个url到底对应了哪个controller以及action,就用rake routes展开所有的奥秘吧。
几多规则
routes的定义规则其实不多,让我们来一一分析下吧。不过要保持耐心。
分析routes.rb文件,首先得搞清楚,它的优先级是从上到下,谁先匹配谁先得。
1. 默认规则
# Install the default routes as the lowest priority. # Note: These default routes make all actions in every controller accessible via GET requests. You should # consider removing the them or commenting them out if you're using named routes and resources. map.connect ':controller/:action/:id' map.connect ':controller/:action/:id.:format'
自定义的路由规则往往需要覆盖默认的路由规则,所以,rails的routes模板把默认路由规则放在文件的最下面,并且鼓励考虑用具名路由替换它们。
2. 无名规则
大家看到前面的map.connect,那么这个方法到底做了什么?
# Create an unnamed route with the provided +path+ and +options+. See # ActionController::Routing for an introduction to routes. def connect(path, options = {}) @set.add_route(path, options) end
可以看到,connect方法为我们生成了一个无名路由规则。
我们可以如此生成一个无名规则:
map.connect 'products/:id', :controller => 'catalog', :action => 'view'
3. 具名规则
为什么要有具名规则,主要是因为rails可以为具名规则生成一些url helper。关于routes的helper,待会儿再详细描述,先来看看具名规则吧。
map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
如此,我们已经生成了一个名字叫做purchase的具名规则。
看看源代码
def named_route(name, path, options = {}) #:nodoc: @set.add_named_route(name, path, options) end
4. 单资源REST风格路由规则: map.resource
我们定义这样一个规则: map.resource :account
它会帮我们定义怎样的一个路由规则呢? 答案是一个遵循REST风格的路由规则,非常完美。
# maps these actions in the Accounts controller: class AccountsController < ActionController::Base # GET new_account_url def new # return an HTML form for describing the new account end # POST account_url def create # create an account end # GET account_url def show # find and return the account end # GET edit_account_url def edit # return an HTML form for editing the account end # PUT account_url def update # find and update the account end # DELETE account_url def destroy # delete the account end end
让我们用rake routes看看它为我们生成了怎样的路由。
account POST /account {:controller=>"accounts", :action=>"create"}
formatted_account POST /account.:format {:controller=>"accounts", :action=>"create"}
new_account GET /account/new {:controller=>"accounts", :action=>"new"}
formatted_new_account GET /account/new.:format {:controller=>"accounts", :action=>"new"}
edit_account GET /account/edit {:controller=>"accounts", :action=>"edit"}
formatted_edit_account GET /account/edit.:format {:controller=>"accounts", :action=>"edit"}
GET /account {:controller=>"accounts", :action=>"show"}
GET /account.:format {:controller=>"accounts", :action=>"show"}
PUT /account {:controller=>"accounts", :action=>"update"}
PUT /account.:format {:controller=>"accounts", :action=>"update"}
DELETE /account {:controller=>"accounts", :action=>"destroy"}
DELETE /account.:format {:controller=>"accounts", :action=>"destroy"}
5. 集合资源REST风格路由规则
我们定义一个这样的规则: map.resources :messages
# map.resources :messages # will map the following actions in the corresponding controller: class MessagesController < ActionController::Base # GET messages_url def index # return all messages end # GET new_message_url def new # return an HTML form for describing a new message end # POST messages_url def create # create a new message end # GET message_url(:id => 1) def show # find and return a specific message end # GET edit_message_url(:id => 1) def edit # return an HTML form for editing a specific message end # PUT message_url(:id => 1) def update # find and update a specific message end # DELETE message_url(:id => 1) def destroy # delete a specific message end end
messages GET /messages {:controller=>"messages", :action=>"index"}
formatted_messages GET /messages.:format {:controller=>"messages", :action=>"index"}
POST /messages {:controller=>"messages", :action=>"create"}
POST /messages.:format {:controller=>"messages", :action=>"create"}
new_user GET /messages/new {:controller=>"messages", :action=>"new"}
formatted_new_user GET /messages/new.:format {:controller=>"messages", :action=>"new"}
edit_user GET /messages/:id/edit {:controller=>"messages", :action=>"edit"}
formatted_edit_user GET /messages/:id/edit.:format {:controller=>"messages", :action=>"edit"}
user GET /messages/:id {:controller=>"messages", :action=>"show"}
formatted_user GET /messages/:id.:format {:controller=>"messages", :action=>"show"}
PUT /messages/:id {:controller=>"messages", :action=>"update"}
PUT /messages/:id.:format {:controller=>"messages", :action=>"update"}
DELETE /messages/:id {:controller=>"messages", :action=>"destroy"}
DELETE /messages/:id.:format {:controller=>"messages", :action=>"destroy"}
6. 嵌套规则
简单嵌套规则
map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller
用rake routes看看生成的路由规则,即可一目了然。
一个更加复杂的嵌套规则
map.resources :products do |products| products.resources :comments products.resources :sales, :collection => { :recent => :get } end
7. root
# You can have the root of your site routed with map.root -- just remember to delete public/index.html. map.root :controller => 'home'
root路由用于映射根目录。
8. namespace
我们还可以定义一个命名空间,如这样。
map.namespace :admin do |admin| # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb) admin.resources :products end
7. path_prefix, name_prefix等其他options
当想让不同的method访问同一个url时,对应到不同的controller或者action,可以如是:
map.signup '/signup', :controller => 'people', :action => 'new', :conditions => {:method => :get} map.signup '/signup', :controller => 'people', :action => 'create', :conditions => {:method => :post}
还是不再赘述了,这样的option很多,在不明白的时候,不如直接看源代码吧。
routes helpers
当我们定义了一个具名路由或者REST风格路由时,router helper会为我们生成一些帮助方法。
比如当我们定义了一个这样的路由时:
map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
生成的helper方法有:
purchase_url(:id => 1) # http://test.url/products/1/purchase
purchase_path(:id => 1) # /products/1/purchase
hash_for_purchase_url(:id => 1) # {:controller=>"sessions", :action=>"new", :use_route=>:new_session, :only_path=>false}
hash_for_purchase_path(:id => 1) # {:controller=>"sessions", :action=>"new", :use_route=>:new_session, :only_path=>true}
delete & put
对delete和put method,现在大多数浏览器都不能处理,所以以post方法代替,并附上:method参数。
<% form_for :message, @message, :url => message_path(@message), :html => {:method => :put} do |f| %>
保持简单
最后有点感触,随着项目的进行,routes会变得越来越复杂,越来越难以看懂。所以,我们要遵循保持简单,尽量使用REST风格的原则。
1 楼 4268146 2011-06-02 10:19