scope "/", FooWeb do
pipe_through :browser
get "/", PageController, :index
get "/about", PageController, :about
...
get "/login/web_auth", WebAuthController, :show
...
I have a route get "/login/web_auth", WebAuthController, :show, which needs to be additionally piped through the plug :requires_login on top of the :browser pipeline.
I have tried to include the plug in the show function (the other routes in the controller don’t the need to be piped through :requires_login. So, I didn’t include it in the module definition), which failed.
I have considered:
Making a nested scope login/web_auth/ which is piped through :browser and :requires_login
with a get "/" route.
Making a new controller for the route which needs :requires_login.
However, I haven’t tried the above as I wasn’t sure which is more ideal.
What is the common convention is such a scenario? How would you solve the problem?
I personally try to keep my routing flat. The part that was not obvious to me was that you can have multiple scopes on the same level with the same prefix:
#
# Web app routes
#
scope "/", MyAppWeb do
pipe_through :browser
#
# Authentication.
#
scope "/" do
pipe_through :require_unauthenticated_user
get "/sign-in", SessionController, :new
post "/sign-in", SessionController, :create
end
#
# Authenticated users only.
#
scope "/" do
pipe_through :require_authenticated_user
get "/", PageController, :index
# ...
end
end
So basically scope is a way to group routes (but it can introduce a route prefix too, like "/api").
Looking at the Hexpm code, I found that in the Controller definition, the variable action is in scope. action has the value of your action (eg: :index, :show, etc.). We can compare action and apply a plug selectively in our module declaration. That would be something like this:
defmodule FooWeb.WebAuthController do
use FooWeb, :controller
plug :requires_login when action == :show
This in my opinion is even better than @stefanchrobot’s solution (which is pretty good!).
Yes, you can conditionally plug in the controller. This makes controllers “self-contained”. On the other hand, if you plug something all the time, doing this in the router removes a lot of duplication.
The great thing about Phoenix is that you can do it the way you see fit. You could even do something like:
There’s one big advantage about putting plugs in the controller. It’s that they know the path/controller/action they are associated with, while plugs in routers run before information about the matched controller are set on the conn. This can be quite useful for certain types of plugs.