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