-
Notifications
You must be signed in to change notification settings - Fork 2
Using action_fallback in controllers
I have a lot of common error code in controllers and nested
casestatements, how can I reduce and reuse this code?
You can reuse common error handling in controllers with action_fallback.
Remember that the with statement has a default else clause that returns anything that fails to pattern match, so the first :error tuple will bounce out of the with and up to your fallback controller.
Let's say we have an order controller that looks up an order by id and then validates the current user can view it. We can set this up with a with statement. We won't handle any errors in the OrderController because the fallback controller will pick up on that.
defmodule Web.OrderController do
use Web, :controller
action_fallback Web.FallbackController
def show(conn, %{"id" => id}) do
%{current_user: user} = conn.assigns
with {:ok, order} <- Orders.get(id),
{:ok, order} <- Orders.authorize(order, user) do
conn
|> assign(:order, order)
|> render("show.html")
end
end
endNow we have a product controller to look up products, this will also reuse the fallback controller for a 404 page.
defmodule Web.ProductController do
use Web, :controller
action_fallback Web.FallbackController
def show(conn, %{"id" => id}) do
with {:ok, product} <- Products.get(id)do
conn
|> assign(:product, product)
|> render("show.html")
end
end
endAnd finally our fallback controller is set up with any errors it can handle in call/2 functions. The second argument is the error that a controller action returns.
defmodule Web.FallbackController do
use Web, :controller
def call(conn, {:error, :not_found}) do
conn
|> put_status(404)
|> put_view(Web.ErrorView)
|> render(:404)
end
def call(conn, {:error, :not_authorized}) do
conn
|> put_status(401)
|> put_view(Web.ErrorView)
|> render(:401)
end
end🏳️🌈🦄 Sharing is caring. 🦄🏳️🌈
TODO: can we recreate the tag system here roughly with sections about certain subjects?