Handle HTTP requests with Phoenix controllers including actions, parameters, rendering, flash messages, and redirects
Limited to specific tools
Additional assets for this skill
This skill is limited to using the following tools:
Phoenix controllers are the intermediary modules between the router and views in a Phoenix application. They handle HTTP requests, process parameters, interact with contexts, and determine what response to send back to the client. Controllers are stateless and receive a connection struct (conn) that represents the current HTTP request.
The simplest Phoenix controller uses the HelloWeb, :controller macro and defines actions as functions that receive the connection and parameters:
defmodule HelloWeb.PageController do
use HelloWeb, :controller
def home(conn, _params) do
render(conn, :home, layout: false)
end
end
Each controller action receives:
conn - The Plug.Conn struct representing the HTTP request/responseparams - A map containing request parameters from the URL, query string, and request bodyUse pattern matching to extract specific parameters from the params map:
defmodule HelloWeb.HelloController do
use HelloWeb, :controller
def show(conn, %{"messenger" => messenger}) do
render(conn, :show, messenger: messenger)
end
end
To access both a specific parameter and the full params map, use pattern matching with the = operator:
def show(conn, %{"messenger" => messenger} = params) do
# Access to both messenger and the full params map
render(conn, :show, messenger: messenger)
end
When an action doesn't need parameters, prefix the variable with an underscore to avoid compiler warnings:
def index(conn, _params) do
render(conn, :home)
end
Action names can be customized independently of the template name:
defmodule HelloWeb.PageController do
use HelloWeb, :controller
def index(conn, _params) do
# Renders :home template but action is named :index
render(conn, :home)
end
end
Update the router accordingly:
get "/", PageController, :index
Use the render/3 function to render HTML templates via Phoenix Views:
defmodule HelloWeb.HelloController do
use HelloWeb, :controller
def show(conn, %{"messenger" => messenger}) do
render(conn, :show, messenger: messenger)
end
end
The controller and view must share a root name (e.g., HelloController and HelloHTML).
Disable the layout for specific actions:
def home(conn, _params) do
render(conn, :home, layout: false)
end
Pass data to templates using assign/3:
def show(conn, %{"messenger" => messenger}) do
conn
|> assign(:messenger, messenger)
|> render(:show)
end
Chain multiple assigns for cleaner code:
def show(conn, %{"messenger" => messenger}) do
conn
|> assign(:messenger, messenger)
|> assign(:receiver, "Dweezil")
|> render(:show)
end
For concise syntax, pass assigns directly to render/3:
def show(conn, %{"messenger" => messenger}) do
render(conn, :show, messenger: messenger, receiver: "Dweezil")
end
Configure supported response formats in your controller configuration:
def controller do
quote do
use Phoenix.Controller,
formats: [:html, :json]
...
end
end
This allows controllers to respond with different formats based on the request.
Use put_flash/3 to set temporary messages:
defmodule HelloWeb.PageController do
use HelloWeb, :controller
def home(conn, _params) do
conn
|> put_flash(:error, "Let's pretend we have an error.")
|> render(:home, layout: false)
end
end
Flash message types:
:info - Informational messages:error - Error messages:warning - Warning messages (custom):success - Success messages (custom)Remove all flash messages from the connection:
clear_flash(conn)
Combine flash messages with redirects to provide context after navigation:
def home(conn, _params) do
conn
|> put_flash(:error, "Let's pretend we have an error.")
|> redirect(to: ~p"/redirect_test")
end
Redirect to another route using the verified routes syntax:
def create(conn, params) do
# ... create logic
redirect(conn, to: ~p"/posts")
end
Include dynamic parameters in redirects:
def create(conn, params) do
# ... create post
redirect(conn, to: ~p"/posts/#{post}")
end
Redirect to external URLs:
def external(conn, _params) do
redirect(conn, external: "https://example.com")
end
Action fallback controllers handle error cases elegantly by centralizing error handling logic:
defmodule HelloWeb.MyController do
use Phoenix.Controller
action_fallback HelloWeb.MyFallbackController
def show(conn, %{"id" => id}, current_user) do
with {:ok, post} <- fetch_post(id),
:ok <- authorize_user(current_user, :view, post) do
render(conn, :show, post: post)
end
end
end
The fallback controller handles non-ok tuples:
defmodule HelloWeb.MyFallbackController do
use Phoenix.Controller
def call(conn, {:error, :not_found}) do
conn
|> put_status(:not_found)
|> put_view(HelloWeb.ErrorHTML)
|> render(:"404")
end
def call(conn, {:error, :unauthorized}) do
conn
|> put_status(403)
|> put_view(HelloWeb.ErrorHTML)
|> render(:"403")
end
end
Test controller actions using ConnCase:
defmodule HelloWeb.PostControllerTest do
use HelloWeb.ConnCase
import Hello.BlogFixtures
@create_attrs %{body: "some body", title: "some title"}
@update_attrs %{body: "some updated body", title: "some updated title"}
@invalid_attrs %{body: nil, title: nil}
describe "index" do
test "lists all posts", %{conn: conn} do
conn = get(conn, ~p"/posts")
assert html_response(conn, 200) =~ "Listing Posts"
end
end
describe "show" do
test "displays a single post", %{conn: conn} do
post = post_fixture()
conn = get(conn, ~p"/posts/#{post}")
assert html_response(conn, 200) =~ post.title
end
end
end
Assert redirects in tests:
test "redirects after create", %{conn: conn} do
conn = post(conn, ~p"/posts", post: @create_attrs)
assert %{id: id} = redirected_params(conn)
assert redirected_to(conn) == ~p"/posts/#{id}"
end
Verify flash messages are set correctly:
test "sets flash message on error", %{conn: conn} do
conn = post(conn, ~p"/posts", post: @invalid_attrs)
assert get_flash(conn, :error) == "Could not create post"
end
Use this skill when you need to:
~p sigil for route paths to catch errors at compile time~p)