查看源代码 Phoenix.Controller (Phoenix v1.7.14)

控制器用于将相同(可插拔)模块中的通用功能分组。

例如,路由

get "/users/:id", MyAppWeb.UserController, :show

将调用 MyAppWeb.UserController 中的 show/2 动作。

defmodule MyAppWeb.UserController do
  use MyAppWeb, :controller

  def show(conn, %{"id" => id}) do
    user = Repo.get(User, id)
    render(conn, :show, user: user)
  end
end

动作是一个常规函数,它接收连接和请求参数作为参数。连接是一个 Plug.Conn 结构,如 Plug 库所指定。

然后我们调用 render/3,将连接、要渲染的模板(通常以动作命名)和 user: user 作为分配传递。我们将在接下来探索所有这些概念。

连接

控制器默认情况下提供了许多用于操作连接、渲染模板等的操作。

这些函数从两个模块导入

  • Plug.Conn - 一组用于操作连接的低级函数

  • Phoenix.Controller - Phoenix 提供的函数,用于支持渲染和其他 Phoenix 特定的行为

如果要执行操作连接的函数而不完全实现控制器,可以导入这两个模块,而不是 use Phoenix.Controller

渲染和布局

控制器提供的最重要的功能之一是能够根据客户端发送的信息执行内容协商和渲染模板。

控制器中有两种方法可以渲染内容。一种选择是调用特定格式的函数,例如 html/2json/2

但是,控制器最常调用称为视图的自定义模块。视图是能够渲染自定义格式的模块。这可以通过在定义控制器时指定选项 :formats 来实现

use Phoenix.Controller,
  formats: [:html, :json]

现在,在调用 render/3 时,名为 MyAppWeb.UserController 的控制器将在渲染每个格式时分别调用 MyAppWeb.UserHTMLMyAppWeb.UserJSON

def show(conn, %{"id" => id}) do
  user = Repo.get(User, id)
  # Will invoke UserHTML.show(%{user: user}) for html requests
  # Will invoke UserJSON.show(%{user: user}) for json requests
  render(conn, :show, user: user)
end

某些格式也很方便,可以使用布局,布局在所有页面上渲染共享内容。我们也可以在 use 上指定布局

use Phoenix.Controller,
  formats: [:html, :json],
  layouts: [html: MyAppWeb.Layouts]

您也可以通过直接使用连接调用 put_view/2put_layout/2 来指定要渲染的格式和布局。上面的行也可以直接在您的动作中编写为

conn
|> put_view(html: MyAppWeb.UserHTML, json: MyAppWeb.UserJSON)
|> put_layout(html: MyAppWeb.Layouts)

向后兼容性

在以前的 Phoenix 版本中,您始终渲染 MyApp.UserView 的控制器。通过将后缀传递给格式选项,可以显式保留此行为

use Phoenix.Controller,
  formats: [html: "View", json: "View"],
  layouts: [html: MyAppWeb.Layouts]

选项

使用时,控制器支持以下选项来自定义模板渲染

  • :formats - 此控制器默认将渲染的格式。例如,为名为 MyAppWeb.UserController 的控制器指定 formats: [:html, :json] 将在分别渲染每个格式时调用 MyAppWeb.UserHTMLMyAppWeb.UserJSON。如果未设置 :formats,则默认视图设置为 MyAppWeb.UserView

  • :layouts - 为每种格式渲染的布局,例如:[html: DemoWeb.Layouts]

已弃用的选项

  • :namespace - 设置布局的命名空间。请使用 :layouts 代替

  • :put_default_views - 控制是否应该设置默认视图和布局。请改用 formats: []layouts: []

Plug 管道

与路由器一样,控制器也有自己的 Plug 管道。但是,与路由器不同,控制器只有一个管道

defmodule MyAppWeb.UserController do
  use MyAppWeb, :controller

  plug :authenticate, usernames: ["jose", "eric", "sonny"]

  def show(conn, params) do
    # authenticated users only
  end

  defp authenticate(conn, options) do
    if get_session(conn, :username) in options[:usernames] do
      conn
    else
      conn |> redirect(to: "/") |> halt()
    end
  end
end

:authenticate Plug 将在动作之前调用。如果 Plug 调用 Plug.Conn.halt/1(默认情况下导入到控制器中),它将停止管道并且不会调用动作。

守卫

控制器中的 plug/2 支持守卫,允许开发人员配置 Plug 仅在某些特定动作中运行。

plug :do_something when action in [:show, :edit]

由于 Elixir 中的操作符优先级,如果第二个参数是关键字列表,则在使用 when 时,需要将关键字用 [...] 括起来

plug :authenticate, [usernames: ["jose", "eric", "sonny"]] when action in [:show, :edit]
plug :authenticate, [usernames: ["admin"]] when not action in [:index]

第一个 Plug 仅在动作是 show 或 edit 时运行。第二个 Plug 将始终运行,除了 index 动作。

这些守卫的工作原理与常规 Elixir 守卫相同,在守卫中唯一可访问的变量是 connaction(作为原子)和 controller(作为别名)。

控制器是 Plug

与路由器一样,控制器是 Plug,但它们被连接到调度到特定函数(称为动作)的函数。

例如,路由

get "/users/:id", UserController, :show

将调用 UserController 作为 Plug

UserController.call(conn, :show)

这将触发 Plug 管道,并最终调用将调度到 UserController 中的 show/2 函数的内部动作 Plug。

由于控制器是 Plug,因此它们都实现了 init/1call/2,并且它还提供了一个名为 action/2 的函数,该函数负责在 Plug 堆栈之后调度适当的动作(也可以覆盖)。

覆盖 action/2 以获取自定义参数

Phoenix 在您的控制器中注入了一个 action/2 Plug,它调用从路由器匹配的函数。默认情况下,它传递 conn 和 params。在某些情况下,在控制器中覆盖 action/2 Plug 是将参数注入到您的动作中的一种有用方法,否则您需要反复从连接中获取这些参数。例如,假设您在连接中存储了一个 conn.assigns.current_user,并且希望快速访问每个控制器动作中的用户

def action(conn, _) do
  args = [conn, conn.params, conn.assigns.current_user]
  apply(__MODULE__, action_name(conn), args)
end

def index(conn, _params, user) do
  videos = Repo.all(user_videos(user))
  # ...
end

def delete(conn, %{"id" => id}, user) do
  video = Repo.get!(user_videos(user), id)
  # ...
end

总结

函数

根据可用格式执行内容协商。

注册 Plug 作为控制器动作的回退进行调用。

将动作名称作为原子返回,如果不可用,则引发异常。

一个 Plug,它可能会将 JSON 响应转换为 JSONP 响应。

清除所有闪存消息。

将控制器模块作为原子返回,如果不可用,则引发异常。

返回当前请求路径及其默认查询参数

返回具有给定查询参数的当前路径。

返回当前请求 URL 及其默认查询参数

返回具有查询参数的当前请求 URL。

从进程字典中删除 CSRF 令牌。

将端点模块作为原子返回,如果不可用,则引发异常。

获取闪存存储。

获取或生成 CSRF 令牌。

get_flash(conn) 已弃用

返回先前设置的闪存消息的映射,或空映射。

通过 key 从闪存返回一条消息(如果 key 没有可用消息,则返回 nil)。

返回请求格式,例如“json”、“html”。

发送 html 响应。

发送 JSON 响应。

检索给定格式的当前布局。

检索当前布局格式。

将映射合并到闪存中。

在闪存中持久保存一个值。

将格式放入连接中。

存储要渲染的布局。

设置渲染时具有布局的格式。

如果尚未存储,则存储要渲染的布局。

如果尚未存储,则存储要渲染的视图。

存储要渲染的根布局。

将用于路由生成的 URL 字符串或 %URI{} 放入。

放入有助于提高浏览器安全的标头。

将用于静态 URL 生成的 URL 或 %URI{} 放入。

存储要渲染的视图。

向给定 URL 发送重定向响应。

使用给定的分配渲染给定的模板或当前动作指定的默认模板。

根据 conn 信息渲染给定的 templateassigns

检索给定格式的当前根布局。

将路由器模块作为原子返回,如果不可用,则引发异常。

从请求中清除参数。

将给定的文件或二进制文件作为下载发送。

从模板名称生成状态消息。

发送文本响应。

检索给定格式的当前视图。

返回视图中渲染的模板名称,以字符串形式(如果未渲染任何模板,则为 nil)。

类型

@type layout() :: {module(), layout_name :: atom()} | atom() | false
@type view() :: atom()

函数

链接到此函数

accepts(conn, accepted)

查看源代码
@spec accepts(Plug.Conn.t(), [binary()]) :: Plug.Conn.t()

根据可用格式执行内容协商。

它接收一个连接,一个服务器能够渲染的格式列表,然后根据请求信息进行内容协商。如果客户端接受任何给定的格式,请求将继续进行。

如果请求包含“_format”参数,则认为它是客户端所需的格式。如果不存在“_format”参数,则此函数将解析“accept”标头并相应地找到匹配的格式。

此函数在您可能希望从同一路由提供不同内容类型(例如 JSON 和 HTML)时很有用。但是,如果您始终具有不同的路由,您也可以禁用内容协商,并在路由管道中简单地硬编码您选择的格式。

plug :put_format, "html"

重要的是要注意,浏览器在历史上发送了错误的 accept 标头。出于这个原因,此函数将在以下情况下默认为“html”格式:

  • 接受的参数列表包含“html”格式

  • accept 标头指定了多个媒体类型,这些媒体类型在通配符媒体类型“*/*”之前或之后。

此函数引发 Phoenix.NotAcceptableError,它以状态 406 渲染,无论何时服务器无法以客户端期望的任何格式提供响应。

示例

accepts/2 可以作为函数调用

iex> accepts(conn, ["html", "json"])

或用作插件

plug :accepts, ["html", "json"]
plug :accepts, ~w(html json)

自定义媒体类型

可以将自定义媒体类型添加到您的 Phoenix 应用程序中。第一步是在您的 config/config.exs 文件中告知 Plug 这些新媒体类型。

config :mime, :types, %{
  "application/vnd.api+json" => ["json-api"]
}

键是媒体类型,值是媒体类型可以识别的一系列格式。例如,通过使用“json-api”,您将能够使用扩展名为“index.json-api”的模板,或者通过发送“?_format=json-api”来强制在给定 URL 中使用特定格式。

更改后,您必须重新编译 plug

$ mix deps.clean mime --build
$ mix deps.get

现在您可以在 accepts 中使用它。

plug :accepts, ["html", "json-api"]
链接到此宏

action_fallback(plug)

查看源代码 (宏)

注册 Plug 作为控制器动作的回退进行调用。

回退插件对于将常见的域数据结构转换为有效的 %Plug.Conn{} 响应很有用。如果控制器操作无法返回 %Plug.Conn{},则将调用提供的插件,并接收控制器 %Plug.Conn{} (如在调用操作之前一样),以及控制器操作返回的值。

示例

defmodule MyController do
  use Phoenix.Controller

  action_fallback MyFallbackController

  def show(conn, %{"id" => id}, current_user) do
    with {:ok, post} <- Blog.fetch_post(id),
         :ok <- Authorizer.authorize(current_user, :view, post) do

      render(conn, "show.json", post: post)
    end
  end
end

在上面的示例中,with 用于仅匹配成功的帖子获取,然后是当前用户的有效授权。如果两者都不匹配,with 不会调用渲染块,而是返回不匹配的值。在这种情况下,假设 Blog.fetch_post/2 返回 {:error, :not_found}Authorizer.authorize/3 返回 {:error, :unauthorized}。对于这些数据结构在域中跨多个边界充当返回值的情况,可以使用单个回退模块将值转换为有效的响应。例如,您可以编写以下回退控制器来处理上述值。

defmodule MyFallbackController do
  use Phoenix.Controller

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

  def call(conn, {:error, :unauthorized}) do
    conn
    |> put_status(:forbidden)
    |> put_view(MyErrorView)
    |> render(:"403")
  end
end
@spec action_name(Plug.Conn.t()) :: atom()

将动作名称作为原子返回,如果不可用,则引发异常。

链接到此函数

allow_jsonp(conn, opts \\ [])

查看源代码
@spec allow_jsonp(Plug.Conn.t(), Keyword.t()) :: Plug.Conn.t()

一个 Plug,它可能会将 JSON 响应转换为 JSONP 响应。

如果返回 JSON 响应,只要查询字符串中存在回调字段,它就会被转换为 JSONP。回调字段本身默认为“callback”,但可以通过 callback 选项配置。

如果不存在回调或响应未以 JSON 格式编码,则它将不起作用。

回调名称中仅允许字母数字字符和下划线。否则会引发异常。

示例

# Will convert JSON to JSONP if callback=someFunction is given
plug :allow_jsonp

# Will convert JSON to JSONP if cb=someFunction is given
plug :allow_jsonp, callback: "cb"

清除所有闪存消息。

链接到此函数

controller_module(conn)

查看源代码
@spec controller_module(Plug.Conn.t()) :: atom()

将控制器模块作为原子返回,如果不可用,则引发异常。

返回当前请求路径及其默认查询参数

iex> current_path(conn)
"/users/123?existing=param"

请参阅 current_path/2 以覆盖默认参数。

路径根据 conn.script_nameconn.path_info 标准化。例如,“/foo//bar/” 将变为“/foo/bar”。如果您想要原始路径,请改用 conn.request_path

链接到此函数

current_path(conn, params)

查看源代码

返回具有给定查询参数的当前路径。

您也可以通过传递一个空的 params 映射来仅检索请求路径。

示例

iex> current_path(conn)
"/users/123?existing=param"

iex> current_path(conn, %{new: "param"})
"/users/123?new=param"

iex> current_path(conn, %{filter: %{status: ["draft", "published"]}})
"/users/123?filter[status][]=draft&filter[status][]=published"

iex> current_path(conn, %{})
"/users/123"

路径根据 conn.script_nameconn.path_info 标准化。例如,“/foo//bar/” 将变为“/foo/bar”。如果您想要原始路径,请改用 conn.request_path

返回当前请求 URL 及其默认查询参数

iex> current_url(conn)
"https://www.example.com/users/123?existing=param"

请参阅 current_url/2 以覆盖默认参数。

链接到此函数

current_url(conn, params)

查看源代码

返回具有查询参数的当前请求 URL。

路径将通过 current_path/1 从当前请求的路径中检索。方案、主机和其他信息将从您的 Phoenix 端点中的 URL 配置中接收。我们不使用请求中的主机和方案信息的原因是,大多数应用程序都位于代理后面,主机和方案可能实际上不反映客户端访问的主机和方案。如果您想以客户端请求的完全方式访问 URL,请参阅 Plug.Conn.request_url/1

示例

iex> current_url(conn)
"https://www.example.com/users/123?existing=param"

iex> current_url(conn, %{new: "param"})
"https://www.example.com/users/123?new=param"

iex> current_url(conn, %{})
"https://www.example.com/users/123"

自定义 URL 生成

在某些情况下,您需要生成请求的 URL,但使用不同的方案、不同的主机等。这可以通过两种方式完成。

如果您想在个别情况下这样做,您可以定义一个自定义函数,该函数获取端点 URI 配置并相应地进行更改。例如,要始终以 HTTPS 格式获取当前 URL

def current_secure_url(conn, params \\ %{}) do
  current_uri = MyAppWeb.Endpoint.struct_url()
  current_path = Phoenix.Controller.current_path(conn, params)
  Phoenix.VerifiedRoutes.unverified_url(%URI{current_uri | scheme: "https"}, current_path)
end

但是,如果您希望所有生成的 URL 始终具有特定的方案、主机等,您可以使用 put_router_url/2

从进程字典中删除 CSRF 令牌。

注意:仅在发送响应后删除令牌。

@spec endpoint_module(Plug.Conn.t()) :: atom()

将端点模块作为原子返回,如果不可用,则引发异常。

链接到此函数

fetch_flash(conn, opts \\ [])

查看源代码

获取闪存存储。

获取或生成 CSRF 令牌。

如果存在令牌,则返回它,否则它将被生成并存储在进程字典中。

此函数已弃用。get_flash/1 已弃用。请使用 :fetch_flash 插件提供的 @flash 赋值。

返回先前设置的闪存消息的映射,或空映射。

示例

iex> get_flash(conn)
%{}

iex> conn = put_flash(conn, :info, "Welcome Back!")
iex> get_flash(conn)
%{"info" => "Welcome Back!"}
此函数已弃用。get_flash/2 已弃用。请改用 Phoenix.Flash.get(@flash, key)。

通过 key 从闪存返回一条消息(如果 key 没有可用消息,则返回 nil)。

示例

iex> conn = put_flash(conn, :info, "Welcome Back!")
iex> get_flash(conn, :info)
"Welcome Back!"

返回请求格式,例如“json”、“html”。

此格式用于将模板渲染为原子。例如,render(conn, :foo) 将渲染 "foo.FORMAT",其中格式是此处设置的格式。默认格式通常从 accepts/2 中进行的协商中设置。

@spec html(Plug.Conn.t(), iodata()) :: Plug.Conn.t()

发送 html 响应。

示例

iex> html(conn, "<html><head>...")
@spec json(Plug.Conn.t(), term()) :: Plug.Conn.t()

发送 JSON 响应。

它使用 :phoenix 应用程序下配置的 :json_library 用于 :json 来选择编码器模块。

示例

iex> json(conn, %{id: 123})
链接到此函数

layout(conn, format \\ nil)

查看源代码
@spec layout(Plug.Conn.t(), binary() | nil) :: {atom(), String.t() | atom()} | false

检索给定格式的当前布局。

如果没有提供格式,则从连接中获取当前格式。

此函数已弃用。layout_formats/1 已弃用,请改用 put_layout/put_root_layout 传递关键字列表。
@spec layout_formats(Plug.Conn.t()) :: [String.t()]

检索当前布局格式。

链接到此函数

merge_flash(conn, enumerable)

查看源代码

将映射合并到闪存中。

返回更新的连接。

示例

iex> conn = merge_flash(conn, info: "Welcome Back!")
iex> Phoenix.Flash.get(conn.assigns.flash, :info)
"Welcome Back!"
链接到此函数

protect_from_forgery(conn, opts \\ [])

查看源代码

启用 CSRF 保护。

当前用作 Plug.CSRFProtection 的包装函数,主要用作 YourApp.Router 中的函数插件。

请参阅 get_csrf_token/0delete_csrf_token/0 以检索和删除 CSRF 令牌。

链接到此函数

put_flash(conn, key, message)

查看源代码

在闪存中持久保存一个值。

返回更新的连接。

示例

iex> conn = put_flash(conn, :info, "Welcome Back!")
iex> Phoenix.Flash.get(conn.assigns.flash, :info)
"Welcome Back!"
链接到此函数

put_format(conn, format)

查看源代码

将格式放入连接中。

此格式用于将模板渲染为原子。例如,render(conn, :foo) 将渲染 "foo.FORMAT",其中格式是此处设置的格式。默认格式通常从 accepts/2 中进行的协商中设置。

请参阅 get_format/1 以检索。

链接到此函数

put_layout(conn, layout)

查看源代码
@spec put_layout(Plug.Conn.t(), [{format :: atom(), layout()}] | false) ::
  Plug.Conn.t()

存储要渲染的布局。

布局必须作为关键字列表给出,其中键是将应用布局的请求格式(例如 :html),而值是以下之一

  • {module, layout},其中 module 是定义布局的模块,layout 是一个原子。

  • layout,当布局的名称。这需要在先前给出的 {module, layout} 形状中为给定格式提供一个布局。

  • false,它禁用布局。

如果在没有格式的情况下给出 false,则所有布局都将被禁用。

示例

iex> layout(conn)
false

iex> conn = put_layout(conn, html: {AppView, :application})
iex> layout(conn)
{AppView, :application}

iex> conn = put_layout(conn, html: :print)
iex> layout(conn)
{AppView, :print}

如果 conn 已经发送,则引发 Plug.Conn.AlreadySentError

链接到此函数

put_layout_formats(conn, formats)

查看源代码
此函数已弃用。put_layout_formats/2 已弃用,请改用 put_layout/put_root_layout 传递关键字列表。
@spec put_layout_formats(Plug.Conn.t(), [String.t()]) :: Plug.Conn.t()

设置渲染时具有布局的格式。

示例

iex> layout_formats(conn)
["html"]

iex> put_layout_formats(conn, ["html", "mobile"])
iex> layout_formats(conn)
["html", "mobile"]

如果 conn 已经发送,则引发 Plug.Conn.AlreadySentError

链接到此函数

put_new_layout(conn, layout)

查看源代码
@spec put_new_layout(Plug.Conn.t(), [{format :: atom(), layout()}] | layout()) ::
  Plug.Conn.t()

如果尚未存储,则存储要渲染的布局。

请参阅 put_layout/2 以获取更多信息。

如果 conn 已经发送,则引发 Plug.Conn.AlreadySentError

链接到此函数

put_new_view(conn, formats)

查看源代码
@spec put_new_view(Plug.Conn.t(), [{format :: atom(), view()}] | view()) ::
  Plug.Conn.t()

如果尚未存储,则存储要渲染的视图。

如果 conn 已经发送,则引发 Plug.Conn.AlreadySentError

链接到此函数

put_root_layout(conn, layout)

查看源代码
@spec put_root_layout(Plug.Conn.t(), [{format :: atom(), layout()}] | false) ::
  Plug.Conn.t()

存储要渲染的根布局。

布局必须作为关键字列表给出,其中键是将应用布局的请求格式(例如 :html),而值是以下之一

  • {module, layout},其中 module 是定义布局的模块,layout 是一个原子。

  • layout,当布局的名称。这需要在先前给出的 {module, layout} 形状中为给定格式提供一个布局。

  • false,它禁用布局。

示例

iex> root_layout(conn)
false

iex> conn = put_root_layout(conn, html: {AppView, :root})
iex> root_layout(conn)
{AppView, :root}

iex> conn = put_root_layout(conn, html: :bare)
iex> root_layout(conn)
{AppView, :bare}

如果 conn 已经发送,则引发 Plug.Conn.AlreadySentError

链接到此函数

put_router_url(conn, uri)

查看源代码

将用于路由生成的 URL 字符串或 %URI{} 放入。

此函数覆盖从 %Plug.Conn{} 的端点配置中提取的默认 URL 生成。

示例

假设您的应用程序配置为在“example.com”上运行,但用户登录后,您希望所有链接都使用“some_user.example.com”。您可以通过设置正确的路由器 URL 配置来做到这一点。

def put_router_url_by_user(conn) do
  put_router_url(conn, get_user_from_conn(conn).account_name <> ".example.com")
end

现在,当您调用 Routes.some_route_url(conn, ...) 时,它将使用上面设置的路由器 URL。请记住,如果您要生成指向当前域的路由,最好使用 Routes.some_route_path 帮助程序,因为这些帮助程序始终是相对的。

链接到此函数

put_secure_browser_headers(conn, headers \\ %{})

查看源代码

放入有助于提高浏览器安全的标头。

它设置以下标头

  • referrer-policy - 仅在跨域请求时发送来源
  • x-frame-options - 设置为 SAMEORIGIN 以避免通过 iframe 进行点击劫持(除非在同一来源)
  • x-content-type-options - 设置为 nosniff。这要求脚本和样式标记以正确的内容类型发送。
  • x-download-options - 设置为 noopen 以指示浏览器不要直接在浏览器中打开下载,以避免 HTML 文件在内联呈现并访问应用程序的安全上下文(如关键域 Cookie)
  • x-permitted-cross-domain-policies - 设置为 none 以限制 Adobe Flash Player 访问数据

还可以提供一个自定义标头映射,将其与默认值合并。建议自定义标头键使用小写,以避免在请求中发送重复的键。此外,通过 HTTP/2 提供的具有混合大小写标头的响应不被常见的客户端视为有效,导致响应丢失。

链接到此函数

put_static_url(conn, uri)

查看源代码

将用于静态 URL 生成的 URL 或 %URI{} 放入。

%Plug.Conn{} 结构体上使用此函数告诉 static_url/2 使用给定的信息来生成 URL,而不是 %Plug.Conn{} 的端点配置(类似于 put_router_url/2,但适用于静态 URL)。

链接到此函数

put_view(conn, formats)

查看源代码
@spec put_view(Plug.Conn.t(), [{format :: atom(), view()}] | view()) :: Plug.Conn.t()

存储要渲染的视图。

如果 conn 已经发送,则引发 Plug.Conn.AlreadySentError

示例

# Use single view module
iex> put_view(conn, AppView)

# Use multiple view module for content negotiation
iex> put_view(conn, html: AppHTML, json: AppJSON)

向给定 URL 发送重定向响应。

出于安全考虑,:to 仅接受路径。使用 :external 选项重定向到任何 URL。

响应将使用连接中定义的状态代码发送,通过 Plug.Conn.put_status/2。如果未设置状态代码,则会发送 302 响应。

示例

iex> redirect(conn, to: "/login")

iex> redirect(conn, external: "https://elixir.erlang.ac.cn")
链接到此函数

render(conn, template_or_assigns \\ [])

查看源代码
@spec render(Plug.Conn.t(), Keyword.t() | map() | binary() | atom()) :: Plug.Conn.t()

使用给定的分配渲染给定的模板或当前动作指定的默认模板。

有关更多信息,请参见 render/3

链接到此函数

render(conn, template, assigns)

查看源代码
@spec render(Plug.Conn.t(), binary() | atom(), Keyword.t() | map()) :: Plug.Conn.t()

根据 conn 信息渲染给定的 templateassigns

模板渲染完成后,模板格式将设置为响应内容类型(例如,HTML 模板将设置“text/html”作为响应内容类型),并将数据发送到客户端,默认状态为 200。

参数

  • conn - Plug.Conn 结构体

  • template - 可以是原子或字符串。如果是原子,例如 :index,它将渲染一个模板,其格式与 get_format/1 返回的模板相同。例如,对于 HTML 请求,它将渲染“index.html”模板。如果模板是字符串,则必须包含扩展名,例如“index.json”

  • assigns - 包含要在视图中使用的分配的字典。这些分配将合并,并且优先级高于连接分配 (conn.assigns)

示例

defmodule MyAppWeb.UserController do
  use Phoenix.Controller

  def show(conn, _params) do
    render(conn, "show.html", message: "Hello")
  end
end

上面的示例从 MyAppWeb.UserView 渲染模板“show.html”,并将响应内容类型设置为“text/html”。

在许多情况下,您可能希望模板格式根据请求动态设置。为此,您可以将模板名称作为原子(不带扩展名)传递。

def show(conn, _params) do
  render(conn, :show, message: "Hello")
end

为了使上面的示例正常工作,我们需要在渲染之前使用 accepts 插件进行内容协商。您可以在管道中(在路由器中)添加以下内容来实现这一点

plug :accepts, ["html"]

视图

默认情况下,控制器在与控制器名称相似的视图中渲染模板。例如,MyAppWeb.UserController 将在 MyAppWeb.UserView 中渲染模板。可以使用 put_view/2 函数随时更改此信息

def show(conn, _params) do
  conn
  |> put_view(MyAppWeb.SpecialView)
  |> render(:show, message: "Hello")
end

put_view/2 也可以用作插件

defmodule MyAppWeb.UserController do
  use Phoenix.Controller

  plug :put_view, html: MyAppWeb.SpecialView

  def show(conn, _params) do
    render(conn, :show, message: "Hello")
  end
end

布局

模板通常在布局内渲染。默认情况下,Phoenix 将为 html 请求渲染布局。例如

defmodule MyAppWeb.UserController do
  use Phoenix.Controller

  def show(conn, _params) do
    render(conn, "show.html", message: "Hello")
  end
end

将在 MyAppWeb.LayoutView 中指定的“app.html”模板中渲染“show.html”模板。put_layout/2 可用于更改布局,类似于 put_view/2 可用于更改视图的方式。

链接到此函数

root_layout(conn, format \\ nil)

查看源代码
@spec root_layout(Plug.Conn.t(), binary() | nil) ::
  {atom(), String.t() | atom()} | false

检索给定格式的当前根布局。

如果没有提供格式,则从连接中获取当前格式。

@spec router_module(Plug.Conn.t()) :: atom()

将路由器模块作为原子返回,如果不可用,则引发异常。

链接到此函数

scrub_params(conn, required_key)

查看源代码
@spec scrub_params(Plug.Conn.t(), String.t()) :: Plug.Conn.t()

从请求中清除参数。

此过程分为两部分

  • 检查 required_key 是否存在
  • required_key 的空参数(递归地)更改为 nil

此函数对于删除通过 HTML 表单发送的空字符串很有用。如果您提供 API,则可能不需要调用 scrub_params/2

如果 required_key 不存在,它将引发 Phoenix.MissingParamError

示例

iex> scrub_params(conn, "user")
链接到此函数

send_download(conn, kind, opts \\ [])

查看源代码

将给定的文件或二进制文件作为下载发送。

第二个参数必须是 {:binary, contents},其中 contents 将作为下载发送,或者 {:file, path},其中 path 是要发送的文件的文件系统位置。注意不要从外部参数中插入路径,因为它可能会允许遍历文件系统。

下载是通过将“content-disposition”设置为附件来实现的。“content-type”也将根据给定文件名的扩展名进行设置,但可以通过 :content_type:charset 选项进行自定义。

选项

  • :filename - 向用户呈现为下载的文件名
  • :content_type - 作为下载发送的文件或二进制文件的 content type。它会自动从文件名扩展名中推断出来
  • :disposition - 指定处理类型 (:attachment:inline)。如果使用 :attachment,系统将提示用户保存文件。如果使用 :inline,浏览器将尝试打开文件。默认为 :attachment
  • :charset - 文件的字符集,例如“utf-8”。默认为无
  • :offset - 读取时的字节偏移量。默认为 0
  • :length - 要读取的总字节数。默认为 :all
  • :encode - 使用 URI.encode/2 对文件名进行编码。默认为 true。当为 false 时,禁用编码。如果您禁用编码,则需要保证文件名中没有特殊字符,例如引号、换行符等。否则,您可能会将应用程序暴露于安全攻击

示例

要发送存储在应用程序 priv 目录中的文件

path = Application.app_dir(:my_app, "priv/prospectus.pdf")
send_download(conn, {:file, path})

使用 {:file, path} 时,文件名从给定的路径中推断出来,但也可以显式设置。

允许用户下载作为二进制或字符串存储在内存中的内容

send_download(conn, {:binary, "world"}, filename: "hello.txt")

如果您想访问用于通过 Plug 发送文件和响应的底层函数,请参见 Plug.Conn.send_file/3Plug.Conn.send_resp/3

链接到此函数

status_message_from_template(template)

查看源代码

从模板名称生成状态消息。

示例

iex> status_message_from_template("404.html")
"Not Found"
iex> status_message_from_template("whatever.html")
"Internal Server Error"
@spec text(Plug.Conn.t(), String.Chars.t()) :: Plug.Conn.t()

发送文本响应。

示例

iex> text(conn, "hello")

iex> text(conn, :implements_to_string)
链接到此函数

view_module(conn, format \\ nil)

查看源代码
@spec view_module(Plug.Conn.t(), binary() | nil) :: atom()

检索给定格式的当前视图。

如果没有提供格式,则从连接中获取当前格式。

@spec view_template(Plug.Conn.t()) :: binary() | nil

返回视图中渲染的模板名称,以字符串形式(如果未渲染任何模板,则为 nil)。