What is proper way to pass a data between controller?

Hi everyone!
As title says, I am trying to send submitted form information to different controller.
What I am actually doing is this.

  1. Customer sign
  2. Verify Phone number
  3. Enter verification code.
  4. Confirm.

In Step 2, customer submit the form with phone number. and I want it to be passed to different controller.
In Step 2, post controller is this.

defmodule MyApp.PhoneVerifyController do
    def create(conn, %{"phone_verify" => %{"phone_number" => phone_number}}) do
        	with {:ok, message} <- Messageman.verify_start(phone_number) do
        		conn
        		|> put_flash(:info, message)
        		|> assign(:phone_number, phone_number)
        		|> redirect(to: code_verify_path(conn, :new))
        	else
        	{:error, reason, _http_status_code} ->
        		conn
        		|> put_flash(:error, reason)
        		|> render("new.html")
        	end
    end
end

I tried using assign(:phone_number, phone_number) to pass a phone number to controller.
and in Step 3 I tried to check if its value is available like this

defmodule MyApp.CodeVerifyController do
  def new(conn, _params) do
    IO.puts conn.assigns.phone_number
  end
end

but it returns nil.

I heard that in every request, conn is reset. if it is correct, How can I pass a data between controller?

Please understand, I am a very new to Phoenix Framework :slight_smile:

Thanks in adavance.

1 Like

What do you mean by this?

Move this logic to regular modules, or other GenServers, abstract it away from your controllers.

3 Likes

Yep, what @entone said, controllers should not be ‘doing’ anything, only be a thin layer between the modules that do the work, and the views that display it, nothing more.

Can you show me an example so I can get an idea?

What he means is that You don’t need to store data inside conn, You could delegate to some sort of micro(nano)-service.

In Elixir/Phoenx, there are plenty of ways to store state. That could be database, but that can be processes, ets table etc… There is even a special GenServer, Agent, done for this.

But if You are new to Elixir, You might not know about OTP.

After all, your API is not that big, maybe

  • create token
  • cancel token
  • check token
  • expire token

Depending on what You want, it can be also a more complex example, including supervisors, genservers, dynamic_supervisors, workers.

I have been doing something similar, with SMS confirmation.

The idea is to have the controller play the middleman between the customer and the service.

[Just passing through] I got the idea of using GenServers or an agent, but wouldn’t you need an id to keep track of each phone number that is waiting? How could that be if the user still not logged in, maybe session id ? If so why not store it in the session ?

What I did is to create an uuid and pass it to the user… on create success.

and to confirm the phone, the user has to pass back the uuid to the server.

Because I wanted the request to expire after some time… 5 minutes, I spawned a worker, with a timeout. If the user does not use it, it would be deleted automatically.

BTW I do not use session, but Phoenix Token, as there is also a mobile client.

1 Like

Nice way! Thanks for sharing

You should read a bit about HTTP protocol, and how redirects work. In short:

  1. HTTP protocol is stateless - you can retrieve only data that is passed through requests
  2. Your “redirect” function returns to browser very small response, with 301 status code and new address.
  3. Your browser sends new request to hit your new URL, that was retrieved by redirect.
  4. This request after series of mutations “appears” in your code controller.

Because this request is new, and has 0 connection to previous, it has new Conn structure, new assigns and everything new.

Now, the question is

How to store data across requests?

The simplest answer is: session

Phoenix uses sessions for flash messages, for example, but you can use it to store your “wizard” middle data.

3 Likes