I want to redirect a user to a certain page if “some_condition” is met. For instance, a user must create an article if there’re no articles created at all. The redirect must happen from all the controllers and actions in them.
I have a plug:
defmodule P1 do
alias Plug.Conn
def init(opts), do: opts
def call(conn, opts) do
if some_condition do
Phoenix.Controller.redirect(conn, to: MyApp.Router.Helpers.article_path(conn, :new))
|> Plug.Conn.halt()
end
end
end
An issue with this is that
a) it’ll redirect all the requests to “article_path/new”, including ones to js, css, etc … files. Namely, it’ll occur multiple times in a row per one route. Instead of one single redirect, as I want.
b) a page won’t be rendered. It’ll be displayed as an empty page when I redirect a user this way
It seems your plug says what to do when some_condition is met but returns anything in the else case. Usually It should just return conn in the “else” block.
def call(conn, opts) do
if some_condition do
conn
|> redirect(to: MyApp.Router.Helpers.article_path(conn, :new))
|> Plug.Conn.halt()
else
conn
end
end
Well I just wanted to highlight that a plug in Elixir must always return conn, and a function must always return an explicit value. If these two points are not verified, nothing will work at all!
For the rest, just call the plug in the right place and it should be fine. I will suggest to define a pipeline in the router and make all the requests (except the one that will allow to create the missing resource) pass through it, and that should be fine. ^^
Well you will plug it in the endpoint only when you want it to be called for all requests without exception.
Since you want to force the user to add an article, you have to allow him to access at least the page to perform that requirement at MyApp.Router.Helpers.article_path(conn, :new).
So in the router file you have a default browser pipeline right? Just define another one and name it for example :restricted like below:
pipeline :restricted do
plug :browser
# Ensure that user has added at least one article.
plug MyApp.P1
end
Make sure all your routes pass through the restricted pipeline except the one that will allow to add an article which should pass just through the normal :browser pipeline.
Are you trying to guess the answer? Have you tried it yourself?
That won’t work – all the requests to assets still go via :browser which will cause it to get triggered multiple times if I reload a page.
I don’t need “pipeline restristed”. Do you understand my question?
I just saw your reply in my mails.
The assets are served by Plug.Static which is called in the top of your endpoint. As long as you keep the default setup all your assets should be served at the root of your endpoint url.
I don’t tried your exact Plug but I have a lot of similar plugs in my Phoenix projects that work.
For example plugs that require user to be authenticated, or to add a profile picture if authenticated etc.
None of them prevent my public assets to be served.
To put it simple you’re facing infinite redirection loop because your Plug is called for any request even the one that should allow the user to add an article. To avoid that you have to call your plug in the router as I suggested.