Phoenix控制器Skill phoenix-controllers

Phoenix 控制器是 Phoenix 框架中的核心模块,用于处理 HTTP 请求、管理参数、渲染响应、设置 flash 消息和执行重定向,适用于后端开发,支持 Elixir 语言和 Web 应用构建。关键词:Phoenix、控制器、HTTP 请求、Elixir、Web 开发、后端开发、框架、路由、参数处理、flash 消息、重定向。

后端开发 1 次安装 4 次浏览 更新于 3/25/2026

名称: phoenix-controllers 用户可调用: false 描述: 使用 Phoenix 控制器处理 HTTP 请求,包括动作、参数、渲染、flash 消息和重定向 允许工具: [Bash, Read]

Phoenix 控制器

Phoenix 控制器是 Phoenix 应用程序中路由器和视图之间的中间模块。它们处理 HTTP 请求、处理参数、与上下文交互,并决定向客户端发送什么响应。控制器是无状态的,并接收一个表示当前 HTTP 请求的连接结构(conn)。

基本控制器结构

最简单的 Phoenix 控制器使用 HelloWeb, :controller 宏,并定义接收连接和参数的函数作为动作:

defmodule HelloWeb.PageController do
  use HelloWeb, :controller

  def home(conn, _params) do
    render(conn, :home, layout: false)
  end
end

每个控制器动作接收:

  • conn - 表示 HTTP 请求/响应的 Plug.Conn 结构
  • params - 一个包含来自 URL、查询字符串和请求体的请求参数的映射

控制器动作和参数处理

提取特定参数

使用模式匹配从 params 映射中提取特定参数:

defmodule HelloWeb.HelloController do
  use HelloWeb, :controller

  def show(conn, %{"messenger" => messenger}) do
    render(conn, :show, messenger: messenger)
  end
end

访问完整参数映射

要访问特定参数和完整 params 映射,使用 = 运算符进行模式匹配:

def show(conn, %{"messenger" => messenger} = params) do
  # 同时访问 messenger 和完整 params 映射
  render(conn, :show, messenger: messenger)
end

忽略参数

当动作不需要参数时,在变量前加下划线以避免编译器警告:

def index(conn, _params) do
  render(conn, :home)
end

重命名动作

可以独立于模板名称自定义动作名称:

defmodule HelloWeb.PageController do
  use HelloWeb, :controller

  def index(conn, _params) do
    # 渲染 :home 模板,但动作命名为 :index
    render(conn, :home)
  end
end

相应更新路由器:

get "/", PageController, :index

渲染响应

渲染 HTML 模板

使用 render/3 函数通过 Phoenix 视图渲染 HTML 模板:

defmodule HelloWeb.HelloController do
  use HelloWeb, :controller

  def show(conn, %{"messenger" => messenger}) do
    render(conn, :show, messenger: messenger)
  end
end

控制器和视图必须共享根名称(例如,HelloControllerHelloHTML)。

渲染无布局

为特定动作禁用布局:

def home(conn, _params) do
  render(conn, :home, layout: false)
end

分配值到模板

使用 assign/3

使用 assign/3 将数据传递给模板:

def show(conn, %{"messenger" => messenger}) do
  conn
  |> assign(:messenger, messenger)
  |> render(:show)
end

链式多个分配

链式多个分配以使代码更清晰:

def show(conn, %{"messenger" => messenger}) do
  conn
  |> assign(:messenger, messenger)
  |> assign(:receiver, "Dweezil")
  |> render(:show)
end

直接将分配传递给 render/3

为简洁语法,直接将分配传递给 render/3

def show(conn, %{"messenger" => messenger}) do
  render(conn, :show, messenger: messenger, receiver: "Dweezil")
end

配置控制器格式

在控制器配置中配置支持的响应格式:

def controller do
  quote do
    use Phoenix.Controller,
      formats: [:html, :json]
    ...
  end
end

这允许控制器根据请求以不同格式响应。

Flash 消息

设置 Flash 消息

使用 put_flash/3 设置临时消息:

defmodule HelloWeb.PageController do
  use HelloWeb, :controller

  def home(conn, _params) do
    conn
    |> put_flash(:error, "让我们假装有一个错误。")
    |> render(:home, layout: false)
  end
end

Flash 消息类型:

  • :info - 信息性消息
  • :error - 错误消息
  • :warning - 警告消息(自定义)
  • :success - 成功消息(自定义)

清除 Flash 消息

从连接中移除所有 flash 消息:

clear_flash(conn)

带重定向的 Flash

将 flash 消息与重定向结合,以在导航后提供上下文:

def home(conn, _params) do
  conn
  |> put_flash(:error, "让我们假装有一个错误。")
  |> redirect(to: ~p"/redirect_test")
end

重定向

基本重定向

使用已验证的路由语法重定向到另一条路由:

def create(conn, params) do
  # ... 创建逻辑
  redirect(conn, to: ~p"/posts")
end

带参数的重定向

在重定向中包含动态参数:

def create(conn, params) do
  # ... 创建帖子
  redirect(conn, to: ~p"/posts/#{post}")
end

外部重定向

重定向到外部 URL:

def external(conn, _params) do
  redirect(conn, external: "https://example.com")
end

动作回退

动作回退控制器通过集中错误处理逻辑优雅地处理错误情况:

defmodule HelloWeb.MyController do
  use Phoenix.Controller

  action_fallback HelloWeb.MyFallbackController

  def show(conn, %{"id" => id}, current_user) do
    with {:ok, post} <- fetch_post(id),
         :ok <- authorize_user(current_user, :view, post) do
      render(conn, :show, post: post)
    end
  end
end

回退控制器处理非 ok 元组:

defmodule HelloWeb.MyFallbackController do
  use Phoenix.Controller

  def call(conn, {:error, :not_found}) do
    conn
    |> put_status(:not_found)
    |> put_view(HelloWeb.ErrorHTML)
    |> render(:"404")
  end

  def call(conn, {:error, :unauthorized}) do
    conn
    |> put_status(403)
    |> put_view(HelloWeb.ErrorHTML)
    |> render(:"403")
  end
end

测试控制器

HTML 控制器测试

使用 ConnCase 测试控制器动作:

defmodule HelloWeb.PostControllerTest do
  use HelloWeb.ConnCase

  import Hello.BlogFixtures

  @create_attrs %{body: "一些内容", title: "一些标题"}
  @update_attrs %{body: "一些更新内容", title: "一些更新标题"}
  @invalid_attrs %{body: nil, title: nil}

  describe "index" do
    test "列出所有帖子", %{conn: conn} do
      conn = get(conn, ~p"/posts")
      assert html_response(conn, 200) =~ "帖子列表"
    end
  end

  describe "show" do
    test "显示单个帖子", %{conn: conn} do
      post = post_fixture()
      conn = get(conn, ~p"/posts/#{post}")
      assert html_response(conn, 200) =~ post.title
    end
  end
end

测试重定向

在测试中断言重定向:

test "创建后重定向", %{conn: conn} do
  conn = post(conn, ~p"/posts", post: @create_attrs)
  assert %{id: id} = redirected_params(conn)
  assert redirected_to(conn) == ~p"/posts/#{id}"
end

测试 Flash 消息

验证 flash 消息是否正确设置:

test "错误时设置 flash 消息", %{conn: conn} do
  conn = post(conn, ~p"/posts", post: @invalid_attrs)
  assert get_flash(conn, :error) == "无法创建帖子"
end

何时使用此技能

在需要以下情况时使用此技能:

  1. 创建新的控制器动作以处理 HTTP 请求
  2. 处理和验证传入的请求参数
  3. 使用动态数据渲染 HTML 模板
  4. 通过 flash 消息实现用户反馈
  5. 处理表单提交或操作后的重定向
  6. 测试控制器行为和响应
  7. 使用动作回退实现错误处理
  8. 构建 RESTful API 端点
  9. 将控制器动作与 Phoenix 上下文集成
  10. 管理会话数据和用户身份验证流程
  11. 处理文件上传和多部分表单
  12. 为列表视图实现分页
  13. 创建自定义响应格式(JSON、XML 等)
  14. 调试请求/响应周期

最佳实践

  1. 保持控制器精简 - 将业务逻辑移动到上下文,保持控制器专注于 HTTP 相关事务
  2. 使用模式匹配 - 仅从 params 映射中提取所需的参数
  3. 早期验证 - 在传递给上下文之前在控制器级别验证参数
  4. 使用动作回退 - 使用动作回退控制器集中错误处理
  5. 利用管道 - 使用 plugs 进行常见操作,如身份验证和授权
  6. 明确分配 - 仅传递必要数据到模板,以避免暴露敏感信息
  7. 使用已验证的路由 - 始终使用 ~p 符号表示路由路径,以便在编译时捕获错误
  8. 全面测试 - 为所有控制器动作和边缘情况编写全面的测试
  9. 优雅处理错误 - 提供有意义的错误消息和适当的 HTTP 状态码
  10. 明智使用 flash 消息 - 在操作后向用户提供清晰、可操作的反馈
  11. 避免业务逻辑 - 控制器应协调,而不是实现业务规则
  12. 早期返回 - 使用保护子句和早期返回以使代码更清晰
  13. 一致的命名 - 遵循 Phoenix 的动作命名约定(index、show、new、create、edit、update、delete)
  14. 记录复杂动作 - 为非显而易见的控制器逻辑添加注释
  15. 使用结构体而非映射 - 尽可能使用来自上下文的结构体,而不是原始参数映射

常见陷阱

  1. 将业务逻辑放入控制器 - 这使代码更难测试和重用
  2. 未验证参数 - 始终验证和清理用户输入
  3. 过度使用分配 - 传递整个上下文模块或大型数据结构到视图
  4. 忽略错误情况 - 未正确处理来自上下文函数的错误
  5. 硬编码路径 - 使用字符串路径而不是已验证的路由(~p
  6. 未使用动作回退 - 在多个动作中重复错误处理逻辑
  7. 测试实现细节 - 测试行为,而不是内部实现
  8. 暴露内部错误 - 向用户泄露堆栈跟踪或内部错误
  9. 未设置状态码 - 忘记为错误设置适当的 HTTP 状态码
  10. 突变参数 - 尝试修改 params 映射而不是使用分配
  11. 跳过 CSRF 保护 - 在不理解影响的情况下禁用安全功能
  12. 大型控制器文件 - 创建整体控制器而不是拆分关注点
  13. 未使用 with 语句 - 错过使用 with 语句干净地链式操作的机会
  14. 忘记内容协商 - 未适当处理不同的响应格式
  15. 混淆关注点 - 在同一控制器中处理 API 和 HTML 响应而没有适当分离

资源