Routes的几种用法整理
resources
最常见的 Routes 用法是 resources
,比如说
resources :users
Rails 就会默认替我们生成对应 7 个 action 的路径,并且生成相应的 helper 方便我们在 view 中使用
controller#action | HTTP verb | path | helper |
---|---|---|---|
users#index | GET | /users | users_path |
users#create | POST | /users | users_path |
users#new | GET | /users/new | new_user_path |
user#edit | GET | /users/:id | edit_user_path(user_id) |
user#show | GET | /users/:id | user_path(user_id) |
user#update | PUT/PATCH | /users/:id | user_path(user_id) |
user#destroy | DELETE | /users/:id | user_path(user_id) |
也就是我们常说的 CRUD 的 7 个 actions。
生成的 helper 除了 _path
后缀之外,其实还有一个 _url
后缀,前者是指向相对路径,后者是指向绝对路径。
如果用单数的 resouce
,会产生 6 个 action,没有了 index
。
resource :user
controller#action | HTTP verb | path | helper |
---|---|---|---|
users#create | POST | /user | users_path |
users#new | GET | /user/new | new_user_path |
user#edit | GET | /user/edit | edit_user_path |
user#show | GET | /user | user_path |
user#update | PUT/PATCH | /user | user_path |
users#destroy | DELETE | /user | user_path |
在 Rails 中,Routes 的作用是,建立 View 和 Controller 之间的连接通道。通过 Routes 建立的每一个路径,都应该指向某个 controller 里的某一个 action。Rails 遵循的一个原则是 “Convension Over Configuration”,换句话说,在 Rails 里有很多「约定俗成」的规则。一句 resources
就生成 7 个 action ,就是一种约定俗成的做法。
通过 resources
和 resource
生成路径的对比,可以看出一个规则
复数形式:指向某个对象时,需要输入
id
单数形式:全部都是指向单个对象,不需要输入
id
除了 convenstion
,Rails 其实也支持 configuration
,也就是说也可以自定义。
单条资源请求
比如说,指向一个单数资源,有时候也并不需要传递 id
,比如说网站用户的信息编辑页面,也就是 users#show
。如果用 resources
来生成的话,路径就是会是 /users/:id
。
因为只有登录的用户才可以进入自身的信息编辑页面,既然已经登录,用户的 id
已经通过 session 获取了,就并不需要在网址上传递自身的 id
。在 routes 里就可以这样来设定
get 'profile', to: 'users#show'
或者
get 'profile' => 'users#show'
这样得到的网址是 /profile
,HTTP verb 是 GET
,指向是 users
这个 controller 下 show
这个 action, helper 是 profile_path
和 profile_url
。
namespace
最常用的场景就是,针对不同的用户,我们对同一个数据库有不同的操作。
比如说 users
这个 model,对于普通用户,应该可以对于自身的信息进行编辑,需要 controller 来实现;对于管理员,应该可以对所有用户的某些信息进行编辑,比如说修改用户的权限等,也需要 controller 来实现。显然,普通用户和管理员最好能用不同的 controller,比如说 admin/users_controller
和 account/users_countroller
。
这种情况下,在 routes 里也应该有相应的指向,就会用到 namespace。
namespace :admin do
resources :users
end
这样得到的 path 都带一个 /admin/..
,controller 也是对应到 /admin
下的 controller,得到的 helper 也会带一个 admin_users
前缀。比如说
controller#action | HTTP verb | path | helper |
---|---|---|---|
admin/users#index | GET | /admin/users | admin_user_path |
Scope
这是一个特殊的使用场景,假如我们不希望在网址中显示 /admin 这个前缀,但又想把路径指向 /admin 下的 controller,这时就可以用 scope
# 批量定义
scope module: 'admin' do
resources :users
end
# 单个定义
reources :users, module: 'admin'
这样得到的结果是(以 Index 为例)
controller#action | HTTP verb | path | helper |
---|---|---|---|
admin/users#index | GET | /users | users_path |
注意到,与 namespace 不同的是 path 中没有显示 /admin
这个前缀,helper 中也没有 admin_
的前缀,但实际上都是指向了 admin/users_controller
。
反过来,如果我们想在 url 中显示 /admin
,但不想把 controller 放在 /admin
下,也可以用 scope 来实现
scope '/admin' do
resources :users
end
这样的效果是
controller#action | HTTP verb | path | helper |
---|---|---|---|
users#index | GET | admin/users | users_path |
nested resources
嵌入路由。
resources :users do
resources :posts
end
这里的 user
和 post
之间的关系一般是::user
has_many :posts
。也是说,一个用户可以发表很多篇文章,当要对某一用户所发表的文章进行操作时,那就可能要通过 url 请求传递两个信息,一个是用户的 id,另一个可能是文章的 id。通过嵌入式的路由,Rails 会帮我们生成这样的 url,例如上述的例子,得到的结果是
controller#action | HTTP verb | path | helper |
---|---|---|---|
posts#index | GET | /users/:user_id/posts | users_posts_path |
posts#create | POST | /users/:user_id/posts | users_posts_path |
posts#new | GET | /users/new/:user_id/posts/new | new_user_post_path(user_id, id) |
posts#edit | GET | /users/:id/:user_id/posts/:id/edit | edit_user_post_path(user_id, id) |
posts#show | GET | /users/:id/:user_id/posts/:id | user_post_path(user_id, id) |
posts#update | PUT/PATCH | /users/:id/:user_id/posts/:id | user_post_path(user_id, id) |
posts#destroy | DELETE | /users/:id/:user_id/posts/:id | user_post_path(user_id, id) |
使用嵌入路由的时候要注意,一般只嵌入一次,不要多次嵌入,这样会造成 URL 的请求的信息过多,而实际上我们往往并不需要那么多的信息。
比如说 user 下有 post, post 下有 comment,一般把 post 嵌到 user 下,把 comment 嵌到 post 下,但要分成两个嵌入
resources :users do
resources :posts
end
resources :posts do
resources :comments
end
而不要写成这样
resources :users do
resources :posts do
resources :comments
end
end
user 和 post 是一对多的关系,post 和 comment 也是一对多的关系。有了 post_id ,就可以回溯得到 user_id,没有必要再 URL 里增加一个 user_id 的请求。
排除不需要的 action 和请求方式
通过 resources
可以得到 7 个 action 的请求方式,但有时候我们并不需要那么多,这时可以用 only:
来指定。
resources :users, only: [:index, :destroy]
自上而下的机制
如果在 routes 中对同一个路由多次定义,Rails 只认第一次定义,比如
get '/users' => 'users#index'
get '/users' => 'users#index_old'
那么 Rails 实际只认第一个路由。
member 和 collection
当我们需要自定义 RESTful 的路由的时候,可以用 member
,collection
。当需要在 URL 里传递 :id
的时候用 member
,不需要传递 :id
的时候用 collection
。
# 批量指定
resources :users do
member do
post :status
#...
end
collection do
get :online
#...
end
end
# 单个指定
resources :users do
post :status, on: :member
get :online, on: :collection
end
得到的结果会是
controller#action | HTTP verb | path | helper |
---|---|---|---|
users#status | POST | /users/:id/status | status_user_path |
users#online | GET | /users/online | online_users_path |
也可以简单地理解,单数时用 member
,复数时用 collection
。
重定向
在 routes 里是可以做重定向的,例如
get '/articles', to: redirect('/posts')
用 as
来定义 helper
例子:
get '/welcome/hello' => 'welcome#hello', as: 'welcome_hello'
这里自定义了一个路由,:as
的作用是生成一个 welcome_hello
的 helper,这样我们在 view 里可以用 welcome_hello_path
或者 welcome_hello_url
来指向这个地址了。
在用 member
或者 collection
自定义 RESTful 路由的时候,我们也可以自定义 helper
resources :users do
post :status, on: :member, as: 'profile'
end
这样的得到的 helper 就不是 status_user
,而是 profile_user
了。
对同一个 controller 做批量指定
例子:
controller ':welcome' do
get '/welcome/hello'
get '/welcome/plan'
end
得到的结果是
controller#action | HTTP verb | path | helper |
---|---|---|---|
welcome#hello | GET | /welcome/hello | welcome_hello_path |
welcome#plan | GET | /welcome/plan | welcom_plan_path |
关于 link_to
在 view 中,用 link_to
方法可以让 Rails 帮我们生成一个链接,但是对于同一个链接地址,可能会对应不同的 action,还需要用请求方式来进一步确定。
比如我们用 resources
定义了:posts
,如果我们在 view 中这么写
<% link_to "update", post_path(post) %>
指向的会是 show
这个 action,而不是 update
。
因为 link_to
方法如果没有指定请求方式的话,默认会用 GET 来请求。 post_path(post)
+ GET 的请求对应的是 posts#show
。如果想要指向 posts#update
,需要这么来指定
<% link_to "update", post_path(post), method: :patch %>