Phoenix.Controller
Controllers are used to group common functionality in the same (pluggable) module.
For example, the route:
get "/users/:id", UserController, :show
will invoke the show/2
action in the UserController
:
defmodule UserController do
use Phoenix.Controller
plug :action
def show(conn, %{"id" => id}) do
user = Repo.get(User, id)
render conn, "show.html", user: user
end
end
An action is just a regular function that receives the connection
and the request parameters as arguments. The connection is a
Plug.Conn
struct, as specified by the Plug library.
Connection
A controller by default provides many convenience functions for manipulating the connection, rendering templates, and more.
Those functions are imported from two modules:
Plug.Conn
- a bunch of low-level functions to work with the connectionPhoenix.Controller
- functions provided by Phoenix to support rendering, and other Phoenix specific behaviour
Rendering and layouts
One of the main features provided by controllers is the ability
to do content negotiation and render templates based on
information sent by the client. Read render/3
to learn more.
It is also important to not confuse Phoenix.Controller.render/3
with Phoenix.View.render/3
in the long term. The former expects
a connection and relies on content negotiation while the latter is
connection-agnostic and typically invoked from your views.
Plug pipeline
As routers, controllers also have their own plug pipeline. However, different from routers, controllers have a single pipeline:
defmodule UserController do
use Phoenix.Controller
plug :authenticate, usernames: ["jose", "eric", "sonny"]
plug :action
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(Router.root_path) |> halt
end
end
end
The :action
plug must always be invoked and it represents the action
to be dispatched to.
Check Phoenix.Controller.Pipeline
for more information on plug/2
and how to customize the plug pipeline.
Summary↑
accepts(conn, accepted) | Performs content negotiation based on the available formats |
action_name(conn) | Returns the action name as an atom, raises if unavailable |
clear_flash(conn) | Clears all flash messages |
controller_module(conn) | Returns the controller module as an atom, raises if unavailable |
delete_csrf_token() | Deletes any CSRF token set |
endpoint_module(conn) | Returns the endpoint module as an atom, raises if unavailable |
fetch_flash(conn, opts \\ []) | Fetches the flash storage |
get_csrf_token() | Gets the CSRF token |
get_flash(conn) | Returns a previously set flash message or nil |
get_flash(conn, key) | Returns a message from flash by key |
html(conn, data) | Sends html response |
json(conn, data) | Sends JSON response |
layout(conn) | Retrieves the current layout |
layout_formats(conn) | Retrieves current layout formats |
protect_from_forgery(conn, opts \\ []) | Enables CSRF protection |
put_flash(conn, key, message) | Persists a value in flash |
put_layout(conn, layout) | Stores the layout for rendering |
put_layout_formats(conn, formats) | Sets which formats have a layout when rendering |
put_new_layout(conn, layout) | Stores the layout for rendering if one was not stored yet |
put_new_view(conn, module) | Stores the view for rendering if one was not stored yet |
put_view(conn, module) | Stores the view for rendering |
redirect(conn, opts) | Sends redirect response to the given url |
render(conn, template_or_assigns \\ []) | Render the given template or the default template specified by the current action with the given assigns |
render(conn, template, assigns) | Renders the given |
render(conn, view, template, assigns) | |
router_module(conn) | Returns the router module as an atom, raises if unavailable |
scrub_params(conn, required_key) | Scrubs the parameters from the request |
text(conn, data) | Sends text response |
view_module(conn) | Retrieves the current view |
Functions
Specs:
- accepts(Plug.Conn.t, [binary]) :: Plug.Conn.t | no_return
Performs content negotiation based on the available formats.
It receives a connection, a list of formats that the server is capable of rendering and then proceeds to perform content negotiation based on the request information. If the client accepts any of the given formats, the request proceeds.
If the request contains a “format” parameter, it is considered to be the format desired by the client. If no “format” parameter is available, this function will parse the “accept” header and find a matching format accordingly.
It is important to notice that browsers have historically sent bad accept headers. For this reason, this function will default to “html” format whenever:
the accepted list of arguments contains the “html” format
- the accept header specified more than one media type preceeded or followed by the wildcard media type “/“
This function raises Phoenix.NotAcceptableError
, which is rendered
with status 406, whenever the server cannot serve a response in any
of the formats expected by the client.
Examples
accepts/2
can be invoked as a function:
iex> accepts(conn, ["html", "json"])
or used as a plug:
plug :accepts, ["html", "json"]
plug :accepts, ~w(html json)
Specs:
- action_name(Plug.Conn.t) :: atom
Returns the action name as an atom, raises if unavailable.
Clears all flash messages.
Specs:
- controller_module(Plug.Conn.t) :: atom
Returns the controller module as an atom, raises if unavailable.
Deletes any CSRF token set.
Specs:
- endpoint_module(Plug.Conn.t) :: atom
Returns the endpoint module as an atom, raises if unavailable.
Fetches the flash storage.
Gets the CSRF token.
Returns a previously set flash message or nil.
Examples
iex> conn = put_flash(conn, :notice, "Welcome Back!")
iex> get_flash(conn)
%{"notice" => "Welcome Back!"}
Returns a message from flash by key
Examples
iex> conn = put_flash(conn, :notice, "Welcome Back!")
iex> get_flash(conn, :notice)
"Welcome Back!"
Specs:
- html(Plug.Conn.t, iodata) :: Plug.Conn.t
Sends html response.
Examples
iex> html conn, "<html><head>..."
Specs:
- json(Plug.Conn.t, term) :: Plug.Conn.t
Sends JSON response.
It uses the configured :format_encoders
under the :phoenix
application for :json
to pick up the encoder module.
Examples
iex> json conn, %{id: 123}
Specs:
- layout(Plug.Conn.t) :: {atom, String.t} | false
Retrieves the current layout.
Specs:
- layout_formats(Plug.Conn.t) :: [String.t]
Retrieves current layout formats.
Enables CSRF protection.
Currently used as a wrapper function for Plug.CSRFProtection
and mainly serves as a function plug in YourApp.Router
.
Check get_csrf_token/0
and delete_csrf_token/0
for
retrieving and deleting CSRF tokens.
Persists a value in flash.
Returns the updated connection.
Examples
iex> conn = put_flash(conn, :notice, "Welcome Back!")
iex> get_flash(conn, :notice)
"Welcome Back!"
Specs:
- put_layout(Plug.Conn.t, {atom, binary} | binary | false) :: Plug.Conn.t
Stores the layout for rendering.
The layout must be a tuple, specifying the layout view and the layout
name, or false. In case a previous layout is set, put_layout
also
accepts the layout name to be given as a string or as an atom. If a
string, it must contain the format. Passing an atom means the layout
format will be found at rendering time, similar to the template in
render/3
.
Examples
iex> layout(conn)
false
iex> conn = put_layout conn, {AppView, "application"}
iex> layout(conn)
{AppView, "application"}
iex> conn = put_layout conn, "print"
iex> layout(conn)
{AppView, "print"}
iex> conn = put_layout :print
iex> layout(conn)
{AppView, :print}
Raises Plug.Conn.AlreadySentError
if the conn was already sent.
Specs:
- put_layout_formats(Plug.Conn.t, [String.t]) :: Plug.Conn.t
Sets which formats have a layout when rendering.
Examples
iex> layout_formats conn
["html"]
iex> put_layout_formats conn, ["html", "mobile"]
iex> layout_formats conn
["html", "mobile"]
Raises Plug.Conn.AlreadySentError
if the conn was already sent.
Specs:
- put_new_layout(Plug.Conn.t, {atom, binary} | false) :: Plug.Conn.t
Stores the layout for rendering if one was not stored yet.
Raises Plug.Conn.AlreadySentError
if the conn was already sent.
Specs:
- put_new_view(Plug.Conn.t, atom) :: Plug.Conn.t
Stores the view for rendering if one was not stored yet.
Raises Plug.Conn.AlreadySentError
if the conn was already sent.
Specs:
- put_view(Plug.Conn.t, atom) :: Plug.Conn.t
Stores the view for rendering.
Raises Plug.Conn.AlreadySentError
if the conn was already sent.
Sends redirect response to the given url.
For security, :to
only accepts paths. Use the :external
option to redirect to any URL.
Examples
iex> redirect conn, to: "/login"
iex> redirect conn, external: "http://elixir-lang.org"
Specs:
- render(Plug.Conn.t, Dict.t | binary | atom) :: Plug.Conn.t
Render the given template or the default template specified by the current action with the given assigns.
See render/3
for more information.
Specs:
- render(Plug.Conn.t, module, binary | atom) :: Plug.Conn.t
- render(Plug.Conn.t, binary | atom, Dict.t) :: Plug.Conn.t
Renders the given template
and assigns
based on the conn
information.
Once the template is rendered, the template format is set as the response content type (for example, an HTML template will set “text/html” as response content type) and the data is sent to the client with default status of 200.
Arguments
conn
- thePlug.Conn
structtemplate
- which may be an atom or a string. If an atom, like:index
, it will render a template with the same format as the one found inconn.params["format"]
. For example, for an HTML request, it will render the “index.html” template. If the template is a string, it must contain the extension too, like “index.json”assigns
- a dictionary with the assigns to be used in the view. Those assigns are merged and have higher precedence than the connection assigns (conn.assigns
)
Examples
defmodule MyApp.UserController do
use Phoenix.Controller
plug :action
def show(conn, _params) do
render conn, "show.html", message: "Hello"
end
end
The example above renders a template “show.html” from the MyApp.UserView
and sets the response content type to “text/html”.
In many cases, you may want the template format to be set dynamically based on the request. To do so, you can pass the template name as an atom (without the extension):
def show(conn, _params) do
render conn, :show, message: "Hello"
end
In order for the example above to work, we need to do content negotiation with the accepts plug before rendering. You can do so by adding the following to your pipeline (in the router):
plug :accepts, ["html"]
Views
By default, Controllers render templates in a view with a similar name to the
controller. For example, MyApp.UserController
will render templates inside
the MyApp.UserView
. This information can be changed any time by using
render/3
, render/4
or the put_view/2
function:
def show(conn, _params) do
render(conn, MyApp.SpecialView, :show, message: "Hello")
end
def show(conn, _params) do
conn
|> put_view(MyApp.SpecialView)
|> render(:show, message: "Hello")
end
put_view/2
can also be used as a plug:
defmodule MyApp.UserController do
use Phoenix.Controller
plug :put_view, MyApp.SpecialView
plug :action
def show(conn, _params) do
render conn, :show, message: "Hello"
end
end
Layouts
Templates are often rendered inside layouts. By default, Phoenix will render layouts for html requests. For example:
defmodule MyApp.UserController do
use Phoenix.Controller
plug :action
def show(conn, _params) do
render conn, "show.html", message: "Hello"
end
end
will render the “show.html” template inside an “application.html”
template specified in MyApp.LayoutView
. put_layout/2
can be used
to change the layout, similar to how put_view/2
can be used to change
the view.
layout_formats/2
and put_layout_formats/2
can be used to configure
which formats support/require layout rendering (defaults to “html” only).
Specs:
- render(Plug.Conn.t, atom, atom | binary, Dict.t) :: Plug.Conn.t
Specs:
- router_module(Plug.Conn.t) :: atom
Returns the router module as an atom, raises if unavailable.
Specs:
- scrub_params(Plug.Conn.t, [String.t]) :: Plug.Conn.t
Scrubs the parameters from the request.
This process is two-fold:
- Checks to see if the
required_key
is present - Changes empty parameters of
required_key
(recursively) to nils
This function is useful to remove empty strings sent
via HTML forms. If you are providing an API, there
is likely no need to invoke scrub_params/2
.
If the required_key
is not present, it will
raise Phoenix.MissingParamError
.
Examples
iex> scrub_params(conn, "user")
Specs:
- text(Plug.Conn.t, String.Chars.t) :: Plug.Conn.t
Sends text response.
Examples
iex> text conn, "hello"
iex> text conn, :implements_to_string
Specs:
- view_module(Plug.Conn.t) :: atom
Retrieves the current view.