Plug.Conn.assign not working when called from a Pipeline Plug

5.5k views Asked by At

I'm following the Phoenix Guide on Plugs to create my own Module Plug that loads the current user from session. The @user is not being assigned when the Plug Module is used, but works fine when I call it as a private function within router.ex.

This is my web/router:

defmodule MyApp.Router do
  use MyApp.Web, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug MyApp.Plugs.User
  end

  # Scopes and Routes...

end

This is my Module (in web/plugs/user.ex):

defmodule MyApp.Plugs.User do
  import Plug.Conn

  def init(default), do: default

  def call(conn, default) do
    user = %{
      id:       get_session(conn, :user_id),
      username: get_session(conn, :username)
    }

    assign(conn, :user, user)

    IO.inspect conn
  end
end

I tried inspecting it to see if it really was being assigned, but it wasn't:

%Plug.Conn{adapter: {Plug.Adapters.Cowboy.Conn, :...}, assigns: %{},
 before_send: [#Function<1.75757453/1 in Plug.CSRFProtection.call/2>,
  #Function<1.30521811/1 in Phoenix.Controller.fetch_flash/2>,
  #Function<0.39713025/1 in Plug.Session.before_send/2>,
  #Function<1.7608073/1 in Plug.Logger.call/2>,
  #Function<0.72065156/1 in Phoenix.LiveReloader.before_send_inject_reloader/1>],
 body_params: %{},
 cookies: ....
1

There are 1 answers

2
Paweł Obrok On BEST ANSWER

Plug.Conn.assign returns a modified connection. Since all data in Elixir is immutable it's not possible to modify the old one. In your case you're throwing away the result of assign and conn still points to the old connection. You probably want something like:

conn = assign(conn, :user, user)

This will rebind the conn variable to point to the modified connection structure. Of course it would also work if assign(conn, :user, user) would be the last expression in your function since its result would be returned.