跳过正文

第8篇:ActionPack 概览

·547 字·3 分钟
xieweicong
作者
xieweicong

学习目标:理解 ActionPack 的整体架构和请求处理流程

引言
#

ActionPack 是 Rails 的 MVC 中的 C(Controller)和 V(View)层,负责处理 HTTP 请求和生成响应。

1. ActionPack 的组成
#

1.1 核心组件
#

ActionPack
├── ActionDispatch    # 路由和中间件
├── ActionController  # 控制器
└── AbstractController # 抽象控制器基类

1.2 与 Rack 的关系
#

# Rails 应用是一个 Rack 应用
Rails.application.is_a?(Rack::Application)  # => true

# config.ru
run Rails.application

2. Rack 接口
#

2.1 Rack 应用的本质
#

# 最简单的 Rack 应用
class HelloApp
  def call(env)
    [200, {"Content-Type" => "text/plain"}, ["Hello World"]]
  end
end

# Rack 应用必须响应 call 方法
# 接收环境哈希
# 返回 [状态码, 响应头, 响应体]

2.2 Rails 如何实现 Rack
#

# railties/lib/rails/application.rb
module Rails
  class Application < Engine
    def call(env)
      env["action_dispatch.request_id"] = SecureRandom.uuid
      @middleware_stack.call(env)
    end
  end
end

3. 中间件栈
#

3.1 什么是中间件?
#

class SimpleMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    # 请求前处理
    puts "Before request"

    status, headers, body = @app.call(env)

    # 响应后处理
    puts "After request"

    [status, headers, body]
  end
end

3.2 Rails 的中间件栈
#

$ rails middleware
use Rack::Sendfile
use ActionDispatch::Static
use Rack::Runtime
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
run MyApp::Application.routes

3.3 自定义中间件
#

# config/application.rb
config.middleware.use MyMiddleware
config.middleware.insert_before Rack::Runtime, MyMiddleware
config.middleware.delete Rack::Runtime

4. 请求生命周期
#

4.1 完整的请求流程
#

1. 浏览器发送 HTTP 请求
   ↓
2. Web 服务器(Puma)接收请求
   ↓
3. Rack 接口转换为 env 哈希
   ↓
4. 经过中间件栈处理
   ↓
5. 路由匹配(ActionDispatch::Routing)
   ↓
6. 分发到控制器(ActionController)
   ↓
7. 执行 action
   ↓
8. 渲染视图(ActionView)
   ↓
9. 返回响应
   ↓
10. 反向经过中间件栈
   ↓
11. 发送给浏览器

4.2 Request 和 Response 对象
#

# ActionDispatch::Request
request.path              # => "/users/1"
request.method            # => "GET"
request.params            # => {"id" => "1"}
request.headers           # => {"Accept" => "text/html", ...}
request.cookies           # => {"session_id" => "..."}
request.xhr?              # => false (是否 AJAX)
request.remote_ip         # => "192.168.1.1"

# ActionDispatch::Response
response.status           # => 200
response.headers          # => {"Content-Type" => "text/html", ...}
response.body             # => "<html>...</html>"
response.content_type     # => "text/html"

5. ActionDispatch 核心类
#

5.1 RouteSet - 路由集合
#

# actionpack/lib/action_dispatch/routing/route_set.rb
class RouteSet
  def draw(&block)
    # 执行 config/routes.rb 中的代码
  end

  def recognize_path(path, environment = {})
    # 识别路径,返回控制器和 action
  end
end

5.2 Journey - 路由引擎
#

# Rails 使用 Journey 库进行路由匹配
# actionpack/lib/action_dispatch/journey/

6. 参数处理
#

6.1 参数的来源
#

# URL 参数
# GET /users?name=John
params[:name]  # => "John"

# 路径参数
# GET /users/1
# routes: get '/users/:id', to: 'users#show'
params[:id]    # => "1"

# POST 数据
# POST /users
# Body: { user: { name: "John" } }
params[:user][:name]  # => "John"

6.2 Strong Parameters
#

class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    # ...
  end

  private
    def user_params
      params.require(:user).permit(:name, :email, :password)
    end
end

# 不允许的参数会被过滤
# params = { user: { name: "John", admin: true } }
# user_params => { name: "John" }  # admin 被过滤

7. Session 和 Cookies
#

7.1 Session
#

# 存储在 session 中
session[:user_id] = current_user.id

# 读取
current_user_id = session[:user_id]

# 删除
session.delete(:user_id)

# 清空
reset_session

7.2 Cookies
#

# 普通 cookie
cookies[:user_preference] = "dark_mode"

# 签名 cookie(防止篡改)
cookies.signed[:user_id] = current_user.id

# 加密 cookie
cookies.encrypted[:secret_data] = "sensitive"

# 永久 cookie
cookies.permanent[:remember_token] = token

8. Flash 消息
#

class SessionsController < ApplicationController
  def create
    if user.authenticate(params[:password])
      flash[:success] = "Welcome back!"
      redirect_to root_path
    else
      flash.now[:error] = "Invalid credentials"
      render :new
    end
  end
end

# 视图中
<% if  flash[:success] %>
  <div class="alert alert-success"><%= flash[:success] %></div>
<% end  %>

9. 本章总结
#

ActionPack 是 Rails 请求处理的核心:

  • 基于 Rack 接口
  • 使用中间件栈处理请求
  • 提供路由、控制器、视图支持
  • 处理参数、会话、cookies

上一篇ActiveModel 接口 下一篇路由系统深入 返回学习指南首页