檢視原始碼 Phoenix.Controller (Phoenix v1.7.14)
Controller 用於將相同(可插入的)模組中的共用功能分組。
例如,路由程式碼
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 函式庫指定的 Plug.Conn
結構。
然後,呼叫 render/3
,傳遞連線、要呈現範本(通常以函式命名)和 user: user
作為分配。稍後我們會探討所有這些概念。
連線
Controller 預設提供許多便利函式,用於操作連線、呈現範本等等。
這些函式從兩個模組匯入
Plug.Conn
- 處理連線的一系列低階函式Phoenix.Controller
- Phoenix 提供的函式,用於支援呈現和其他 Phoenix 特定行為
如果您想要具有處理連線但未完全實作 controller 的函式,您可以直接匯入這兩個模組,而不是 use Phoenix.Controller
。
呈現和配置
Controller 提供的主要功能之一是根據用戶端傳送的資訊執行內容協商並呈現範本。
在 Controller 中有兩種呈現內容的方式。一種選項是呼叫特定於格式的函式,例如 html/2
和 json/2
。
但是,最常見的做法是 Controller 呼叫自訂模組,稱為檢視。檢視是可以呈現自訂格式的模組。這是在定義 Controller 時指定選項 :formats
的做法
use Phoenix.Controller,
formats: [:html, :json]
現在,呼叫 render/3
時,在呈現個別格式時,名為 MyAppWeb.UserController
的 Controller 將分別呼叫 MyAppWeb.UserHTML
和 MyAppWeb.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/2
和 put_layout/2
來指定要呈現的格式和版型。上面的那行也可以直接寫成在您動作中的以下寫法
conn
|> put_view(html: MyAppWeb.UserHTML, json: MyAppWeb.UserJSON)
|> put_layout(html: MyAppWeb.Layouts)
相容性
在先前的 Phoenix 版本中,控制器永遠只會呈現 MyApp.UserView
。這種行為可以透過將字尾傳遞給 formats 選項來明確地保留
use Phoenix.Controller,
formats: [html: "View", json: "View"],
layouts: [html: MyAppWeb.Layouts]
選項
控制器在使用時支援以下選項,以自訂範本呈現
:formats
- 這個控制器預設會呈現的格式。例如,針對一個名稱為MyAppWeb.UserController
的控制器指定formats: [:html, :json]
,分別呈現每個格式時會呼叫MyAppWeb.UserHTML
和MyAppWeb.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 只會在 action 為 show 或 edit 時執行。第二個 Plug 會一直執行,除了 index 動作之外。
這些防護的運作方式與一般 Elixir 防護相同,防護中唯一可以存取的變數有 conn
、原子的 action
和別名的 controller
。
控制器是 Plug
就像路由一樣,控制器是外掛程式,但它們可以連接到特定函式(稱為動作)來執行派送工作。
例如,路由程式碼
get "/users/:id", UserController, :show
將喚叫 UserController
作為外掛程式
UserController.call(conn, :show)
這將觸發外掛程式管線,最終將呼叫執行派送工作的功能外掛程式,而它將派送至 UserController
中的 show/2
函式。
由於控制器是外掛程式,因此它們會實作 init/1
和 call/2
,而且它也提供了一個名為 action/2
的函式,負責在堆疊外掛程式之後執行適當的動作(而且也可以覆寫)。
覆寫 action/2
以使用自訂參數
Phoenix 會將 action/2
外掛程式注入控制器中,這個外掛程式會呼叫與路由相符的函式。預設情況下,它會傳遞 conn 與 params。在某些情況下,覆寫控制器中的 action/2
外掛程式會是一個很有用的方式,可以將參數注入至你的動作中(否則你必須重複從連線中提取參數)。例如,假設你儲存在連線中的 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
摘要
函式
根據可用的格式執行內容協商。
註冊外掛程式,供控制器動作當作備份來呼叫。
以原子型態傳回動作名稱,如果無法使用則會引發例外。
一個外掛程式,它可以將 JSON 回應轉換為 JSONP 回應。
清除所有快閃訊息。
以原子型態傳回控制器模組,如果無法使用則會引發例外。
傳回目前的請求路徑及其預設的查詢參數
傳回具有指定查詢參數的目前路徑。
傳回目前的請求 URL 及其預設的查詢參數
傳回具有查詢參數的目前請求 URL。
從處理程序詞彙中刪除 CSRF 令牌。
以原子型態傳回端點模組,如果無法使用則會引發例外。
擷取快閃儲存區。
取得或產生 CSRF 令牌。
傳回先前設定的快閃訊息的對應,或傳回一個空的對應。
依據 key
傳回快閃訊息(如果找不到 key
的訊息,則傳回 nil
)。
傳回請求格式,例如「json」、「html」。
傳送 html 回應。
傳送 JSON 回應。
擷取特定格式的目前版面配置。
擷取目前的版面配置格式。
將映射合併至快閃訊息中。
啟用 CSRF 防護
在快閃訊息中儲存值。
將格式放入連線中。
儲存版面配置以進行呈現。
設定呈現時哪些格式具有版面配置。
尚未儲存版面配置時,儲存版面配置以進行呈現。
尚未儲存檢視時,儲存檢視以進行呈現。
儲存根版面配置以進行呈現。
放入 url 字串或 %URI{}
以用於路由產生。
放入用於增強瀏覽器安全的標頭。
放入 URL 或 %URI{}
以用於靜態 URL 產生。
儲存檢視以進行呈現。
傳送重新導向回應至提供的 URL。
使用提供的 assign 呈現已提供的範本或由目前的動作指定預設範本。
根據 conn
資訊,呈現已提供的 template
和 assigns
。
擷取特定格式的目前根版面配置。
回傳路由模組做為原子,如果無法取得則會引發例外狀況。
清除請求參數。
將已提供的檔案或二進制檔案送出作為下載。
根據範本名稱產生狀態訊息。
傳送文字回應。
擷取特定格式的目前檢視。
傳回檢視時呈現在畫面上之樣板名稱(若無樣板呈現在畫面上,則傳回 nil)。
類型
函式
@spec accepts(Plug.Conn.t(), [binary()]) :: Plug.Conn.t()
根據可用的格式執行內容協商。
它會接收一個連線、一個伺服器能夠呈現之格式清單,接著根據要求資訊進行內容協商。如果客戶端接受任何特定格式,要求將繼續進行。
如果要求包含「_format」參數,則這會被視為客戶端想要的格式。如果沒有「_format」參數,這個函式會解析「接收」標頭,並據此尋找相符的格式。
當您想要從相同的路徑提供不同的內容類型(例如 JSON 和 HTML)時,這個函式會很有用。不過,如果您總是有不同的路徑,您也可以停用內容協商,並僅在您的路徑管線中硬寫您選擇的格式
plug :put_format, "html"
需要注意的是,瀏覽器從過去就一直傳送錯誤的接收標頭。因此,當
已接受的引數清單中包含「html」格式時
接收標頭指定多於一種媒體類型,且在之前或之後有萬用媒體類型 "
*/*
"
這個函式會 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"]
註冊外掛程式,供控制器動作當作備份來呼叫。
備用程式連接頭很適合將常見的網域資料結構轉譯為有效的 %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()
以原子型態傳回動作名稱,如果無法使用則會引發例外。
@spec allow_jsonp(Plug.Conn.t(), Keyword.t()) :: Plug.Conn.t()
一個外掛程式,它可以將 JSON 回應轉換為 JSONP 回應。
若回傳了 JSON 回應,只要查詢字串中存在 callback 欄位,它就會轉換為 JSONP。callback 欄位本身預設為「callback」,但可以使用 callback 選項進行設定。
若不存在 callback 或是回應未採用 JSON 格式編碼,則為不執行任何作業。
callback 名稱僅允許字母數字字元及底線。否則會引發例外狀況。
範例
# 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"
清除所有快閃訊息。
@spec controller_module(Plug.Conn.t()) :: atom()
以原子型態傳回控制器模組,如果無法使用則會引發例外。
傳回目前的請求路徑及其預設的查詢參數
iex> current_path(conn)
"/users/123?existing=param"
請參閱 current_path/2
以覆寫預設參數。
路徑會根據 conn.script_name
和 conn.path_info
來正規化。例如,"/foo//bar/" 會變成 "/foo/bar"。如果您想要原始的路徑,請改用 conn.request_path
。
傳回具有指定查詢參數的目前路徑。
您也可以透過傳遞空的參數映射集只擷取請求路徑。
範例
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_name
和 conn.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
以覆寫預設參數。
傳回具有查詢參數的目前請求 URL。
路徑將透過 current_path/1
從目前要求的路徑擷取。將從 Phoenix 端點的 URL 設定中接收 scheme、主機和其他資訊。我們不使用要求中的主機和 scheme 資訊,原因在於大多數應用程式都位於代理伺服器之後,而主機和 scheme 可能實際上並未反映客戶端存取的主機和 scheme。如果想要完全根據客戶端的請求存取 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,但使用不同的 scheme、不同的主機等。這有兩種方法可以達成。
如果您想要逐案進行,您可以定義一個自訂函式,取得端點 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 都始終為特定的 schema、主機等,您可以使用 put_router_url/2
。
從處理程序詞彙中刪除 CSRF 令牌。
注意事項:只在傳送回應後才會刪除 token。
@spec endpoint_module(Plug.Conn.t()) :: atom()
以原子型態傳回端點模組,如果無法使用則會引發例外。
擷取快閃儲存區。
取得或產生 CSRF 令牌。
如果 token 存在,則會傳回,否則會在處理程序字典中產生 stored。
傳回先前設定的快閃訊息的對應,或傳回一個空的對應。
範例
iex> get_flash(conn)
%{}
iex> conn = put_flash(conn, :info, "Welcome Back!")
iex> get_flash(conn)
%{"info" => "Welcome Back!"}
依據 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})
@spec layout(Plug.Conn.t(), binary() | nil) :: {atom(), String.t() | atom()} | false
擷取特定格式的目前版面配置。
如果未給定格式,則從連線中取得目前的格式。
@spec layout_formats(Plug.Conn.t()) :: [String.t()]
擷取目前的版面配置格式。
將映射合併至快閃訊息中。
傳回已更新的連線。
範例
iex> conn = merge_flash(conn, info: "Welcome Back!")
iex> Phoenix.Flash.get(conn.assigns.flash, :info)
"Welcome Back!"
啟用 CSRF 防護
目前用於 Plug.CSRFProtection
的 wrapper 函式,主要用於將 YourApp.Router
函式插入閘道器。
可查看 get_csrf_token/0
和 delete_csrf_token/0
以取得及刪除 CSRF 令牌。
在快閃訊息中儲存值。
傳回已更新的連線。
範例
iex> conn = put_flash(conn, :info, "Welcome Back!")
iex> Phoenix.Flash.get(conn.assigns.flash, :info)
"Welcome Back!"
將格式放入連線中。
此格式用於以降階元呈現範本時。例如,render(conn, :foo)
會呈現 "foo.FORMAT"
,其中格式為在此設定的一個。預設格式通常是從 accepts/2
中進行協商後設定的。
請參閱 get_format/1
以取得。
@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
。
@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
。
@spec put_new_layout(Plug.Conn.t(), [{format :: atom(), layout()}] | layout()) :: Plug.Conn.t()
尚未儲存版面配置時,儲存版面配置以進行呈現。
請參閱 put_layout/2
以取得更多資訊。
如果 conn
已傳送,則會產生 Plug.Conn.AlreadySentError
。
@spec put_new_view(Plug.Conn.t(), [{format :: atom(), view()}] | view()) :: Plug.Conn.t()
尚未儲存檢視時,儲存檢視以進行呈現。
如果 conn
已傳送,則會產生 Plug.Conn.AlreadySentError
。
@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
。
放入 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
輔助程式,因為那些都是相對的。
放入用於增強瀏覽器安全的標頭。
它會設定以下標頭
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 上提供的混合式大小寫標頭的回應為有效,導致回應被捨棄。
放入 URL 或 %URI{}
以用於靜態 URL 產生。
在 %Plug.Conn{}
結構中使用此功能,會告訴 static_url/2
使用所提供的資訊來產生網址,而非 %Plug.Conn{}
的端點設定(與 put_router_url/2
相似,但適用於靜態網址)。
@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
選項。
回應會傳送與連線中定義的狀態碼,透過 Plug.Conn.put_status/2
傳送。如果未設定狀態碼,就會傳送回應 302。
範例
iex> redirect(conn, to: "/login")
iex> redirect(conn, external: "https://elixir.dev.org.tw")
@spec render(Plug.Conn.t(), Keyword.t() | map() | binary() | atom()) :: Plug.Conn.t()
使用提供的 assign 呈現已提供的範本或由目前的動作指定預設範本。
有關詳細資訊,請參閱 render/3
。
@spec render(Plug.Conn.t(), binary() | atom(), Keyword.t() | map()) :: Plug.Conn.t()
根據 conn
資訊,呈現已提供的 template
和 assigns
。
一旦範本呈現,範本格式就會設定為回應的內容類型(例如,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
可用於變更檢視。
@spec root_layout(Plug.Conn.t(), binary() | nil) :: {atom(), String.t() | atom()} | false
擷取特定格式的目前根版面配置。
如果未給定格式,則從連線中取得目前的格式。
@spec router_module(Plug.Conn.t()) :: atom()
回傳路由模組做為原子,如果無法取得則會引發例外狀況。
@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")
將已提供的檔案或二進制檔案送出作為下載。
第二個引數必須為 {:binary, contents}
,其中 contents
將以下載形式傳送,或 {:file, path}
,其中 path
是要傳送檔案的檔案系統位置。請小心不要從外部參數插入路徑,因為這樣可能會被用於跨越檔案系統。
下載是透過將「content-disposition」設定為附加檔來達成。「content-type」也將按據給定檔名的副檔名自動推論,但可透過 :content_type
和 :charset
選項進行自訂。
選項
:filename
- 要提供給使用者作為下載時顯示的檔名: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/3
和 Plug.Conn.send_resp/3
。
根據範本名稱產生狀態訊息。
範例
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)
@spec view_module(Plug.Conn.t(), binary() | nil) :: atom()
擷取特定格式的目前檢視。
如果未給定格式,則從連線中取得目前的格式。
@spec view_template(Plug.Conn.t()) :: binary() | nil
傳回檢視時呈現在畫面上之樣板名稱(若無樣板呈現在畫面上,則傳回 nil)。