跳至内容 跳至搜索

路由模块以原生 Ruby 的方式提供 URL 重写功能。它是一种将传入请求重定向到控制器和操作的方法。这取代了 mod_rewrite 规则。最棒的是,Rails 的 Routing 可以与任何 Web 服务器配合使用。路由在 config/routes.rb 中定义。

可以将创建路由想象成为请求绘制一张地图。地图会根据一些预定义的模式告诉它们去哪里。

Rails.application.routes.draw do
  Pattern 1 tells some request to go to one place
  Pattern 2 tell them to go to another
  ...
end

以下符号是特殊的。

:controller maps to your controller name
:action     maps to an action with your controllers

其他名称只是映射到一个参数,就像 :id 的情况一样。

资源

资源路由允许您为给定的资源控制器快速声明所有常用路由。您无需为 indexshowneweditcreateupdatedestroy 操作声明单独的路由,一个资源路由只需一行代码即可声明它们。

resources :photos

有时,您会遇到一个客户端始终在不引用 ID 的情况下查找的资源。一个常见的例子是 /profile 始终显示当前登录用户的个人资料。在这种情况下,您可以使用单数资源将 /profile(而不是 /profile/:id)映射到 show 操作。

resource :profile

将资源作为其他资源的逻辑子项是很常见的。

resources :magazines do
  resources :ads
end

您可能希望将一组控制器组织到一个命名空间下。最常见的是,您可能希望将一组管理控制器组织到一个 admin 命名空间下。您会将这些控制器放在 app/controllers/admin 目录下,并在路由器中将它们组合起来。

namespace "admin" do
  resources :posts, :comments
end

或者,您可以使用 scope 在不使用单独目录的情况下为您的路径添加前缀。scope 接受适用于所有封闭路由的其他选项。

scope path: "/cpanel", as: 'admin' do
  resources :posts, :comments
end

更多信息,请参阅 Routing::Mapper::Resources#resourcesRouting::Mapper::Scoping#namespaceRouting::Mapper::Scoping#scope

非资源路由

对于不符合 resources 模式的路由,您可以使用 HTTP 帮助方法 getpostpatchputdelete

get 'post/:id', to: 'posts#show'
post 'post/:id', to: 'posts#create_comment'

现在,如果您 POST 到 /posts/:id,它将路由到 create_comment 操作。同一个 URL 的 GET 请求将路由到 show 操作。

如果您的路由需要响应多个 HTTP 方法(或所有方法),那么在 match 上使用 :via 选项是更优的选择。

match 'post/:id', to: 'posts#show', via: [:get, :post]

命名路由

可以通过传递 :as 选项来命名路由,以便在您的源代码中轻松引用,对于完整 URL 为 name_of_route_url,对于 URI 路径为 name_of_route_path

示例

# In config/routes.rb
get '/login', to: 'accounts#login', as: 'login'

# With render, redirect_to, tests, etc.
redirect_to login_url

也可以传递参数。

redirect_to show_item_path(id: 25)

使用 root 作为将路由命名为根路径“/”的简写。

# In config/routes.rb
root to: 'blogs#index'

# would recognize http://www.example.com/ as
params = { controller: 'blogs', action: 'index' }

# and provide these named routes
root_url   # => 'http://www.example.com/'
root_path  # => '/'

注意:在使用 controller 时,路由的命名仅根据您在块参数上调用的方法而不是映射。

# In config/routes.rb
controller :blog do
  get 'blog/show'    => :list
  get 'blog/delete'  => :delete
  get 'blog/edit'    => :edit
end

# provides named routes for show, delete, and edit
link_to @article.title, blog_show_path(id: @article.id)

漂亮的 URL

路由可以生成漂亮的 URL。例如:

get '/articles/:year/:month/:day', to: 'articles#find_by_id', constraints: {
  year:       /\d{4}/,
  month:      /\d{1,2}/,
  day:        /\d{1,2}/
}

使用上面的路由,URL “localhost:3000/articles/2005/11/06” 映射到:

params = {year: '2005', month: '11', day: '06'}

正则表达式和参数

您可以指定一个正则表达式来定义参数的格式。

controller 'geocode' do
  get 'geocode/:postalcode', to: :show, constraints: {
    postalcode: /\d{5}(-\d{4})?/
  }
end

约束可以包括“忽略大小写”和“扩展语法”正则表达式修饰符。

controller 'geocode' do
  get 'geocode/:postalcode', to: :show, constraints: {
    postalcode: /hx\d\d\s\d[a-z]{2}/i
  }
end

controller 'geocode' do
  get 'geocode/:postalcode', to: :show, constraints: {
    postalcode: /# Postalcode format
       \d{5} #Prefix
       (-\d{4})? #Suffix
       /x
  }
end

使用多行修饰符会引发 ArgumentError。编码正则表达式修饰符会被静默忽略。匹配将始终使用默认编码或 ASCII。

外部重定向

您可以使用路由器中的 redirect 助手将任何路径重定向到另一个路径。

get "/stories", to: redirect("/posts")

Unicode 字符路由

您可以在路由器中指定 Unicode 字符路由。

get "こんにちは", to: "welcome#index"

Routing 到 Rack 应用程序

而不是像 posts#index 这样的 String(对应于 PostsController 中的 index 操作),您可以将任何 Rack 应用程序指定为匹配器的终结点。

get "/application.js", to: Sprockets

重新加载路由

如果您觉得有必要,可以重新加载路由。

Rails.application.reload_routes!

这将清除所有命名路由并重新加载 config/routes.rb(如果文件自上次加载以来已修改)。要绝对强制重新加载,请使用 reload!

测试路由

测试路由的两种主要方法是:

assert_routing

def test_movie_route_properly_splits
  opts = {controller: "plugin", action: "checkout", id: "2"}
  assert_routing "plugin/checkout/2", opts
end

assert_routing 允许您测试路由是否正确解析为选项。

assert_recognizes

def test_route_has_options
  opts = {controller: "plugin", action: "show", id: "12"}
  assert_recognizes opts, "/plugins/show/12"
end

注意两者之间的细微差别:assert_routing 测试 URL 是否符合选项,而 assert_recognizes 测试 URL 是否能正确分解为参数。

在测试中,您可以简单地将 URL 或命名路由传递给 getpost

def send_to_jail
  get '/jail'
  assert_response :success
end

def goes_to_login
  get login_url
  #...
end

查看所有路由的列表

$ bin/rails routes

使用 -c 目标特定控制器,或使用 -g 进行 grep 搜索路由。与显示路由垂直的 --expanded 结合使用非常有用。

命名空间