Rails控制器基础 #

一、控制器概述 #

1.1 什么是控制器 #

控制器是MVC架构中的C,负责处理用户请求、调用模型处理数据、选择视图渲染响应。

text
请求流程:
请求 → 路由 → 控制器 → 模型 → 视图 → 响应

1.2 控制器职责 #

职责 说明
接收请求 解析请求参数
调用模型 处理业务逻辑
准备数据 为视图准备实例变量
渲染响应 返回HTML/JSON等

二、创建控制器 #

2.1 使用生成器 #

bash
# 创建控制器
rails generate controller Articles

# 创建带动作的控制器
rails generate controller Articles index show

# 创建命名空间控制器
rails generate controller Admin::Users

# 撤销生成
rails destroy controller Articles

2.2 手动创建 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def index
    @articles = Article.all
  end
  
  def show
    @article = Article.find(params[:id])
  end
end

2.3 控制器继承 #

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

# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  # 继承ApplicationController
  # 自动拥有require_login过滤器
end

三、控制器动作 #

3.1 定义动作 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  # index动作
  def index
    @articles = Article.all
  end
  
  # show动作
  def show
    @article = Article.find(params[:id])
  end
  
  # new动作
  def new
    @article = Article.new
  end
  
  # create动作
  def create
    @article = Article.new(article_params)
    
    if @article.save
      redirect_to @article, notice: '创建成功'
    else
      render :new, status: :unprocessable_entity
    end
  end
  
  # edit动作
  def edit
    @article = Article.find(params[:id])
  end
  
  # update动作
  def update
    @article = Article.find(params[:id])
    
    if @article.update(article_params)
      redirect_to @article, notice: '更新成功'
    else
      render :edit, status: :unprocessable_entity
    end
  end
  
  # destroy动作
  def destroy
    @article = Article.find(params[:id])
    @article.destroy
    redirect_to articles_url, notice: '删除成功'
  end
  
  private
  
  def article_params
    params.require(:article).permit(:title, :body)
  end
end

3.2 动作命名规范 #

动作名 用途 HTTP方法
index 列表 GET
show 详情 GET
new 新建表单 GET
create 创建 POST
edit 编辑表单 GET
update 更新 PATCH/PUT
destroy 删除 DELETE

四、参数处理 #

4.1 获取参数 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def show
    # 获取路由参数
    @article = Article.find(params[:id])
    
    # 获取查询参数
    # /articles?category=rails&page=2
    @category = params[:category]
    @page = params[:page]
  end
  
  def create
    # 获取表单参数
    # params[:article] => { title: '...', body: '...' }
    @article = Article.new(params[:article])  # 不推荐
    
    # 使用Strong Parameters
    @article = Article.new(article_params)
  end
  
  private
  
  def article_params
    params.require(:article).permit(:title, :body, :status)
  end
end

4.2 Strong Parameters #

ruby
# 基本用法
params.require(:article).permit(:title, :body)

# 允许嵌套参数
params.require(:article).permit(:title, :body, comments: [:id, :body])

# 允许所有参数(不推荐)
params.require(:article).permit!

# 条件性require
params.fetch(:article, {}).permit(:title, :body)

# 多个require
params.permit(:id, :title)

4.3 参数类型转换 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def index
    # 转换为整数
    @page = params[:page].to_i
    
    # 转换为布尔值
    @published = ActiveModel::Type::Boolean.new.cast(params[:published])
    
    # 转换为数组
    @tags = params[:tags].to_a
    
    # 默认值
    @per_page = params[:per_page].presence || 10
  end
end

五、响应渲染 #

5.1 渲染视图 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    
    # 自动渲染 app/views/articles/show.html.erb
  end
  
  def custom
    # 渲染指定视图
    render 'articles/special'
    
    # 渲染其他动作的视图
    render :show
    
    # 渲染其他控制器的视图
    render 'users/profile'
    
    # 渲染绝对路径
    render file: Rails.root.join('public', '404.html')
  end
end

5.2 渲染选项 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    
    # 渲染HTML
    render html: '<h1>Hello</h1>'.html_safe
    
    # 渲染纯文本
    render plain: 'Hello World'
    
    # 渲染JSON
    render json: @article
    
    # 渲染XML
    render xml: @article
    
    # 渲染JavaScript
    render js: "alert('Hello')"
    
    # 渲染内联模板
    render inline: '<% @articles.each do |a| %><p><%= a.title %></p><% end %>'
  end
end

5.3 响应状态码 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def create
    @article = Article.new(article_params)
    
    if @article.save
      render :show, status: :created
    else
      render :new, status: :unprocessable_entity
    end
  end
end

5.4 常用状态码 #

状态码 符号 说明
200 :ok 成功
201 :created 创建成功
204 :no_content 无内容
301 :moved_permanently 永久重定向
302 :found 临时重定向
400 :bad_request 错误请求
401 :unauthorized 未授权
403 :forbidden 禁止访问
404 :not_found 未找到
422 :unprocessable_entity 无法处理
500 :internal_server_error 服务器错误

六、重定向 #

6.1 基本重定向 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def create
    @article = Article.new(article_params)
    
    if @article.save
      # 重定向到show动作
      redirect_to @article
      
      # 重定向到指定路径
      redirect_to articles_path
      
      # 重定向到根路径
      redirect_to root_path
      
      # 重定向到上一个页面
      redirect_back fallback_location: root_path
    end
  end
end

6.2 重定向选项 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def update
    @article = Article.find(params[:id])
    
    if @article.update(article_params)
      # 带notice
      redirect_to @article, notice: '更新成功'
      
      # 带alert
      redirect_to @article, alert: '更新失败'
      
      # 带状态码
      redirect_to @article, status: :moved_permanently
      
      # 允许外部重定向
      redirect_to 'https://example.com', allow_other_host: true
    end
  end
end

七、实例变量 #

7.1 定义实例变量 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def index
    # 实例变量可以在视图中访问
    @articles = Article.all
    @featured = Article.featured
    @categories = Category.all
  end
  
  def show
    @article = Article.find(params[:id])
    @comments = @article.comments
    @related = Article.related(@article)
  end
end

7.2 视图中使用 #

erb
<!-- app/views/articles/index.html.erb -->
<h1>文章列表</h1>

<% @articles.each do |article| %>
  <div class="article">
    <h2><%= article.title %></h2>
    <p><%= truncate(article.body, length: 100) %></p>
  </div>
<% end %>

<% @categories.each do |category| %>
  <%= link_to category.name, category_path(category) %>
<% end %>

八、Flash消息 #

8.1 设置Flash #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def create
    @article = Article.new(article_params)
    
    if @article.save
      # 设置flash消息
      flash[:notice] = '文章创建成功'
      redirect_to @article
      
      # 或使用redirect_to的选项
      redirect_to @article, notice: '文章创建成功'
      redirect_to @article, alert: '创建失败'
    end
  end
end

8.2 Flash类型 #

ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  # 添加自定义flash类型
  add_flash_types :success, :warning, :danger, :info
end

# 使用
redirect_to @article, success: '操作成功'
redirect_to @article, warning: '请注意'
redirect_to @article, danger: '操作失败'

8.3 显示Flash #

erb
<!-- app/views/layouts/application.html.erb -->
<% if flash.any? %>
  <div class="flash-messages">
    <% flash.each do |type, message| %>
      <div class="alert alert-<%= type %>">
        <%= message %>
      </div>
    <% end %>
  </div>
<% end %>

8.4 Flash.now #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def create
    @article = Article.new(article_params)
    
    if @article.save
      redirect_to @article, notice: '创建成功'
    else
      # 当前请求显示(不用于重定向)
      flash.now[:alert] = '创建失败'
      render :new, status: :unprocessable_entity
    end
  end
end

九、请求对象 #

9.1 获取请求信息 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def index
    # 请求方法
    request.method          # => "GET"
    request.get?            # => true
    request.post?           # => false
    
    # 请求路径
    request.path            # => "/articles"
    request.fullpath        # => "/articles?page=2"
    request.url             # => "http://example.com/articles?page=2"
    
    # 请求头
    request.headers['User-Agent']
    request.headers['Accept']
    
    # IP地址
    request.remote_ip       # => "192.168.1.1"
    
    # 格式
    request.format          # => :html
    request.json?           # => false
  end
end

9.2 响应对象 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    
    respond_to do |format|
      format.html
      format.json { render json: @article }
      format.xml  { render xml: @article }
      format.pdf  { render pdf: 'article' }
    end
  end
end

十、控制器最佳实践 #

10.1 瘦控制器 #

ruby
# 不推荐:控制器中包含业务逻辑
class ArticlesController < ApplicationController
  def create
    @article = Article.new(params[:article])
    @article.status = 'draft'
    @article.author = current_user
    @article.published_at = Time.now if params[:publish]
    
    if @article.save
      NotificationMailer.new_article(@article).deliver_later
      redirect_to @article
    end
  end
end

# 推荐:业务逻辑放在模型中
class ArticlesController < ApplicationController
  def create
    @article = current_user.articles.build(article_params)
    
    if @article.save
      redirect_to @article, notice: '创建成功'
    else
      render :new, status: :unprocessable_entity
    end
  end
end

# app/models/article.rb
class Article < ApplicationRecord
  belongs_to :author, class_name: 'User'
  
  before_create :set_defaults
  after_create :send_notification
  
  private
  
  def set_defaults
    self.status ||= 'draft'
    self.published_at = Time.now if publish?
  end
  
  def send_notification
    NotificationMailer.new_article(self).deliver_later
  end
end

10.2 使用私有方法 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  before_action :set_article, only: [:show, :edit, :update, :destroy]
  
  def show
  end
  
  def edit
  end
  
  def update
    if @article.update(article_params)
      redirect_to @article, notice: '更新成功'
    else
      render :edit, status: :unprocessable_entity
    end
  end
  
  private
  
  def set_article
    @article = Article.find(params[:id])
  end
  
  def article_params
    params.require(:article).permit(:title, :body, :status)
  end
end

10.3 错误处理 #

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
  rescue ActiveRecord::RecordNotFound
    redirect_to articles_path, alert: '文章不存在'
  end
end

# 或使用rescue_from
class ApplicationController < ActionController::Base
  rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
  
  private
  
  def record_not_found
    render file: Rails.root.join('public', '404.html'), status: :not_found
  end
end

十一、总结 #

11.1 核心要点 #

要点 说明
控制器职责 处理请求、调用模型、渲染响应
动作定义 公共方法即动作
参数处理 使用Strong Parameters
响应渲染 render和redirect_to
Flash消息 跨请求通知

11.2 下一步 #

现在你已经掌握了控制器基础,接下来让我们学习 参数处理,深入了解Rails的参数处理机制!

最后更新:2026-03-28