跳过正文

第10篇:Controller 详解

·528 字·3 分钟
xieweicong
作者
xieweicong

学习目标:掌握 ActionController 的核心功能和最佳实践

1. Controller 基础
#

1.1 基本结构
#

class PostsController < ApplicationController
  before_action :set_post, only: [:show, :edit, :update, :destroy]

  def index
    @posts = Post.all
  end

  def show
  end

  def new
    @post = Post.new
  end

  def create
    @post = Post.new(post_params)
    if @post.save
      redirect_to @post, notice: 'Post was successfully created.'
    else
      render :new
    end
  end

  private
    def set_post
      @post = Post.find(params[:id])
    end

    def post_params
      params.require(:post).permit(:title, :content)
    end
end

1.2 Action 执行流程
#

1. before_action 回调
   ↓
2. 执行 action 方法
   ↓
3. 渲染视图或重定向
   ↓
4. after_action 回调

2. Filters(过滤器)
#

2.1 before_action
#

class ApplicationController < ActionController::Base
  before_action :authenticate_user!
  before_action :set_locale

  private
    def authenticate_user!
      redirect_to login_path unless current_user
    end

    def set_locale
      I18n.locale = params[:locale] || I18n.default_locale
    end
end

2.2 skip_before_action
#

class PublicController < ApplicationController
  skip_before_action :authenticate_user!
end

2.3 around_action
#

around_action :benchmark

def benchmark
  start_time = Time.now
  yield
  duration = Time.now - start_time
  logger.info "Action took #{duration}ms"
end

3. 渲染和重定向
#

3.1 render 选项
#

# 渲染模板
render :index
render 'posts/index'
render template: 'posts/index'

# 渲染文本
render plain: "Hello"

# 渲染 JSON
render json: @post
render json: @post, status: :created

# 渲染 XML
render xml: @post

# 渲染文件
render file: "/path/to/file"

# 局部模板
render partial: 'form'
render partial: 'post', collection: @posts

# 状态码
render :new, status: :unprocessable_entity

3.2 redirect_to
#

# 重定向到 URL
redirect_to posts_path
redirect_to @post
redirect_to root_url

# 带状态码
redirect_to @post, status: :moved_permanently

# 带 flash
redirect_to @post, notice: "Success!"
redirect_to @post, alert: "Error!"

# redirect_back
redirect_back(fallback_location: root_path)

4. Strong Parameters
#

4.1 基本用法
#

def post_params
  params.require(:post).permit(:title, :content, :published)
end

# 嵌套参数
def user_params
  params.require(:user).permit(
    :name,
    :email,
    profile_attributes: [:bio, :avatar],
    addresses_attributes: [:street, :city, :_destroy]
  )
end

# 数组参数
def post_params
  params.require(:post).permit(:title, tag_ids: [])
end

4.2 自定义清理
#

def user_params
  permitted = params.require(:user).permit(:name, :email)
  permitted[:email] = permitted[:email].downcase if permitted[:email]
  permitted
end

5. Rescue 错误处理
#

5.1 rescue_from
#

class ApplicationController < ActionController::Base
  rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
  rescue_from User::NotAuthorized, with: :user_not_authorized

  private
    def record_not_found
      render plain: "404 Not Found", status: 404
    end

    def user_not_authorized
      flash[:error] = "You are not authorized to perform this action."
      redirect_to root_path
    end
end

6. HTTP 认证
#

6.1 Basic 认证
#

class AdminController < ApplicationController
  http_basic_authenticate_with name: "admin", password: "secret"
end

6.2 Token 认证
#

before_action :authenticate_with_token

def authenticate_with_token
  authenticate_or_request_with_http_token do |token, options|
    User.find_by(api_token: token)
  end
end

7. Streaming
#

7.1 文件下载
#

def download
  send_file '/path/to/file.pdf',
    filename: 'document.pdf',
    type: 'application/pdf',
    disposition: 'attachment'
end

# 发送数据
def export
  send_data generate_csv(@posts),
    filename: 'posts.csv',
    type: 'text/csv'
end

7.2 实时流式传输
#

def stream
  response.headers['Content-Type'] = 'text/event-stream'
  10.times do |i|
    response.stream.write "data: #{i}\n\n"
    sleep 1
  end
ensure
  response.stream.close
end

8. 控制器组织
#

8.1 Concerns
#

# app/controllers/concerns/authenticable.rb
module Authenticable
  extend ActiveSupport::Concern

  included do
    before_action :authenticate_user!
  end

  private
    def authenticate_user!
      redirect_to login_path unless current_user
    end

    def current_user
      @current_user ||= User.find_by(id: session[:user_id])
    end
end

# 使用
class PostsController < ApplicationController
  include Authenticable
end

9. 测试
#

9.1 Controller 测试
#

class PostsControllerTest < ActionDispatch::IntegrationTest
  test "should get index" do
    get posts_url
    assert_response :success
  end

  test "should create post" do
    assert_difference('Post.count') do
      post posts_url, params: { post: { title: "Test", content: "Content" } }
    end
    assert_redirected_to post_url(Post.last)
  end
end

10. 本章总结
#

ActionController 提供了:

  • Filters 和回调系统
  • 渲染和重定向
  • Strong Parameters 安全参数过滤
  • 错误处理
  • HTTP 认证
  • 文件下载和流式传输

上一篇路由系统深入 下一篇ActionView 渲染 返回学习指南首页