Redirect to html from a json endpoint

I receive JSON data from an API endpoint. I have plug :accepts, ["json"].
I would like the controller to render HTML from there with the data I collected.
Nothing is rendered.
I tried to rebuild the “browser” pipeline" with the code below but it does nothing, no render or redirection, and if I directly “redirect”, then the target controller runs but the page is not rendered neither. So I did a HTTP GET but something is missing.
In the target controller, I have a standard render(conn, "index.html").

Any guidance?

use Phoenix.Controller
import Plug.Conn

def handle(conn, p) do
conn
|> fetch_session()
|> fetch_flash()
|> put_Secure_browser_headers
|> put_root_layout({MyAppWeb.LayoutView, :root})
|> put_session(:data, p["data"])
|> put_format("html")
|> put_View(MyAppWeb.Welcome)
|> render(:welcome)
end

or

conn
|> fetch_session()
|> put_session(...)
|> redirect(to: "/welcome")
|> halt()

@ndrean What is POSTing the JSON to your API? Are you doing it from your browser? How will an API client be able to see HTML?

1 Like

It is from the browser indeed, just a

fetch("/my_endpoint, { 
 method: 'POST', 
 headers: {'Content-type': 'application/json',  'X-CSRF-Token': token}, 
 body: ...
}}

In my router, I have

plug :accepts, ["json"]
post "my_endpoint", MyAppWeb.MyCttrl, :handle

From the controller MyAppWeb.MyCtrl, I just receive the payload and want to render a template.

defmodule MyAppWeb.MyCtrl do
  use Phoenix.Controller
  def handle(conn, params) do
    conn |> ...
  end
end

With redirect you will receive HTTP GET.

If you are using javascript or form submit with method as POST - then you will hit the right route.

The configuration in this post seems to be alright. Just use a form submit or js call to trigger the path you are looking for.

what is the response you are getting with fetch api call ?

Also can you see the post route listed when you do mix phx.routes in command line ?

Right so in your javascript, you’ll make your POST, and then the server should do a redirect. Then, your javascript needs to get the redirect URL from the response headers and tell the browser to go to that URL.

1 Like

ahhh maybe, yes… I wondered if it wasn’t the browser that should initiate the redirection. Didn’t explore this because the redirect hits the other controller, and I halt() and I render in the other (“welcome”) controller.
So probably a GET with the data in the query string is more adequate. Will test.

Also can you see the post route listed when you do mix phx.routes in command line ?

Yes, I do.

what is the response you are getting with fetch api call ?

The POST endpoint is “/my_endpoint” below.
When I use redirect(conn, to: "/welcome") |> halt(), this is what I get.

[info] POST /my_endpoint
[debug] Processing with MyMapWeb.MyAuthController.handle/2
  Parameters: %{...the JS payload...}
  Pipelines: []
[debug] QUERY OK db=2.2ms queue=1.6ms idle=1470.7ms
INSERT INTO "users" AS .....
↳ MyAppWeb.MyAuthController.handle/2, at: lib/lmy_map_web/controllers/my_auth_controller.ex:11
[info] Sent 302 in 39ms
[debug] MyAppWeb.MyAuthController halted in :action/2
[info] GET /welcome
[debug] Processing with MyAppWeb.WelcomeController.index/2
  Parameters: %{}
  Pipelines: [:browser]
WELCOME

@ndrean yeah so I think what’s happening is that fetch is following the redirect (which is why you see GET /welcome` in your logs, BUT what that means is that your javascript code is following the redirect and getting the HTML as a result. That’s not really what you want. You want your javascript code to do the POST, and then tell your browser to go do the redirect path so that it can render it.

I think your elixir code is 100% correct, now you just gotta change your javascript code to do the right thing with the response.

I just tried a simple fetch/GET with a query string from the browser but the controller doesn’t render HTML neither. I also modified the POST controller to respond back with a JSON OK so when received, I chain with a fetch/GET to the next page. In both cases, the next controller is hit but even if this controller asks to render, nothing happens. Will look at “integrated” JS functions in the framework, and try to find working examples.

What do you mean? If you go to /welcome on your app, do you see HTML?

Sorry, I can go to /welcome and see something, yes, but not the way I am doing

Can you show your full javascript? It doesn’t sound like your javascript is successfully performing the redirect at the browser level, because what it should be doing is literally telling your browser to go to /welcome.

function graphAPI() {
    FB.api("/me?fields=id,email,name,picture", async function (response) {
      const post = await fetch("/auth/fbk/sdk", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-CSRF-Token": token,
        },
        body: JSON.stringify(response),
      });
      const response = await post.json();
      if (response.r == "ok") {
        console.log("ok");
        return fetch("/Welcome");
      }
    });
  }

nb: I started with a window.location.href = "my_endpoint" but Phoenix returned an error because I have a liveview there. Maybe this could be the key.

# no function clause matching in LiveMapWeb.MapLive.mount/

Version query string works…

function build(response) {
    response.picture = JSON.stringify(response.picture.data);
    const params = new URLSearchParams(response);
    return params.toString();
}

function graphAPI() {
    FB.api("/me?fields=id,email,name,picture", async function (response) {
      const url = `/auth/fbk/sdk?${build(response)}`;
      return (window.location.href = url);
   }
}

Isn’t your mount method plain one like - def mount(params, session, socket) do . Are you pattern matching on params def mount(%{"blah" => blah }, session, socket) do ?