学习目标:掌握 Rails 路由的 DSL、实现原理和高级用法
引言#
路由是 Rails 应用的入口,负责将 HTTP 请求分发到对应的控制器 action。
1. 路由基础#
1.1 RESTful 路由#
# config/routes.rb
Rails.application.routes.draw do
resources :posts
end
# 生成 7 个路由:
# GET /posts => posts#index
# GET /posts/new => posts#new
# POST /posts => posts#create
# GET /posts/:id => posts#show
# GET /posts/:id/edit => posts#edit
# PATCH /posts/:id => posts#update
# DELETE /posts/:id => posts#destroy
1.2 单数资源#
resource :profile
# 生成 6 个路由(没有 index)
# GET /profile/new
# POST /profile
# GET /profile
# GET /profile/edit
# PATCH /profile
# DELETE /profile
1.3 嵌套资源#
resources :posts do
resources :comments
end
# POST /posts/:post_id/comments => comments#create
1.4 限制路由#
resources :posts, only: [:index, :show]
resources :users, except: [:destroy]
2. 自定义路由#
2.1 简单路由#
get '/about', to: 'pages#about'
post '/contact', to: 'contacts#create'
# 或简写
get 'about', to: 'pages#about'
2.2 成员路由和集合路由#
resources :posts do
member do
post 'publish' # POST /posts/:id/publish
get 'preview' # GET /posts/:id/preview
end
collection do
get 'search' # GET /posts/search
end
end
# 简写
resources :posts do
post 'publish', on: :member
get 'search', on: :collection
end
2.3 命名空间#
namespace :admin do
resources :posts
end
# GET /admin/posts => Admin::PostsController#index
scope module: 'admin' do
resources :posts
end
# GET /posts => Admin::PostsController#index
scope '/admin' do
resources :posts
end
# GET /admin/posts => PostsController#index
3. 路由约束#
3.1 参数约束#
get 'posts/:id', to: 'posts#show', constraints: { id: /\d+/ }
get 'users/:username', to: 'users#show', constraints: { username: /[A-Za-z]+/ }
3.2 请求约束#
get 'photos', to: 'photos#index', constraints: { subdomain: 'api' }
get 'photos', to: 'photos#mobile', constraints: { user_agent: /Mobile/ }
3.3 高级约束#
class AdminConstraint
def matches?(request)
request.session[:user_id] && User.find(request.session[:user_id]).admin?
end
end
constraints AdminConstraint.new do
resources :admin_posts
end
4. 路由辅助方法#
4.1 URL 和 Path 方法#
posts_path # => "/posts"
posts_url # => "http://example.com/posts"
post_path(@post) # => "/posts/1"
edit_post_path(@post) # => "/posts/1/edit"
new_post_path # => "/posts/new"
4.2 自定义辅助方法名#
get 'exit', to: 'sessions#destroy', as: :logout
logout_path # => "/exit"
5. 路由实现原理#
5.1 RouteSet 结构#
# actionpack/lib/action_dispatch/routing/route_set.rb
module ActionDispatch
module Routing
class RouteSet
attr_accessor :routes
def draw(&block)
# 在 Mapper 上下文中执行 block
mapper = Mapper.new(self)
mapper.instance_eval(&block)
end
def recognize_path(path, environment = {})
# 匹配路径,返回参数
end
end
end
end
5.2 Mapper - DSL 实现#
# actionpack/lib/action_dispatch/routing/mapper.rb
class Mapper
def resources(name, options = {}, &block)
# 定义 RESTful 路由的实现
get "#{name}", action: :index
get "#{name}/new", action: :new
post "#{name}", action: :create
get "#{name}/:id", action: :show
get "#{name}/:id/edit", action: :edit
patch "#{name}/:id", action: :update
delete "#{name}/:id", action: :destroy
end
end
6. 路由性能优化#
6.1 路由顺序#
# ❌ 慢 - 通配符在前
get '*path', to: 'pages#show'
resources :posts
# ✅ 快 - 具体路由在前
resources :posts
get '*path', to: 'pages#show'
6.2 路由缓存#
# 使用路由辅助方法会缓存结果
posts_path # 第一次慢
posts_path # 之后从缓存读取
7. 调试路由#
7.1 查看所有路由#
$ rails routes
$ rails routes -c posts # 过滤
$ rails routes -g new # grep
7.2 在代码中检查#
Rails.application.routes.recognize_path("/posts/1")
# => {:controller=>"posts", :action=>"show", :id=>"1"}
8. 本章总结#
路由系统是 Rails 的入口:
- RESTful 约定简化路由定义
- 支持嵌套、命名空间、约束
- 提供强大的 DSL
- 自动生成辅助方法
上一篇:ActionPack 概览 下一篇:Controller 详解 返回:学习指南首页