I talked earlier about creating and using a current_user in Phoenix,
but I didn’t explain
how to require a current_user for certain controller actions. In Rails I would
use a before_filter (or I guess now before_action). I wasn’t sure how to do
this in Phoenix, though.
There are two things to figure out:
- What is the equivalent of
before_actionin Phoenix? - How should I share that code in all of my controllers?
Use a plug when you would use a before_filter
You can read the documentation for plugs for more details, but the general idea would be something like:
defmodule App.SomeController do
use App.Web, :controller
plug :require_user when action in [:new, :create]
# actions go here
endThis would run the require_user function before the new and create action,
just like you would do with a before_action in Rails.
Put code in web.ex to share it with all controllers
In Rails you can add methods to ApplicationController and, since all your other
controllers inherit from it, they will also have the same methods. As such, you
define general methods like require_user there so you can use them everywhere.
Elixir is not object oriented so you cannot use inheritance. Instead, there are
a series of macros inside the App.Web module that get run for various types of
modules. For example, the code inside the controller function gets put into
any module where you write use App.Web, :controller. As such, you can add code
to that function and all your controllers will “inherit” it.
What should the require_user function look like?
Currently, I have a module like this:
defmodule App.RequireUser do
def require_user(conn, _) do
if App.UserSession.current_user?(conn) do
conn
else
conn |>
Phoenix.Controller.put_flash(:error, "You must be signed in to view that page.") |>
Phoenix.Controller.redirect(to: "/") |>
Plug.Conn.halt
end
end
endThe general way it works is: if there is a current user, it just returns the
conn without changing anything. This means the controller action will happen
as normal. If there isn’t a user signed in, I add an error message to the flash,
redirect to the home page, and then halt the connection so no further code is
run. Depending on your app, you may just want to return a 403 status code and
not redirect or do something totally different.
Then, I add import App.RequireUser to the quote do block inside the
controller function in App.Web so all the controllers have access to it. I
could also just import App.RequireUser in each controller that needs it, but
all my controllers need it so it makes sense to do it in a single place for me.
I hope this further helps you dealing with the concept of current_user in your
Phoenix applications.