Rails控制器过滤器 #
一、过滤器概述 #
1.1 什么是过滤器 #
过滤器是控制器中在动作执行前后自动运行的方法,用于处理跨切面关注点。
1.2 过滤器类型 #
| 类型 | 执行时机 | 用途 |
|---|---|---|
| before_action | 动作执行前 | 认证、授权、数据准备 |
| after_action | 动作执行后 | 日志记录、响应处理 |
| around_action | 动作执行前后 | 性能监控、事务管理 |
二、before_action #
2.1 基本用法 #
ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :require_login
private
def require_login
redirect_to login_path unless current_user
end
end
2.2 限制动作 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
# 只对指定动作执行
before_action :set_article, only: [:show, :edit, :update, :destroy]
# 排除指定动作
before_action :require_admin, except: [:index, :show]
def show
# @article已由过滤器设置
end
private
def set_article
@article = Article.find(params[:id])
end
def require_admin
redirect_to root_path unless current_user&.admin?
end
end
2.3 过滤器顺序 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
# 按定义顺序执行
before_action :require_login
before_action :set_article
before_action :check_permission
private
def require_login
# 第一个执行
end
def set_article
# 第二个执行
end
def check_permission
# 第三个执行
end
end
2.4 条件执行 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
before_action :set_article, if: :article_id_present?
before_action :check_permission, unless: :public_action?
private
def article_id_present?
params[:id].present?
end
def public_action?
action_name.in?(%w[index show])
end
end
三、after_action #
3.1 基本用法 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
after_action :log_access
private
def log_access
AccessLog.create(
user: current_user,
action: action_name,
path: request.path,
ip: request.remote_ip
)
end
end
3.2 响应处理 #
ruby
# app/controllers/api/v1/base_controller.rb
module Api
module V1
class BaseController < ApplicationController
after_action :set_response_headers
private
def set_response_headers
response.set_header('X-Request-Id', request.uuid)
response.set_header('X-Page', params[:page] || 1)
end
end
end
end
3.3 记录性能 #
ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
after_action :record_performance
private
def record_performance
duration = (Time.now - request_start_time) * 1000
Rails.logger.info "Action #{action_name} took #{duration.round(2)}ms"
end
def request_start_time
request.env['action_dispatch.request_start_time'] || Time.now
end
end
四、around_action #
4.1 基本用法 #
ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
around_action :track_time
private
def track_time
start = Time.now
yield # 执行动作
duration = Time.now - start
Rails.logger.info "Request took #{duration}s"
end
end
4.2 事务管理 #
ruby
# app/controllers/orders_controller.rb
class OrdersController < ApplicationController
around_action :wrap_in_transaction, only: [:create, :update]
def create
@order = Order.new(order_params)
# 如果保存失败,事务会自动回滚
@order.save!
redirect_to @order
end
private
def wrap_in_transaction
ActiveRecord::Base.transaction do
yield
end
end
end
4.3 异常处理 #
ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
around_action :handle_exceptions
private
def handle_exceptions
yield
rescue StandardError => e
Rails.logger.error "Error: #{e.message}"
Rails.logger.error e.backtrace.join("\n")
respond_to do |format|
format.html { render file: 'public/500.html', status: :internal_server_error }
format.json { render json: { error: 'Internal Server Error' }, status: :internal_server_error }
end
end
end
五、过滤器继承 #
5.1 继承链 #
ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :require_login
end
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
# 继承require_login过滤器
before_action :set_article, only: [:show, :edit]
end
# app/controllers/admin/articles_controller.rb
module Admin
class ArticlesController < ApplicationController
# 继承require_login过滤器
before_action :require_admin
end
end
5.2 跳过过滤器 #
ruby
# app/controllers/pages_controller.rb
class PagesController < ApplicationController
# 跳过继承的过滤器
skip_before_action :require_login, only: [:index, :show]
end
# app/controllers/api/base_controller.rb
module Api
class BaseController < ApplicationController
# 跳过CSRF保护
skip_before_action :verify_authenticity_token
end
end
六、过滤器最佳实践 #
6.1 认证过滤器 #
ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :authenticate_user!
private
def authenticate_user!
redirect_to login_path unless current_user
end
def current_user
@current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
end
helper_method :current_user
end
6.2 授权过滤器 #
ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :authorize_user!
private
def authorize_user!
return if current_user&.admin?
redirect_to root_path, alert: '您没有权限访问此页面'
end
end
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
before_action :check_article_owner, only: [:edit, :update, :destroy]
private
def check_article_owner
@article = Article.find(params[:id])
redirect_to @article, alert: '您只能编辑自己的文章' unless @article.author == current_user
end
end
6.3 数据准备过滤器 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit, :update, :destroy]
before_action :set_categories, only: [:new, :edit]
private
def set_article
@article = Article.find(params[:id])
end
def set_categories
@categories = Category.all
end
end
七、高级用法 #
7.1 过滤器链控制 #
ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# 在特定过滤器之前执行
prepend_before_action :set_locale
# 在特定过滤器之后执行
append_after_action :log_request
private
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
def log_request
Rails.logger.info "Request: #{request.method} #{request.path}"
end
end
7.2 过滤器条件组合 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit, :update, :destroy]
before_action :check_permission, only: [:edit, :update, :destroy]
before_action :require_admin, if: -> { action_name == 'destroy' }
private
def set_article
@article = Article.find(params[:id])
end
def check_permission
redirect_to @article unless @article.author == current_user
end
def require_admin
redirect_to root_path unless current_user.admin?
end
end
7.3 过滤器类 #
ruby
# lib/filters/authentication_filter.rb
class AuthenticationFilter
def self.before(controller)
unless controller.current_user
controller.redirect_to controller.login_path
false
end
end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action AuthenticationFilter
end
八、过滤器调试 #
8.1 查看过滤器链 #
ruby
# Rails控制台
rails console
# 查看控制器的过滤器链
ArticlesController._process_action_callbacks.map { |c| c.filter if c.kind == :before }.compact
8.2 日志记录 #
ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :log_filter_execution
private
def log_filter_execution
Rails.logger.debug "Executing filter for #{action_name}"
end
end
九、总结 #
9.1 核心要点 #
| 要点 | 说明 |
|---|---|
| before_action | 动作执行前运行 |
| after_action | 动作执行后运行 |
| around_action | 动作执行前后运行 |
| only/except | 限制过滤器作用范围 |
| if/unless | 条件执行过滤器 |
| skip_before_action | 跳过继承的过滤器 |
9.2 下一步 #
现在你已经掌握了过滤器,接下来让我们学习 响应格式,深入了解Rails的多种响应格式处理!
最后更新:2026-03-28