Phoenix路由定义Skill phoenix-routing

该技能专注于在Phoenix Web框架中定义和管理路由,包括基本路由声明、资源路由、作用域、管道和验证路由。它帮助开发者高效地映射HTTP请求到控制器动作,构建RESTful API,并确保路由的安全性和可维护性。关键词:Phoenix路由、Elixir、Web开发、后端开发、RESTful API、路由配置、编译时验证。

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

名称: phoenix-routing 用户可调用: false 描述: 在Phoenix应用程序中定义路由和URL助手,包括资源、作用域、管道和验证路由 允许工具: [Bash, Read]

Phoenix路由

Phoenix路由将传入的HTTP请求映射到控制器动作。路由器是所有Web请求的入口点,决定哪个控制器动作应处理每个请求。Phoenix提供了强大的路由宏,用于RESTful资源、作用域、管道和验证路由。

基本路由声明

单一路由

使用HTTP动词宏定义单个路由:

get "/", PageController, :home

Phoenix支持所有标准HTTP动词:

get "/users", UserController, :index
post "/users", UserController, :create
patch "/users/:id", UserController, :update
put "/users/:id", UserController, :update
delete "/users/:id", UserController, :delete

带有动态段的路由

使用:param_name语法捕获URL参数:

get "/hello/:messenger", HelloController, :show

:messenger段在控制器的参数字典中变为可用。

资源路由

基本资源声明

使用resources宏生成所有标准RESTful路由:

resources "/users", UserController

这会生成八条路由:

GET     /users           UserController :index
GET     /users/:id/edit  UserController :edit
GET     /users/new       UserController :new
GET     /users/:id       UserController :show
POST    /users           UserController :create
PATCH   /users/:id       UserController :update
PUT     /users/:id       UserController :update
DELETE  /users/:id       UserController :delete

限制资源路由

使用:only生成特定路由:

resources "/users", UserController, only: [:show]
resources "/posts", PostController, only: [:index, :show]

使用:except排除特定路由:

resources "/users", UserController, except: [:create, :delete]

别名资源

使用:as自定义路由路径助手名称:

resources "/users", UserController, as: :person

这会生成路径助手如~p"/person",而不是~p"/users"

嵌套资源

创建分层资源关系:

resources "/users", UserController do
  resources "/posts", PostController
end

生成的路由包括父资源ID:

GET     /users/:user_id/posts           PostController :index
GET     /users/:user_id/posts/:id/edit  PostController :edit
GET     /users/:user_id/posts/new       PostController :new
GET     /users/:user_id/posts/:id       PostController :show
POST    /users/:user_id/posts           PostController :create
PATCH   /users/:user_id/posts/:id       PostController :update
PUT     /users/:user_id/posts/:id       PostController :update
DELETE  /users/:user_id/posts/:id       PostController :delete

验证路由

使用~p符咒

Phoenix提供编译时验证路由,使用~p符咒:

# 静态路径
~p"/users"
~p"/posts/new"

# 带有变量的动态段
~p"/users/#{user_id}"
~p"/users/#{user_id}/posts/#{post_id}"

验证路由与结构体

直接传递结构体以生成路径:

~p"/users/#{@user}"
# 生成:"/users/42"

~p"/users/#{user}/posts/#{post}"
# 生成:"/users/42/posts/17"

Phoenix自动使用Phoenix.Param协议提取ID。

验证路由的好处

  1. 编译时验证 - 在编译时捕获路由错误
  2. 重构安全性 - 路由更改立即被捕获
  3. 类型安全 - 确保正确的参数类型
  4. URL段支持 - 轻松过渡到基于段的路由

作用域

基本作用域

在公共路径前缀下分组路由:

scope "/admin", HelloWeb.Admin do
  pipe_through :browser

  resources "/users", UserController
end

生成的路径包括作用域前缀:

~p"/admin/users"

带有别名的作用域

通过别名控制器模块减少重复:

scope "/", HelloWeb do
  pipe_through :browser

  get "/", PageController, :home
  resources "/posts", PostController
end

嵌套作用域

创建分层路由组织:

scope "/api", HelloWeb.Api, as: :api do
  pipe_through :api

  scope "/v1", V1, as: :v1 do
    resources "/users", UserController
  end

  scope "/v2", V2, as: :v2 do
    resources "/users", UserController
  end
end

生成的路径助手反映嵌套:

~p"/api/v1/users"
~p"/api/v2/users"

管道

定义管道

管道分组为特定路由运行的插头:

pipeline :browser do
  plug :accepts, ["html"]
  plug :fetch_session
  plug :fetch_live_flash
  plug :put_root_layout, html: {HelloWeb.Layouts, :root}
  plug :protect_from_forgery
  plug :put_secure_browser_headers
end

pipeline :api do
  plug :accepts, ["json"]
end

将管道应用于作用域

使用pipe_through应用管道:

scope "/", HelloWeb do
  pipe_through :browser

  get "/", PageController, :home
  resources "/users", UserController
end

scope "/api", HelloWeb.Api do
  pipe_through :api

  resources "/users", UserController
end

管道中的自定义插头

添加应用特定插头:

pipeline :browser do
  plug :accepts, ["html"]
  plug :fetch_session
  plug :fetch_live_flash
  plug :put_root_layout, html: {HelloWeb.Layouts, :root}
  plug :protect_from_forgery
  plug :put_secure_browser_headers
  plug HelloWeb.Plugs.Locale, "en"
end

嵌套管道

为复杂身份验证流组合管道:

pipeline :auth do
  plug :browser
  plug :ensure_authenticated_user
  plug :ensure_user_owns_review
end

scope "/reviews", HelloWeb do
  pipe_through :auth

  resources "/", ReviewController
end

这首先应用:browser管道,然后是身份验证插头。

高级管道模式

会话管理管道

为基于会话的功能创建管道:

pipeline :browser do
  plug :accepts, ["html"]
  plug :fetch_session
  plug :fetch_live_flash
  plug :put_root_layout, html: {HelloWeb.Layouts, :root}
  plug :protect_from_forgery
  plug :put_secure_browser_headers
  plug :fetch_current_scope_for_user
end

defp fetch_current_scope_for_user(conn, _opts) do
  if id = get_session(conn, :scope_id) do
    assign(conn, :current_scope, MyApp.Scope.for_id(id))
  else
    id = System.unique_integer()

    conn
    |> put_session(:scope_id, id)
    |> assign(:current_scope, MyApp.Scope.for_id(id))
  end
end

多租户路由

从URL参数分配组织上下文:

pipeline :browser do
  plug :accepts, ["html"]
  plug :fetch_session
  plug :fetch_live_flash
  plug :put_root_layout, html: {HelloWeb.Layouts, :root}
  plug :protect_from_forgery
  plug :put_secure_browser_headers
  plug :fetch_current_scope_for_user
  plug :assign_org_to_scope
end

defp assign_org_to_scope(conn, _opts) do
  case conn.params["org"] do
    nil -> conn
    org_slug ->
      scope = conn.assigns.current_scope
      org = MyApp.Organizations.get_by_slug!(org_slug)
      assign(conn, :current_scope, Map.put(scope, :organization, org))
  end
end

购物车管道

为当前会话获取或创建购物车:

pipeline :browser do
  plug :accepts, ["html"]
  plug :fetch_session
  plug :fetch_live_flash
  plug :put_root_layout, html: {HelloWeb.Layouts, :root}
  plug :protect_from_forgery
  plug :put_secure_browser_headers
  plug :fetch_current_scope_for_user
  plug :fetch_current_cart
end

alias MyApp.ShoppingCart

defp fetch_current_cart(%{assigns: %{current_scope: scope}} = conn, _opts)
    when not is_nil(scope) do
  if cart = ShoppingCart.get_cart(scope) do
    assign(conn, :cart, cart)
  else
    {:ok, new_cart} = ShoppingCart.create_cart(scope, %{})
    assign(conn, :cart, new_cart)
  end
end

defp fetch_current_cart(conn, _opts), do: conn

转发

转发到插头

将路径前缀委托给另一个插头或应用:

defmodule HelloWeb.Router do
  use HelloWeb, :router

  scope "/", HelloWeb do
    pipe_through :browser
    get "/", PageController, :home
  end

  forward "/jobs", BackgroundJob.Plug
end

所有到/jobs/*的请求由BackgroundJob.Plug处理。

常见转发用例

# 管理界面
forward "/admin", HelloWeb.AdminRouter

# API文档
forward "/api/docs", PhoenixSwagger.Plug.SwaggerUI

# 后台作业仪表板
forward "/jobs", Oban.Web.Router

检查路由

使用mix phx.routes

查看应用中所有定义的路由:

mix phx.routes

输出显示HTTP动词、路径、控制器和动作:

GET     /                HelloWeb.PageController :home
GET     /users           HelloWeb.UserController :index
GET     /users/:id/edit  HelloWeb.UserController :edit
GET     /users/new       HelloWeb.UserController :new
GET     /users/:id       HelloWeb.UserController :show
POST    /users           HelloWeb.UserController :create
PATCH   /users/:id       HelloWeb.UserController :update
PUT     /users/:id       HelloWeb.UserController :update
DELETE  /users/:id       HelloWeb.UserController :delete

过滤路由

Grep特定路由:

mix phx.routes | grep users
mix phx.routes | grep POST

以编程方式构建路径

静态路径

轻松构建静态路径:

~p"/users"
# 返回:"/users"

~p"/posts/new"
# 返回:"/posts/new"

带有整数ID的路径

直接插值ID:

user_id = 42
post_id = 17
~p"/users/#{user_id}/posts/#{post_id}"
# 返回:"/users/42/posts/17"

带有结构体的路径

让Phoenix从结构体提取ID:

~p"/users/#{user}/posts/#{post}"
# 返回:"/users/42/posts/17"

这使用Phoenix.Param协议提取ID。

自定义参数实现

为结构体实现自定义URL生成:

defimpl Phoenix.Param, for: MyApp.Blog.Post do
  def to_param(%{slug: slug}), do: slug
end

# 现在生成基于段的路由:
~p"/posts/#{post}"
# 返回:"/posts/my-great-post"

路由器配置示例

完整路由器设置

典型的Phoenix路由器包括多个管道和作用域:

defmodule HelloWeb.Router do
  use HelloWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {HelloWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", HelloWeb do
    pipe_through :browser

    get "/", PageController, :home
    get "/hello", HelloController, :index
    get "/hello/:messenger", HelloController, :show
  end

  scope "/api/v1", HelloWeb.Api.V1, as: :api_v1 do
    pipe_through :api

    resources "/users", UserController, only: [:index, :show]
  end

  # 管理界面
  scope "/admin", HelloWeb.Admin, as: :admin do
    pipe_through [:browser, :admin_auth]

    resources "/users", UserController
    resources "/posts", PostController
  end

  # 在开发中启用LiveDashboard
  if Mix.env() in [:dev, :test] do
    import Phoenix.LiveDashboard.Router

    scope "/" do
      pipe_through :browser
      live_dashboard "/dashboard", metrics: HelloWeb.Telemetry
    end
  end
end

何时使用此技能

在以下情况下使用此技能:

  1. 为控制器和动作定义新路由
  2. 为CRUD操作创建RESTful资源路由
  3. 使用作用域和命名空间组织路由
  4. 构建嵌套资源关系
  5. 配置请求处理管道
  6. 在控制器和模板中生成验证路由路径
  7. 使用作用域路由实现API版本控制
  8. 调试路由问题和检查可用路由
  9. 将请求转发到外部插头或应用
  10. 实现自定义URL段生成
  11. 设置身份验证和授权管道
  12. 创建多租户路由架构
  13. 构建带有单独作用域的管理界面
  14. 配置不同响应格式(HTML、JSON等)

最佳实践

  1. 使用验证路由 - 始终使用~p符咒以确保编译时安全性
  2. 分组相关路由 - 使用作用域逻辑地组织路由
  3. 限制资源动作 - 只生成实际需要的路由
  4. 明确命名作用域 - 使用描述性作用域前缀和别名
  5. 保持管道专注 - 每个管道应具有单一职责
  6. 仔细排序路由 - 更具体的路由应放在通用路由之前
  7. 使用资源 - 优先使用resources而非单个路由声明
  8. 记录自定义路由 - 为非标准路由模式添加注释
  9. 避免深度嵌套 - 限制嵌套资源到2-3层
  10. 版本化API - 使用作用域进行API版本控制
  11. 保护敏感路由 - 适当应用身份验证管道
  12. 测试路由解析 - 验证路由解析到正确控制器
  13. 明智使用转发 - 转发到良好定义的插头接口
  14. 定期检查 - 在开发中使用mix phx.routes
  15. 遵循约定 - 为资源坚持RESTful约定

常见陷阱

  1. 硬编码路径 - 使用字符串而非验证路由
  2. 过度嵌套资源 - 创建深度嵌套的资源层次
  3. 缺少管道 - 忘记通过所需管道路由
  4. 错误的路由顺序 - 通用路由捕获特定路由请求
  5. 暴露所有动作 - 生成不必要的CRUD路由
  6. 不使用作用域 - 重复控制器模块前缀
  7. 命名不一致 - 混合路由命名约定
  8. 跳过CSRF保护 - 移除安全插头而不了解影响
  9. 缺少身份验证 - 不保护敏感路由
  10. 重复路由 - 在多个地方定义相同路由
  11. 错误的HTTP动词 - 为动作使用错误的动词(例如用GET进行破坏性操作)
  12. 不测试路由 - 未验证路由配置
  13. 暴露内部路由 - 在生产中使调试/管理路由可用
  14. 复杂路由逻辑 - 在路由定义中放入业务逻辑
  15. 忽略路由冲突 - 不检查重叠路由模式

资源