跳过正文

第9篇:路由系统深入

·501 字·3 分钟
xieweicong
作者
xieweicong

学习目标:掌握 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 详解 返回学习指南首页