Search code examples
jsonelixirhtml-parsingplug

Plug Parser: raising ParseError


I'm writing a Plug Parser that among other things decodes JSON using Poison (I'd prefer to let Plug.Parsers.JSON do that, but I need to read the raw request body to verify it against a signature, so that's not possible).

I'm using Poison.decode/2 to decode the json. This returns an {:error, ...} tuple on error. As a Plug parser, I think I am expected to raise Plug.Parsers.ParseError if there was an error during the parsing. However ParseError expects an exception struct. I don't have one of those, I only have the tuple returned from Poison.decode/2.

As a workaround, I can use use Poison.decode!/2 and rescue the raised error, re-raising it as a ParseError, but that seems weird when the non-raising decode/2 is available.

So my questions are, in increasing abstractness:

  1. How do I raise a ParseError from a parser without a source exception?
  2. Do I need to raise the ParseError, or it it better to raise my own exception?
  3. Is there a better way altogether, allowing me to verify the signature without re-implementing the JSON parsing?

Solution

  • (I'd prefer to let Plug.Parsers.JSON do that, but I need to read the raw request body to verify it against a signature, so that's not possible).

    ...

    1. Is there a better way altogether, allowing me to verify the signature without re-implementing the JSON parsing?

    Letting Plug.Parsers.JSON do that is in fact the best option. Since version 1.5.1 it's possible to provide a custom body reader to the parser that can cache the body for later use. This is a much more generic solution than re-implementing a JSON-parser plug.

    Here is my custom reader:

    def read_body(conn, opts) do
      case Plug.Conn.read_body(conn, opts) do
        {res, body, conn} when res in [:ok, :more] ->
          {res, body, update_in(conn.assigns[:raw_body], &((&1 || "") <> body))}
    
        unknown ->
          unknown
      end
    end