HMAC, Elixir, Plug.Conn (trying to call read_body

2019-05-24 11:45发布

问题:

I'm struggling with an issue where something is reading the body of an http request before Plug.Parsers.JSON gets it in the pipeline. Because of this, read_body in the plug for json times out--you can't read the body twice.

We have an HMAC implementation in an earlier plug in our pipeline and it reads the body in some cases. Is there a pattern for how use of the body is to behave in Plug? I mean, if we can only read it once, and it has to be decoded in Plug.Parsers.JSON, well...it's not going to work.

Follow on question. Do we need to include the request body when we generate the HMAC hash? I mean, it feels to me like we have to do that, but I've thought myself in a circle at this point.

thanks!

回答1:

You can pass a custom :body_reader option to Plug.Parsers in order to cache the body for later use.

You'll want to not read the body before the Parser and instead cache the body to read later from your plug that wants to hash it.

Option:

:body_reader - an optional replacement (or wrapper) for Plug.Conn.read_body/2 to provide a function that gives access to the raw body before it is parsed and discarded. It is in the standard format of {Module, :function, [args]} (MFA) and defaults to {Plug.Conn, :read_body, []}.

Example:

Sometimes you may want to customize how a parser reads the body from the connection. For example, you may want to cache the body to perform verification later, such as HTTP Signature Verification. This can be achieved with a custom body reader that would read the body and store it in the connection, such as:

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

which could then be set as:

plug Plug.Parsers,
  parsers: [:urlencoded, :json],
  pass: ["text/*"],
  body_reader: {CacheBodyReader, :read_body, []},
  json_decoder: Jason

It was added in Plug v1.5.1.