#203 Routing in Rails 3
- Download:
- source codeProject Files in Zip (89 KB)
- mp4Full Size H.264 Video (21.1 MB)
- m4vSmaller H.264 Video (14.3 MB)
- webmFull Size VP8 Video (41.3 MB)
- ogvFull Size Theora Video (28.9 MB)
这一集我们继续研究Rails的新特性, 这次我们看一下Rails 3的路由. Rails 3提供了一些新的API和新的功能来定义路由. 为了更好的演示,我们先建立一个detour应用。
rails detour
建立好应用之后,我们可以打开config/routes.rb
文件看一下. 默认的文件包含了一些演示新API的文档和例子,当你有空的时候,这些都值得一看。 我们先删掉自带的文档,换上老的格式,再来对比一下Rails 3新的用法。
Detour::Application.routes.draw do |map| map.resources :products, :member => { :detailed => :get } map.resources :forums, :collection => { :sortable => :get, :sort => :put } do |forums| forums.resources :topics end map.root :controller => "home", :action => "index" map.about "/about", :controller => "info", :action => "about" end
以上是需要转化的老格式。
我们先从这一个开始:
map.resources :products, :member = { :detailed => :get }
这个路由有一个product资源,和一个叫做detailed的action,可以通过Get方法请求。
Rails 3第一个值得注意的改变,就是我们不再使用map
对象,取而代之的是在routes.draw
块里面直接调用resources。resources包括的:member
和:collection
动作,可以通过一个块来定义, 上述的路由可以新格式是这样:
resources :products do get :detailed, :on => :member end
下面,我们来看以下更复杂的路由:
map.resources :forums, :collection => { :sortable => :get, :sort => :put } do |forums| forums.resources :topics end
这个路由,在forum资源里面,我们有两个额外的:collection动作,还有一个嵌套的Topic资源。新的API中,可以写成这样:
resources :forums do collection do get :sortable put :sort end resources :topics end
以上代码在一次展示了取消map.resources
,直接用resources
块。我们有两个collection动作,尽管我们可以像第一个例子中detailed动作一样使用:on
,但我们这里用collection块达到了同样的效果 (member动作也可以使用member
块)。任何在这个块里面定义的路由,都会作用在forums的集合上。在我们的例子中,我们定义了两个,一个是GET方法的sortable和PUT方法的sort。
至于嵌套的topics资源,我们可以在forums的块里再次调用resources
,起到嵌套的效果。
下一个路由,我们看一下怎么定义root的控制器和动作。
map.root :controller => "home", :action => "index"
这里,我们可以直接调用root
,用:to
来定义要转到的动作,控制器跟动作之间,可以用#来区隔。
root :to => "home#index"
用一个#分隔的字符串来定义控制器跟动作,是Rails 3的新特性之一。我们可以用类似的格式来定义named路由。
map.about "/about", :controller => "info", :action => "about"
Rails 3中改写成这样:
match "/about" => "info#about", :as => :about
没有:as参数,这个路由就是单纯的转向”/about”, 加了:as
之后,在我们的应用里面可以使用about_path
或者about_url
。
新功能
从上面的例子可以看出,只需要微小的改动,就可以从老的API转化到新的API,但是真正吸引人的是新API提供的一些新功能,下面我们来看其中的一部分。
可选参数
前一个版本就已经有对可选参数的支持,但是用法比较麻烦,下面我们来看一下Rails 3中怎么使用。
我们建立一个info
控制器和about
动作来展示可选参数。Rails 3支持用rails g
来作为rails generate
的快捷方式。
rails g controller info about
我们可以用rails s
快捷方式来启动服务器
rails s
现在我们打开http://localhost:3000/about, 我们会转到之前已经在路由配制文件里面定义的info#about动作。
假设我们下一步想要添加对pdf格式的支持,可是现在打开http://localhost:3000/about.pdf的话,我们会看到一个路由错误,提示没有定义路由。
在路由文件里,我们可以添加一个format参数。
match "/about.:format" => "info#about", :as => :about
如果我们刷新页面,这次会得到应用找不到template错误。
我们已经解决了之前的问题,但是format参数不是可选参数,如果我们返回之前没有.pdf后缀的页面,我们会遇到路由错误,我们可以做简单的改动,只需用括号括起来,就可以让format变为可选参数。
match "/about(.:format)" => "info#about", :as => :about
然后我们可以正常的打开http://localhost:3000/about 或者 http://localhost:3000/about.pdf。
下面,我们来看一下怎么使用更为复杂的可选参数,例如,我们现在有一个博客应用,我们想要在网址中,用可选的年月日来过滤一段特定时间的贴子。
我们可以用下面的路由达到这个效果,主义括号可以嵌套起来定义多个可选参数。
match "/:year(/:month(/:day))" => "info#about"
在view中,我们添加一些调试信息,来检查传入的参数:
<h1>Info#about</h1> <p>Find me in app/views/info/about.html.erb</p> <%= debug params %>
下面,如果我们传入year参数,about动作会收到我们传入的值,month和day参数也是如此。
这个路由是比较宽泛的,如果我们传入,例如http://localhost:3000/foo/bar,这样的参数也会被传到控制器,很显然,想要符合日期规范的参数,我们可以用constraints来达到这样的目的。
Constraints
Constraints 就是Rails 2中的requirements. :constraints
可以传入一个hash来限定匹配。以下的例子用四个数字来限定年,两个数字来限定月和日。
match "/:year(/:month(/:day))" => "info#about", :constraints => { :year => /\d{4}/, :month => /\d{2}/, :day => /\d{2}/ }
添加了这个constraint之后,我们再看一下/foo/bar路径,这次我们得到了一个无法匹配的错误。
我们现在做的,Rails 2的requirements都可以做到。但是Rails 3的Constraint更加强大。
例如,我们可以用user_agent参数来限制一些浏览器的访问,以Firefox为例。
match "/secret" => "info#about", :constraints => { :user_agent => /Firefox/ }
如果我们用Safari, Chrome或者Opera访问页面,我们会看到一个匹配错误。但是Firefox可以正常浏览。
我们也可以添加一些更有用处的constraint比如主机名。
match "/secret" => "info#about", :constraints => { :host => /localhost/ }
使用这个constraint,我们可以通过http://localhost:3000/secret来浏览网页,但是用ip地址将会返回错误( http://127.0.0.1/secret ),其实两个地址本质上都是一样的。这个功能可以用来限制某些二级域名的访问,目前这一部分还在开发中,在以后的版本中,constraint可以传入:subdomain参数。
如果我们有多个路由同时使用一个constraint,会造成很多代码的重复。我们可以用constraints方法把这些路由放到一个块中来降低代码重复。
constraints :host => /localhost/ do match "/secret" => "info#about" match "/topsecret" => "info#about" end
还有很多constraint的高级功能,就不在这里一一赘述了。
Rack路由
因为时间的关系,我们没办法涵盖所有Rails3的路由改进,我们会在未来的视频里慢慢接触。 我今天想介绍的最后一个功能,是Rails 3路由与Rack的集成。 通常我们将地址匹配到一个控制器和动作,但是我们也可以匹配给一个Rack应用,这是一个非常强大的功能,我用一个简单的Rack应用来展示这个功能。
match "/hello" => proc { |env| [200, {}, "Hello Rack!"] }
如果你不是很熟悉Rack,可以看一下第151集视频,我们上面代码就是返回HTTP成功代码,和很简单的内容。
我们访问 /hello 就可以看到“Hello Rack!,以此我们可以看出Rack工作正常并响应请求.你可以用这个强大的功能将路由转到不同的应用。比如可以将路由匹配到Sinatra应用。
Rails 3的路由功能带来了很多令人振奋的可能性,尽管今天我们只是大体地介绍了一下,以后我们会详细的介绍一部分具体用法。
如果你需要更多Rails 3的路由信息,可以参考Yehuda Katz的博客和RailsGuides.