Can we use Controllers as mere plugs?

I wanted to use a controller as an intermediate plug, but without rendering anything specific.

defmodule ModWeb.MyController do
  use ModWeb, :controller
  plug :put_layout, false

  def myfunc(conn, _params) do
    ...
    conn
  end
end

that I would then use like this:

conn
|> ...
|> ModWeb.MyController.call(:myfunc)
|>...

But when I do so I get an error telling me that the function ModWeb.MyView.render/2 is undefined : What I understand is that Phoenix tries to call render on some view associated with the controller, but I did not ask him to.

Should we always design things so that Controllers works with Views ?

  • If yes, what for instance if I want just to send some json in a request ? Also in this case, is there a natural place where to put the code which has neither to do with controllers, nor with schemas/database ?

  • If no, how to do so ?

Phoenix controllers are designed to be the last thing called in a pipeline. You can write your own plugs for intermediary steps.

defmodule MyAppWeb.Plugs.Something do
  use Plug.Builder

  def call(conn, _opts) do
    conn
    |> myfunc()
  end
end
1 Like

But they’re still proper plugs by themselves. Just because phoenix calls them at the end doesn’t mean you cannot manually call them beforehand. If that’s useful is a different question though :smiley:

Controllers should work just fine without any Views.

Thanks for your answers.

What do you mean by “Phoenix calls them at the end” ? Phoenix calls them when we ask Phoenix to call them, isn’t that right ?

Otherwise, in my case, Controllers do not work fine without any views : Phoenix tries to call the render method of the view which is supposed to be attached to the controller, but which does not exists; and I don’t know what causes that.

I’m happy to only use Controller when I have views attached to them. With this question, I was hopping to understand a bit more how things work in Phoenix.

Uh, it shouldn’t be doing that… What is a git url so we can try to git clone and test it out to see what is happening? Preferably with a test that directly demonstrates the issue?

Thanks for your help.

I’m not sure how to make it easy with a git url, especially dealing with the dependencies (which are quite many files by default). But the behavior I describe is quite easy to achieve by starting from a fresh project:

mix phx.new test --no-webpack

answer ‘Y’ to installing dependencies

mix ecto.create

Then you can add this function into router.ex

defp authenticate_user(conn, _) do
  case get_session(conn, :user_id) do
    nil ->
      conn
      |> TestWeb.SessionController.call(:create_anonymous)
  end
end

and modify this line in router.ex

- pipe_through :browser
+ pipe_through [:browser, :authenticate_user]

Then simply add a new controller in a file session_controller.ex :

defmodule TestWeb.SessionController do
  use TestWeb, :controller

  plug :put_layout, false

  def create_anonymous(conn, _params) do
    conn
  end
end

Finally run mix phx.server and visit url http://localhost:4000/

Oh if that’s all you are doing then the SessionController will override your PageController’s settings as it injected first, you need to be sure to clear out the controller-specific settings (or just don’t use use TestWeb, :controller or use Phoenix’s Controller at all). I bet it’s not the controller you are using as a plug that is failing but rather the render call or one of it’s kin at the controller you end up eventually accessing? What is your full stacktrace, that should always be shown. :slight_smile:

2 Likes

Thanks a lot for the information. I think I roughly understand why this is happening.
I will then use a mere Plug :slight_smile:

1 Like