W3cubDocs

/Phoenix

Phoenix.Controller

Controllers are used to group common functionality in the same (pluggable) module.

For example, the route:

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

will invoke the show/2 action in the MyApp.UserController:

defmodule MyApp.UserController do
  use MyAppWeb, :controller

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

An action is 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.

Options

When used, the controller supports the following options:

  • :namespace - sets the namespace to properly inflect the layout view. By default it uses the base alias in your controller name

  • :log - the level to log. When false, disables controller logging

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 collection of low-level functions to work with the connection

  • Phoenix.Controller - functions provided by Phoenix to support rendering, and other Phoenix specific behaviour

If you want to have functions that manipulate the connection without fully implementing the controller, you can import both modules directly instead of use Phoenix.Controller.

Plug pipeline

As with routers, controllers also have their own plug pipeline. However, different from routers, controllers have a single pipeline:

defmodule MyApp.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

The :authenticate plug will be invoked before the action. If the plug calls Plug.Conn.halt/1 (which is by default imported into controllers), it will halt the pipeline and won’t inoke the action.

Guards

plug/2 in controllers supports guards, allowing a developer to configure a plug to only run in some particular action:

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

The first plug will run only when action is show or edit. The second plug will always run, except for the index action.

Those guards work like regular Elixir guards and the only variables accessible in the guard are conn, the action as an atom and the controller as an alias.

Controllers are plugs

Like routers, controllers are plugs, but they are wired to dispatch to a particular function which is called an action.

For example, the route:

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

will invoke UserController as a plug:

UserController.call(conn, :show)

which will trigger the plug pipeline and which will eventually invoke the inner action plug that dispatches to the show/2 function in the UserController.

As controllers are plugs, they implement both init/1 and call/2, and it also provides a function named action/2 which is responsible for dispatching the appropriate action after the plug stack (and is also overridable).

Overriding action/2 for custom arguments

Phoenix injects an action/2 plug in your controller which calls the function matched from the router. By default, it passes the conn and params. In some cases, overriding the action/2 plug in your controller is a useful way to inject arguments into your actions that you would otherwise need to fetch of the connection repeatedly. For example, imagine if you stored a conn.assigns.current_user in the connection and wanted quick access to the user for every action in your controller:

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

Rendering and layouts

One of the main features provided by controllers is the ability to perform content negotiation and render templates based on information sent by the client. Read render/3 to learn more.

It is also important not to confuse Phoenix.Controller.render/3 with Phoenix.View.render/3. The former expects a connection and relies on content negotiation while the latter is connection-agnostic and typically invoked from your views.

Summary

Functions

accepts(conn, accepted)

Performs content negotiation based on the available formats

action_fallback(plug)

Registers the plug to call as a fallback to the controller action

action_name(conn)

Returns the action name as an atom, raises if unavailable

allow_jsonp(conn, opts \\ [])

A plug that may convert a JSON response into a JSONP one

clear_flash(conn)

Clears all flash messages

controller_module(conn)

Returns the controller module as an atom, raises if unavailable

current_path(conn)

Returns the current request path, with and without query params

current_path(conn, params)
current_url(conn)

Returns the current request URL, with and without query params

current_url(conn, params)
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 map of previously set flash messages or an empty map

get_flash(conn, key)

Returns a message from flash by key

get_format(conn)

Returns the request format, such as “json”, “html”

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_format(conn, format)

Puts the format in the connection

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_secure_browser_headers(conn, headers \\ %{})

Put headers that improve browser security

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 template and assigns based on the conn information

render(conn, view, template, assigns)

A shortcut that renders the given template in the given view

router_module(conn)

Returns the router module as an atom, raises if unavailable

scrub_params(conn, required_key)

Scrubs the parameters from the request

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

Sends the given file or binary as a download

text(conn, data)

Sends text response

view_module(conn)

Retrieves the current view

view_template(conn)

Returns the template name rendered in the view as a string (or nil if no template was rendered)

Functions

accepts(conn, accepted)

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 preceded 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)

Custom media types

It is possible to add custom media types to your Phoenix application. The first step is to teach Plug about those new media types in your config/config.exs file:

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

The key is the media type, the value is a list of formats the media type can be identified with. For example, by using “json-api”, you will be able to use templates with extension “index.json-api” or to force a particular format in a given URL by sending “?_format=json-api”.

After this change, you must recompile plug:

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

And now you can use it in accepts too:

plug :accepts, ["html", "json-api"]

action_fallback(plug) (macro)

Registers the plug to call as a fallback to the controller action.

A fallback plug is useful to translate common domain data structures into a valid %Plug.Conn{} response. If the controller action fails to return a %Plug.Conn{}, the provided plug will be called and receive the controller’s %Plug.Conn{} as it was before the action was invoked along with the value returned from the controller action.

Examples

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

In the above example, with is used to match only a successful post fetch, followed by valid authorization for the current user. In the event either of those fail to match, with will not invoke the render block and instead return the unmatched value. In this case, imagine Blog.fetch_post/2 returned {:error, :not_found} or Authorizer.authorize/3 returned {:error, :unauthorized}. For cases where these datastructures serve as return values across multiple boundaries in our domain, a single fallback module can be used to translate the value into a valid response. For example, you could write the following fallback controller to handle the above values:

defmodule MyFallbackController do
  use Phoenix.Controller

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

  def call(conn, {:error, :unauthorized}) do
    conn
    |> put_status(403)
    |> render(MyErrorView, :"403")
  end
end

action_name(conn)

action_name(Plug.Conn.t) :: atom

Returns the action name as an atom, raises if unavailable.

allow_jsonp(conn, opts \\ [])

allow_jsonp(Plug.Conn.t, Keyword.t) :: Plug.Conn.t

A plug that may convert a JSON response into a JSONP one.

In case a JSON response is returned, it will be converted to a JSONP as long as the callback field is present in the query string. The callback field itself defaults to “callback”, but may be configured with the callback option.

In case there is no callback or the response is not encoded in JSON format, it is a no-op.

Only alphanumeric characters and underscore are allowed in the callback name. Otherwise an exception is raised.

Examples

# 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"

clear_flash(conn)

Clears all flash messages.

controller_module(conn)

controller_module(Plug.Conn.t) :: atom

Returns the controller module as an atom, raises if unavailable.

current_path(conn)

Returns the current request path, with and without query params.

By default, the connection’s query params are included in the generated path. Custom query params may be used instead by providing a map of your own params. You may also retrieve only the request path by passing an empty map of params.

Examples

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"

current_path(conn, params)

current_url(conn)

Returns the current request URL, with and without query params.

The connection’s endpoint will be used for URL generation. See current_path/1 for details on how the request path is generated.

Examples

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"

Custom URL Generation

In some cases, you’ll need to generate a request’s URL, but using a different scheme, different host, etc. This can be accomplished by concatentating the request path with a custom built URL from your Router helpers, another Endpoint, mix config, or a hand-built string.

For example, you may way to generate an https URL from an http request. You could define a function like the following:

def current_secure_url(conn, params \\ %{}) do
  cur_uri  = Phoenix.Controller.endpoint_module(conn).struct_url()
  cur_path = Phoenix.Controller.current_path(conn, params)

  MyAppWeb.Router.Helpers.url(%URI{cur_uri | scheme: "https}) <> cur_path
end

Or maybe you have a subdomain based URL for different organizations:

def organization_url(conn, org, params \\ %{}) do
  cur_uri  = Phoenix.Controller.endpoint_module(conn).struct_url()
  cur_path = Phoenix.Controller.current_path(conn, params)
  org_host = "#{org.slug}.#{cur_uri.host}"

  MyAppWeb.Router.Helpers.url(%URI{cur_uri | host: org_host}) <> cur_path
end

current_url(conn, params)

delete_csrf_token()

Deletes any CSRF token set.

endpoint_module(conn)

endpoint_module(Plug.Conn.t) :: atom

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 map of previously set flash messages or an empty map.

Examples

iex> get_flash(conn)
%{}

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

get_flash(conn, key)

Returns a message from flash by key.

Examples

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

get_format(conn)

Returns the request format, such as “json”, “html”.

html(conn, data)

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

Sends html response.

Examples

iex> html conn, "<html><head>..."

json(conn, data)

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}

layout(conn)

layout(Plug.Conn.t) :: {atom, String.t} | false

Retrieves the current layout.

layout_formats(conn)

layout_formats(Plug.Conn.t) :: [String.t]

Retrieves current layout formats.

protect_from_forgery(conn, opts \\ [])

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.

put_flash(conn, key, message)

Persists a value in flash.

Returns the updated connection.

Examples

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

put_format(conn, format)

Puts the format in the connection.

See get_format/1 for retrieval.

put_layout(conn, layout)

put_layout(Plug.Conn.t, {atom, binary | atom} | 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. It can also be set to false. In this case, no layout would be used.

Examples

iex> layout(conn)
false

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

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

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

Raises Plug.Conn.AlreadySentError if the conn was already sent.

put_layout_formats(conn, formats)

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.

put_new_layout(conn, layout)

put_new_layout(Plug.Conn.t, {atom, binary | atom} | 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.

put_new_view(conn, module)

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.

put_secure_browser_headers(conn, headers \\ %{})

Put headers that improve browser security.

It sets the following headers:

* x-frame-options - set to SAMEORIGIN to avoid clickjacking
  through iframes unless in the same origin
* x-content-type-options - set to nosniff. This requires
  script and style tags to be sent with proper content type
* x-xss-protection - set to "1; mode=block" to improve XSS
  protection on both Chrome and IE

A custom headers map may also be given to be merged with defaults.

put_view(conn, module)

put_view(Plug.Conn.t, atom) :: Plug.Conn.t

Stores the view for rendering.

Raises Plug.Conn.AlreadySentError if the conn was already sent.

redirect(conn, opts)

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"

render(conn, template_or_assigns \\ [])

render(Plug.Conn.t, Keyword.t | map | 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.

render(conn, template, assigns)

render(Plug.Conn.t, binary | atom, Keyword.t | map) :: 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 - the Plug.Conn struct

  • template - 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 returned by get_format/1. 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

  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

  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

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

will render the “show.html” template inside an “app.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/1 and put_layout_formats/2 can be used to configure which formats support/require layout rendering (defaults to “html” only).

render(conn, view, template, assigns)

render(Plug.Conn.t, atom, atom | binary, Keyword.t | map) :: Plug.Conn.t

A shortcut that renders the given template in the given view.

Equivalent to:

conn
|> put_view(view)
|> render(template, assigns)

router_module(conn)

router_module(Plug.Conn.t) :: atom

Returns the router module as an atom, raises if unavailable.

scrub_params(conn, required_key)

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 for removing 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")

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

Sends the given file or binary as a download.

The second argument must be {:binary, contents}, where contents will be sent as download, or{:file, path}, where path is the filesystem location of the file to be sent. Be careful to not interpolate the path from external parameters, as it could allow traversal of the filesystem.

The download is achieved by setting “content-disposition” to attachment. The “content-type” will also be set based on the extension of the given filename but can be customized via the :content_type and :charset options.

Options

  • :filename - the filename to be presented to the user as download
  • :content_type - the content type of the file or binary sent as download. It is automatically inferred from the filename extension
  • :charset - the charset of the file, such as “utf-8”. Defaults to none.

Examples

To send a file that is stored inside your application priv directory:

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

When using {:file, path}, the filename is inferred from the given path must may also be set explicitly.

To allow the user to download contents that are in memory as a binary or string:

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

See Plug.Conn.send_file/3 and Plug.Conn.send_resp/3 if you would like to access the low-level functions used to send files and responses via Plug.

text(conn, data)

text(Plug.Conn.t, String.Chars.t) :: Plug.Conn.t

Sends text response.

Examples

iex> text conn, "hello"

iex> text conn, :implements_to_string

view_module(conn)

view_module(Plug.Conn.t) :: atom

Retrieves the current view.

view_template(conn)

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

Returns the template name rendered in the view as a string (or nil if no template was rendered).

© 2014 Chris McCord
Licensed under the MIT License.
https://hexdocs.pm/phoenix/Phoenix.Controller.html