Rails 路由详解

1 Rails 路由的意义

Rails 路由能够识别 URL 并且将请求分发到对应的 控制器下的 action 中。 它也可以生成相对路径和 URL 来避免在你的视图中的硬编码(hardcode string).

1.1 链接 URL 和代码

当你的 Rails 应用接收到一个像这样的请求时

GET /patients/17

它就会尝试着让路由去匹配字符串到一个控制器行为中。如果第一条路由规则是这样的:

match "/patients/:id" => "patients#show"

这个请求将会被分发到 patients 控制器下面的 show action 中去,同时 { :id => “17” } 将会存储在 params 中。

1.2 用代码生成 URL 和 路径

你同样可以用路由来生成 URl 和路径,如果你的应用包含了这样的代码:

@patient = Patient.find( 17 )
<%= link_to "Patient Record" , patient_path( @patient ) %>

路由将会自动生成一个路径 /patients/17 ,这将使你的代码更加简洁易懂而健壮。注意这里我们没必要将 id 传递给路由 Helper。

2 Resource 路由规则: Rails 之道

Resource 路由规则能让你迅速地构建出 resourceful 的控制器所需常用路由规则,它可以自动地为你生成 index, show, new, edit, create, updatedestroy 行为,而一个 Resourceful 的路由声明只需要一行代码。

2.1 web 与 Resource

浏览器向 Rails 的一个页面发起请求 URL 的时候必须要指定一个 HTTP 请求方法,例如 GET, POST, PATCH, PUT and DELETE.这些方法将会对 Rails 中的资源进行不同的操作。而一个 Resource 的路由规则将会为一个控制器自动匹配多种相关方法。

例如,当你的 Rails 程序接收到了这样的请求:

DELETE /photos/17

它会向 路由请求匹配到一个控制器行为上,这里假设匹配上了我们路由上的这条规则

resources :photos

Rails 就将会把这个请求分发到 photos 控制器下的 destroy 方法下,并且把 { :id => “17” } 作为 params 的值.

2.2 CRUD, Verbs, Actions

对于 Rails 一个 Resourceful 的路由提供了一组 HTTP 和 控制器行为的对应关系。它还约定了与每个行为相对应的 CRUD 的数据库操作。例如这样的一个 简单的 路由记录:

resources :photos

将会在应用中生成七个不同的路由记录,他们都会匹配到 Photos 控制器上去。

HTTP VerbPathactionused for
GET/photosindexdisplay a list of all photos
GET/photos/newnewreturn an HTML form for creating a new photo
POST/photoscreatecreate a new photo
GET/photos/:idshowdisplay a specific photo
GET/photos/:id/editeditreturn an HTML form for editing a photo
PATCH/PUT/photos/:idupdateupdate a specific photo
DELETE/photos/:iddestroydelete a specific photo

Rails 路由的匹配是按照规则顺序匹配的,所以如果在你的路由规则中有一句 resources :photos,接着一句 get 'photos/poll', 这样的话 resources 定义的 show 这个行为会比 get 方法优先完成匹配。如果这样并非是你的本意,你只要将 get 'photos/poll' 调到 resources 那行的前面就可以先行指定特别的匹配规则。

2.3 URL 和 Path

建立一个 Resourceful 的路由的同时也会为你的应用自动添加一系列的 Helper 方法。以上面的 resources :photos 路由规则为例:

  • photos_path 会返回 /photos
  • new_photo_path 返回 /photos/new
  • edit_photo_path(:id) 返回 /photos/:id/edit (例如, edit_photo_path(10) 返回 /photos/10/edit)
  • photo_path(:id) 返回 /photos/:id (例如, photo_path(10) returns /photos/10)

同时,这些 _path Helper 方法还有一个对应的 _url Helper (例如 photos_url) 后者将会返回包含了主机,端口等信息的绝对路径地址。

因为路由器可以使用 HTTP 动词 加上一个 URL 来匹配受到的请求,所以四个不同的 URL 可以组合出7种不同的行为。

2.4 同时定义多个 Resource

如果你想在路由表中建立多个 Resource, 你可以把他们写到一行里面去,这样只需要调用一个 resources ,同时为你节约点输入时间:

resources :photos , :books , :videos

它和下面这样写的效果是一样的:

resources :photos
resources :books
resources :videos
2.5 单件 Resources

有时候,你并不需要一个能够根据ID查询个体的 resource 。例如,你可能会想用 /profile 来显示当前登录用户的简介,这时候你可以用 map /profile (或者 /profile/:id) 一个单体化的 Resource 来匹配 show 行为。

match "profile" => "users#show"

或者这样来声明一个 Resourceful 的资源:

resource :geocoder

它会为你的应用建立六个不同的 routes ,都将匹配到 Geocoders 控制器:

HTTP VerbPathactionused for
GET/geocoder/newnewreturn an HTML form for creating the geocoder
POST/geocodercreatecreate the new geocoder
GET/geocodershowdisplay the one and only geocoder resource
GET/geocoder/editeditreturn an HTML form for editing the geocoder
PATCH/PUT/geocoderupdateupdate the one and only geocoder resource
DELETE/geocoderdestroydelete the geocoder resource

因为你可能要在路由中同时使用单数 (/account) 和 复数 (/accounts/45) 而想要对应同一个控制器,所以单件 Resource 命令也是对应复数的控制器。

一个 单件 Resourceful 路由生成这些 Helper 方法:

  • new_geocoder_path 返回 /geocoder/new
  • edit_geocoder_path 返回 /geocoder/edit
  • geocoder_path 返回 /geocoder

同复数的 resources 命令一样,以 _url 结尾的 Hepler 将会包括主机名,端口值和其他的相关路径前缀。

2.6 控制器、命名空间(Namespaces),和路由

有时,你可以想要把一些同类的控制器组织到一个命名空间下面去,其中最常见的就是我们会把一组管理功能的控制器放到 Admin:: 下,然后把这个空间下的控制器都放在 app/controllers/admin 目录下,并且统一在路由中配置:

namespace :admin do
   resources :posts , :comments
end

这样将会为每一个 postscomments 方法建立一个路由规则,对于 Admin::PostsController, Rails 会建立这些路由表规则:

HTTP VerbPathactionnamed helper
GET/admin/postsindexadmin_posts_path
GET/admin/posts/newnewnew_admin_post_path
POST/admin/postscreateadmin_posts_path
GET/admin/posts/:idshowadmin_post_path(:id)
GET/admin/posts/:id/editeditedit_admin_post_path(:id)
PATCH/PUT/admin/posts/:idupdateadmin_post_path(:id)
DELETE/admin/posts/:iddestroyadmin_post_path(:id)

如果你想要把路由 /posts (不以 /admin 作前缀) 匹配到 Admin::PostsController,你就需要这样了:

scope :module => "admin" do
   resources :posts , :comments
end

或者对单个 Resource 匹配的时候

resources :posts , :module => "admin"

而你如果想要把路由:/admin/posts 给匹配到 PostsController (控制器中没有 Admin:: 这个模块作为前缀),你可以用

scope "/admin" do
   resources :posts , :comments
end

或者在为单个 Resource 匹配的时候这么写:

resources :posts , :path => "/admin/posts"

在这些例子里面,可以发现 scope 并不会改变 routes 的生成的路径名称的(译者注:named routes,这里指的是生成的Helper的名称。) ,例如在最后一个例子里面,会生成匹配到 PostsController的规则:

HTTP VerbPathactionnamed helper
GET/admin/postsindexposts_path
GET/admin/posts/newnewnew_post_path
POST/admin/postscreateposts_path
GET/admin/posts/:idshowpost_path(:id)
GET/admin/posts/:id/editeditedit_post_path(:id)
PATCH/PUT/admin/posts/:idupdatepost_path(:id)
DELETE/admin/posts/:iddestroypost_path(:id)
2.7 嵌套(Nested)数组

通常,一个 Resource 常常有数个逻辑上的子 Resource 。例如,你的应用有这样一个模型:

class Magazine < ActiveRecord::Base
   has_many :ads
end
 
class Ad < ActiveRecord::Base
   belongs_to :magazine
end

Nested routes allow you to capture this relationship in your routing. In this case, you could include this route declaration:

resources :magazines do
   resources :ads
end

在这里,对于每一个 magazines 的路径下面都需要能够有 ad 作为 Resource 匹配到AdsController。 这时候每一组 ad 都需要指定一个 magzine 作为前缀。

HTTP VerbPathactionused for
GET/magazines/:magazine_id/adsindexdisplay a list of all ads for a specific magazine
GET/magazines/:magazine_id/ads/newnewreturn an HTML form for creating a new ad belonging to a specific magazine
POST/magazines/:magazine_id/adscreatecreate a new ad belonging to a specific magazine
GET/magazines/:magazine_id/ads/:idshowdisplay a specific ad belonging to a specific magazine
GET/magazines/:magazine_id/ads/:id/editeditreturn an HTML form for editing an ad belonging to a specific magazine
PATCH/PUT/magazines/:magazine_id/ads/:idupdateupdate a specific ad belonging to a specific magazine
DELETE/magazines/:magazine_id/ads/:iddestroydelete a specific ad belonging to a specific magazine

这样生成的路由 Hepler 就会是 magazine_ads_urledit_magazine_ad_path。这个帮助方法会把第一个参数当作指定 Magazine 的实例(例如 magazine_ads_url(@magazine))。

2.7.1 嵌套 Resources 的限制

你可以把多个 Resources 一起嵌套起来。例如:

resources :publishers do
   resources :magazines do
     resources :photos
   end
end

但是多层的嵌套很快会让你的路由变得笨重。例如上面那个例子里,你需要这样来访问一个照片:

/publishers/1/magazines/2/photos/3

相关的路由 helper 会变成 publisher_magazine_photo_url ,而且需要在参数中传入指定的每一个实例。事实上,这种令人郁闷的情形已经被很多人讨论过了,Jamis Buck 在 这里 有一份很火的 Rails 路由设计简则 ,这个问题或许可以在那里找到答案。

事实上,你无法建立超过一层深的嵌套 Resources.

2.8 从对象到路径 URL

关于 路由 Helper, Rails 还可以通过传入一组参数来建立 路径或 URL,例如,你设置了这样的一组路由:

resources :magazines do
   resources :ads
end

当你使用 magazine_ad_path 时,你可以把 MagazineAd 实例传入作参数来作为 ID。

<%= link_to "Ad details" , magazine_ad_path( @magazine , @ad ) %>

你也可以用 url_for 带上一组对象,Rails 将会自动地生成正确的路由。

<%= link_to "Ad details" , url_for([ @magazine , @ad ]) %>

在这里,Rails 将会把 @magazine 对应到 Magazine@ad 对应到 Ad 上去,然后决定使用 magazine_ad_path 这个 Helper。而如果你用的是 link_to 这样的Helper,你可以和调用 url_for 一样用对象作参数:

<%= link_to "Ad details" , [ @magazine , @ad ] %>

如果你并没有用嵌套式的路由,只要这样就可以访问一个对象的 show 方法:

<%= link_to "Magazine details" , @magazine %>

如果你要指定对象的其他动作的话你也可以这样:

<%= link_to "Edit Ad" , [ :edit , @magazine , @ad ] %>

这样就能把你的模型和 URL 有效的结合起来了,而且这也是一个很棒很 Resourceful 的特性。

2.9 加入额外的 RESTful 行为

除了7个默认创建的 RESTful 路由之外,如果你喜欢,你可以将一些额外的路由应用到你的物件集合或者单个的物件下面。

2.9.1 成员(Member)路由

加入一个成员路由只需要在你的 resource 代码块中加入你一个 member 代码块就好:

resources :photos do
   member do
     get 'preview'
   end
end

这样将会把 /photos/1/previewGET 动作识别出来,然后路由会匹配到 PhotosController 控制器下面的 preview 行为中。它同样会建立两个Helper:preview_photo_urlpreview_photo_path

当你使用了路由中的 Member 代码块时,你可以任意指定一个HTTP动词: get, patch, put, post, 或者 delete 。如果你并没有太多的 Member 路由规则,可以用 :on 作为后缀来把代码块替换掉:

resources :photos do
   get 'preview' , :on => :member
end
2.9.2 集合(Collection)路由

要想加入一个集合路由:

resources :photos do
   collection do
     get 'search'
   end
end

上面这段代码将会让 Rails 用 GET 方法匹配路径 /photos/search。并且这个 search 行为将会匹配到 PhotosController下,它将会创建 search_photos_urlsearch_photos_path 两个路由Helper。

同成员路由一样,你可以传入一个 :on 选项。

resources :photos do
   get 'search' , :on => :collection
end
2.9.3 注意事项

如果你发现自己对 Resourceful 路由加入了太多的额外规则,是时候停下来好好审视下你是不是应该创建另一个 resource。

3 非 Resourceful 路由

对于 Resourceful 的路由,Rails 有着一整套强大的路由和行为支持,即使如此,你有时候可能也会需要一些不是由 Rails 成组生成的路由,这时候,你就需要把规则手动地一个一个输入到你的应用中去了。

虽然 Resourceful 的路由规则功能强大,但是在很多时候使用一个简单路由规则更为合适。如果你觉得简单路由规则更合适,你并没有必要把应用中的每个规则都往 Resourceful 框架上套。

特别是在你为一个控制器添加上了新的行为的时候,添加一条简单路由规则往往更方便一些。

3.1 相关参数

当你为你的路由设置了一条规则的时候,你就需要提供一组符号字面量来让 Rails 能更好的识别并匹配收到的 HTTP 请求。其中有两个符号量尤其的特别::controller 位置会匹配到你应用中名字对应的控制器,:action 有匹配到你应用中对应的控制器中的行为。举个例子,下面这个默认的 Rails 路由规则:

match ':controller(/:action(/:id))'

如果收到的请求是 /photos/show/1 (前提是它还没有被其他的前面的路由规则匹配),那么按照这个规则 Rails 将会调用 PhotosController 控制器中的 show 行为,然后把后面的参数 "1" 放到 params[:id] 中以供使用。因为 :action:id 被圆括号包围,所以,他俩可以被忽略,所以这条规则同样能够将 /photos 匹配到 PhotosController#index

3.2 动态部分

你可以设置一条路由规则有任意多的动态部分,除了:controller:action ,其他的片段都将会被转换成 params 的一部分。如果你写了这样的一条路由:

match ':controller/:action/:id/:user_id'

如果收到了 /photos/show/1/2 这样的请求,将会分发到 PhotosController 下的 show 行为中,而 params[:id] 会是 "1", params[:user_id]"2".

当你使用了 :controller 来匹配路径时你就无法使用 :namespace:module 了。如果你需要像 Resourceful 路由规则一样使用它就需要加上一些正则条件,例如:

match ':controller(/:action(/:id))' , :controller => /admin\/[^\/]+/

默认情况下 动态部分无法接收句点————这是因为“点”这个符号已经被用作了匹配类型的分隔符。如果你需要在规则的动态部分中使用句点你就需要加入一些特殊的正则条件来覆写原本的匹配方法 ———— 例如:id => /[^\/]/ 将会让你的 :id+ 能够匹配除了 斜杠以外的任何字符。

3.3 静态部分

你可以在创建路由规则的时候指定一个静态的部分:

match ':controller/:action/:id/with_user/:user_id'

这个路由将会生成像这样的路径 /photos/show/1/with_user/2。在这个例子里, params { :controller => “photos”, :action => “show”, :id => “1”, :user_id => “2” }.

3.4 字符串队列

The params will also include any parameters from the query string. For example, with this route:

match ':controller/:action/:id'

如果接收到的路径为 /photos/show/1?user_id=2 ,那么 Rails 会把它分发到 Photos 控制器下面的 show 行为中去,params 会是 { :controller => “photos”, :action => “show”, :id => “1”, :user_id => “2” }.

3.5 设置默认值

如果你需要一个默认的匹配值,你可以用一个没有指定 :controller:action 的路由规则来实现。像这样:

match 'photos/:id' => 'photos#show'

Rails 将会因为这条规则而把接收到的路径 /photos/12 ,匹配到 PhotosController 下面的 show 里面去。

你还可以通过提供一个 :defaults 的散列来指定一个你默认存在的动态片段。例如:

match 'photos/:id' => 'photos#show' , :defaults => { :format => 'jpg' }

这样 photos/12 的路径就会匹配到 PhotosControllershow 行为, params[:format] 会被设置为 "jpg"

3.6 命名路由

你可以用 :as 参数来定义一个路由的名字。

match 'exit' => 'sessions#destroy' , :as => :logout

这样在应用程序中产生的 Helper 就将会是 create logout_pathlogout_url。而调用了 logout_path 就会返回 /exit 路径。

3.7 HTTP 动词约定

你可以用 :via 选项来限定一个请求能相应的一个或者多个 HTTP 方法:

match 'photos/show' => 'photos#show' , :via => :get

简写作:

get 'photos/show'

你也可以将多个动词行为绑定到一条路由规则上去:

match 'photos/show' => 'photos#show' , :via => [ :get , :post ]
3.8 部分正则约定

你可以使用 :constraints 选项来对动态路径部分强制性的匹配:

match 'photos/:id' => 'photos#show' , :constraints => { :id => /[ A - Z ]\d{ 5 }/ }

这个路由将会匹配像 /photos/A12345 这样的路由。你可以用如此更简洁的方式来写出这个规则:

match 'photos/:id' => 'photos#show' , :id => /[ A - Z ]\d{ 5 }/

:constraints 选项中使用正则表达式有一个限制,就是在正则匹配中无法使用锚元素来匹配,像这样的路由规则是无效的:

match '/:id' => 'posts#show' , :constraints => { :id => /^\d/}

总之,确定你没有在路由正则中使用锚元素。因为每个路由规则在匹配的时候就已经用到了锚元素。

例如,下面的路由 postsusers 控制器共享了一个命名空间,他们根据 to_param 值的首字符是否是数字来区分,像 1-hello-world 这样的会匹配到 posts 下面去,而像 david 就会匹配到 users 下面去。

match '/:id' => 'posts#show' , :constraints => { :id => /\d.+/ }
match '/:username' => 'users#show'
3.9 请求限制

您还可以在原有路由规则的基础上限定任何的Request的对象,它将返回 String

你可以用一个 constraint 关键字指定一个请求限制:

match "photos" , :constraints => { :subdomain => "admin" }

你也可以用代码块的形式来体现这样的限制

namespace :admin do
   constraints :subdomain => "admin" do
     resources :photos
   end
end
3.10 高级限定

如果你需要更为高级的限定,你只要为 Rails 提供一个能相应 matches? 方法的对象就可以。例如你想要把所有在黑名单中的用户都被路由过滤到 BlacklistController 里面去。 你可以:

class BlacklistConstraint
   def initialize
     @ips = Blacklist.retrieve_ips
   end
 
   def matches?(request)
     @ips .include?(request.remote_ip)
   end
end
 
TwitterClone::Application.routes.draw do
   match "*path" => "blacklist#index" ,
     :constraints => BlacklistConstraint. new
end
3.11 通配路由匹配

通配路由规则能够指定一个参数让任意部分匹配。例如:

match 'photos/*other' => 'photos#unknown'

这样这个路由将会匹配 photos/12/photos/long/path/to/12,并且把 params[:other] 设置成 "12""long/path/to/12".

通配字符串可以放在路由规则的任意部分,例如:

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".would match zoo/woo/foo/bar/baz with params[:a] equals "zoo/woo", and params[:b] equals "bar/baz".

从 Rails 3.1 开始,通配路由总是会自动地将格式字符串默认匹配,例如下面这个路由:

match '*pages' => 'pages#show'

如果你发起了这样的一个请求,你的参数 params[:pages] 将会是 "foo/bar" ,而你的请求格式会识别为 JSON 。 如果你想要在旧的 3.0.x 中有同样的特性,你需要提供一个散列参数:format => false,像这样

match '*pages' => 'pages#show' , :format => false

如果你想要强制地被指定一种格式,(其无法被忽略),你可以像这样加上 :format => true 参数:

match '*pages' => 'pages#show' , :format => true
3.12 重定向

你可以在路由规则中通过 redirect 指定一个路径重定向到另一个路由中的路径:

match "/stories" => redirect( "/posts" )

你同样可以把一条 match 命令中的动态部分的错误处理(rescue)进行重定向:

match "/stories/:name" => redirect( "/posts/%{name}" )

你同样可以为你的重定向函数加入一个代码块,代码块接收的变量是 params 和 请求对象(后者是可选的):

match "/stories/:name" => redirect {|params| "/posts/#{params[:name].pluralize}" }
match "/stories" => redirect {|p, req| "/posts/#{req.subdomain}" }

请注意,这里的重定向都将是 301 标志 “永久重定向”。在某些浏览器或者代理服务器中这个标识码将会使旧的页面失效。

在这些例子里,如果你事先没有为 Rails 提供一个主机地址,Rails 将会给予当前请求地址进行细节的处理。

3.13 Routing to Rack Applications

Instead of a String, like "posts#index", which corresponds to the index action in the PostsController, you can specify any Rack application as the endpoint for a matcher.

match "/application.js" => Sprockets

As long as Sprockets responds to call and returns a [status, headers, body], the router won’t know the difference between the Rack application and an action.

For the curious, "posts#index" actually expands out to PostsController.action(:index), which returns a valid Rack application.

3.14 使用 root

你可以用 root 方法来指定 Rails 如何匹配 "/" 路由:

root :to => 'pages#main'
root 'pages#main' # shortcut for the above

你可以将 root 规则放在文件的最上方,这样这条规则能第一个进行匹配,因为这恐怕将会是最常用的的规则了。你同样需要删除文件 public/index.html 来让这个规则发挥效用

4 定制 Resourceful 的路由规则

虽然通过 resources :posts 生成的默认的路由和 Helper 方法给你带来了很大的方便。你可能还是需要从某些时候定制这样的路由规则。 Rails 允许你用某种方法定制或者仅生成部分Helper。

4.1 指定使用控制器。

:controller 选项能够让你选择与 resource 关联的控制器。例如:

resources :photos , :controller => "images"

它将会接收 /photos开头的路径然后匹配到 Images 控制器上:

HTTP VerbPathactionnamed helper
GET/photosindexphotos_path
GET/photos/newnewnew_photo_path
POST/photoscreatephotos_path
GET/photos/:idshowphoto_path(:id)
GET/photos/:id/editeditedit_photo_path(:id)
PATCH/PUT/photos/:idupdatephoto_path(:id)
DELETE/photos/:iddestroyphoto_path(:id)

使用 photos_path, new_photo_path,等等 生成路径到这个 resource 上。

4.2 指定限制:

你可以用 :constraints 选项来限定 id 的格式。例如:

resources :photos , :constraints => { :id => /[ A - Z ][ A - Z ][ 0 - 9 ]+/}

这个声明限制了 :id 必须与参数里的正则匹配。所以,在这个例子里,路由将不再匹配 /photos/1 ,而只有 /photos/RR27 这样的才会被匹配。

你可以用一个代码块来把单个正则限制运用到多条规则上去。

constraints( :id => /[ A - Z ][ A - Z ][ 0 - 9 ]+/) do
   resources :photos
   resources :accounts
end

当然,你也可以像在 非resourceful 路由规则中运用正则一样在这里使用各种高级的正则或限定技巧。

默认情况下 :id 参数不接收 点 ———— 这是因为 点已经被用作了格式分割符。如果你需要在 :id 中的正则中匹配出 点,你需要这样复写正则表达式,例如::id => /[^\/]+/ ,这样会允许除了斜杠之外的任何字符。

4.3 覆盖 命名(Named) Helper

:as 选项能让你把默认命名的路由 Helper 重命名成定制的字符串。例如:

resources :photos , :as => "images"

这将会把 /photos 相关的路由请求匹配到 PhotosController ,但是产生的 Helper 却是 :as 选项中的值。

HTTP verbPathactionnamed helper
GET/photosindeximages_path
GET/photos/newnewnew_image_path
POST/photoscreateimages_path
GET/photos/:idshowimage_path(:id)
GET/photos/:id/editeditedit_image_path(:id)
PATCH/PUT/photos/:idupdateimage_path(:id)
DELETE/photos/:iddestroyimage_path(:id)
4.4 覆盖 newedit 片段

:path_names 选项能够让你把自动生成路径中的 “new” 和 “edit” 覆盖:

resources :photos , :path_names => { :new => 'make' , :edit => 'change' }

这将会让 Rails 能够匹配这些请求:

/photos/make
/photos/1/change

这个选项并不会改变控制器中的行为名称,这两个路径依然会被匹配到控制器中的 newedit 路径中去。

如果你想一次性地把一条设置应用到多个规则上,你可以用 scope 。

scope :path_names => { :new => "make" } do
   # rest of your routes
end
4.5 为命名(named) 路由 Helper 加上前缀

你可以使用 :as 选项来为 命名路由的 Helper 加上一个前缀。这样可以防止Helper 名和其他的名字冲突。

scope "admin" do
   resources :photos , :as => "admin_photos"
end
 
resources :photos

这样就将会产生 admin_photos_pathnew_admin_photo_path 这样的路由 Helper。

在 scope 后面用上 :as 参数,就可以为一组路由规则修改前缀:

scope "admin" , :as => "admin" do
   resources :photos , :accounts
end
 
resources :photos , :accounts

这样将会建立像 admin_photos_pathadmin_accounts_path 这样的 helper ,他们分别对应 /admin/photos and /admin/accounts

namespace scope 将会自动为你添加 :as:module:path 前缀。

你甚至还能为你的路由规则添加上一个 命名参数(named parameter):

scope ":username" do
   resources :posts
end

这样将会让你 匹配上 /bob/posts/1 这样的路径,并且在控制器,视图和 Helper 中会将 username 对应到 params[:username] 上。

4.6 对建立的路由进行限制

默认地, Rails 会在应用中为每一个 RESTful 路由建立7个动作 (index, show, new, create, edit, update, and destroy)。不过你可以用 :only:except 选项来针对你的路由进行调整:

resources :photos , :only => [ :index , :show ]

这样, 对于 /photos 的一个 GET 请求将会成功,但是对 /photos 发起的 POST 请求(原本对应的是 create 动作)将会失败。

:except 选项指定了你所_不_需要创建的动作。

resources :photos , :except => :destroy

在这里, Rails 会创建除了 destroy (针对 /photos/:id 发起的 DELETE 请求) 之外的所有默认动作。

如果你的应用程序有很多 RESTful 路由,使用 :only:except 可以只生成有用的路径,这能够减少匹配所需时间和内存。

4.7 对路径进行翻译

scope 我们可以更改 resources 生成时的路径名

scope( :path_names => { :new => "neu" , :edit => "bearbeiten" }) do
   resources :categories , :path => "kategorien"
end

Rails 将会建立一组匹配到 CategoriesController 的路由规则。

HTTP verbPathactionnamed helper
GET/kategorienindexcategories_path
GET/kategorien/neunewnew_category_path
POST/kategoriencreatecategories_path
GET/kategorien/:idshowcategory_path(:id)
GET/kategorien/:id/bearbeiteneditedit_category_path(:id)
PATCH/PUT/kategorien/:idupdatecategory_path(:id)
DELETE/kategorien/:iddestroycategory_path(:id)
4.8 重定义单复数

如果你想要为一个 resource 定义一个单数格式,你只要为 Inflector 增加上一个 额外的规则就可以了。

ActiveSupport::Inflector.inflections do |inflect|
   inflect.irregular 'tooth' , 'teeth'
end

h4 在内嵌 Resource 中使用 :as 参数

在内嵌的 Resource 中使用 :as 参数将会覆盖掉自动生成部分的 Helper 名。例如:

resources :magazines do
   resources :ads , :as => 'periodical_ads'
end

这将会建立像 magazine_periodical_ads_urledit_magazine_periodical_ad_path 这样的 Helper。

5 对路由进行检查和测试

Rails 提供了检查和测试路由的工具。

5.1 通过 rake 查看存在的路由规则

如果你想要一整套应用的完整可用的路由规则列表,运行 rake routes 命令就好。他将会根据你在 routes.rb 文件中的顺序打印出你所有的路由。对于每一个路由你都可以看到:

  • 路由的名字(如果有的话)
  • 所使用的 HTTP 动词(除非这个路由规则不相应任何动词)
  • 将会匹配的 URL 模式
  • 匹配到路由的参数

例如,这里是一个 RESTful 路由的 rake routes 命令输出的片段

    users GET    /users(.:format)          users#index
          POST   /users(.:format)          users#create
 new_user GET    /users/new(.:format)      users#new
edit_user GET    /users/:id/edit(.:format) users#edit

你可以设定环境变量 CONTROLLER 来限定只输出匹配到特定控制器的路由规则。

$ CONTROLLER=users rake routes

如果你把终端窗口放大一点让 rake routes 能够完整输出每一行可能你能看的舒服不少.

5.2 路由测试

你的测试中应该加入路由测试(就和你应用程序的其他部分一样)。Rails 提供三个 内建的断言 被设计来帮助你进行路由的测试。

  • assert_generates
  • assert_recognizes
  • assert_routing
5.2.1 assert_generates 断言

assert_generates 能够对是否生成指定的路径进行断言:

assert_generates "/photos/1" , { :controller => "photos" , :action => "show" , :id => "1" }
assert_generates "/about" , :controller => "pages" , :action => "about"
5.2.2 assert_recognizes 断言

assert_recognizesassert_generates 的反向断言. 它能够通过指定的位置判断是否与存在的路由规则匹配。

assert_recognizes({ :controller => "photos" , :action => "show" , :id => "1" }, "/photos/1" )

你可以提供一个 :method 参数来指定使用的 HTTP 行为:

assert_recognizes({ :controller => "photos" , :action => "create" }, { :path => "photos" , :method => :post })
5.2.3 assert_routing 断言
assert_routing 对一个路由双向地进行测试: 它测试的一条路径是否与选项匹配,之后测试选项是否生成路径。因此,这条断言结合了前两个测试方法 assert_generatesassert_recognizes.
assert_routing({ :path => "photos" , :method => :post }, { :controller => "photos" , :action => "create" })

关于 Rails Guides 中文

你也可以为 Rails Guides 中文做出贡献.

如果你发现了任何翻译上的错误或者不足.请到 GitHub 帐号 clone 并且 fork 一份进行更正提交,我们会很快做出回应。 如果你发现了任何代码上的 bug 或者不足,你可以到 docrails 进行反馈与更正。 如果你发现了某一篇文章已经过时了并且并没有做出相应的提示,你也同样可以在 这里 上发起一个请求来提醒我们。

如果对 Rails 有什么建议的话去 这里.

当然你不管你有什么想要说的都可以发到 Ruby China 讨论或者 mail我.欢迎加入到 Ruby on Rails 社区!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值