How can I get the Plug.Conn body before it is consumed by parsers?

I am trying to debug some stuff in one of my controllers and I need access to the raw post body before it is consumed by parsers (namely the json parser). I tried doing this: https://hexdocs.pm/plug/Plug.Parsers.html#module-custom-body-reader and adding some IO.puts inside the CacheBodyReader, but that didn’t print anything to the console like I would expect (meaning it isn’t running). Here is the relevant code (in my router.ex):

defmodule CacheBodyReader do
  def read_body(conn, opts) do
    {:ok, body, conn} = Plug.Conn.read_body(conn, opts)
    IO.puts("TESTING")
    conn = update_in(conn.assigns[:raw_body], &[body | (&1 || [])])
    {:ok, body, conn}
  end
end

pipeline :paypal_webhook do
  plug Plug.Parsers,
    parsers: [:urlencoded, :json],
    pass: ["text/*"],
    body_reader: {CacheBodyReader, :read_body, []},
    json_decoder: Jason
  plug PhilomenaWeb.PaypalWebhookValidatorPlug
end

Is there something I’m missing? I have IO.puts in the PaypalWebhookValidatorPlug call function and those are printing just fine (but by then the connection body is empty as it has been consumed by the json parser and it’s been merged into conn.params). The fact that I see those puts tells me my pipeline is being executed. If anyone could give me some info that would be great. Thanks.

Maybe try writing a custom plug from scratch and put it right before the parsers plug

I just tried this and it didn’t seem to work. I created

defmodule PhilomenaWeb.TestPlug do
  def init([]), do: false
  def call(conn, _opts) do
    IO.puts("TEST PLUG")
    {:ok, body, _conn} = Plug.Conn.read_body(conn)
    IO.puts(body)
    conn
  end
end

and it definitely ran (it printed the message), however it printed an empty line for the IO.puts(body) call. I made sure to plug PhilomenaWeb.TestPlug before the Plug.Parsers one inside my router.ex file. So it seems something is running before the plugs and consuming the body somehow.

According to the docs if it’s read it gets totally consumed so, I guess it’s possible that you have some plug earlier on that’s consuming the whole thing and leaving nothing for your plug.parsers. what happens if you inspect the whole plug.conn, presumably whatever consumed it left some sort of artifact behind.

We use a version of the code from this comment at work, does the job.

2 Likes

That works! I think the thing I missed was the endpoint.ex file. I already had some Plug.Parsers stuff in there so I just had to change it a little bit to use the CacheBodyReader. Thanks

1 Like