I’ve implemented a module implementing the behaviour of Plug.Parsers
From the doc I followed, my parse/5 callback returns {:ok, params, conn} when the body has been successfully parsed.
But when there is a parsing error in the body, I think I have to return {:next, conn}
so that Plug can go on to the next parsers.
When this happens, I have errors in my logs and the clients gets a 500 error, I would expect him to get a 415 or a 400.
Obviously I need to add something in my code to handle gracefully Plug.Parsers.UnsupportedMediaTypeError
Where should I put this ?
Here is an extract of my parse function:
require Logger
require Plug.Parsers.ParseError
defmodule Wsdataselect.BodyParser.ParseError do
defexception message: "Malformed body"
end
defmodule Wsdataselect.BodyParser do
@moduledoc """
This modules parses the body of a HTTP request to generate a %Wsdataselect.DataRequest{} structure.
"""
alias Wsdataselect.{DataRequest, Stream}
@behaviour Plug.Parsers
def init(opts), do: opts
@doc """
Parse the body of the request in order to return a %Wsdataselect.DataRequest{}
This function implements the behaviour of Plug.Parsers.
"""
@spec parse(Plug.Conn.t(), any, any, any, any) :: {:ok, DataRequest.t(), Plug.Conn.t()} | {:error, :too_large, Plug.Conn.t()} | {:next, Plug.Conn.t()}
def parse(conn, _, _, _, _) do
{:ok, body, conn} = Plug.Conn.read_body(conn)
body_list = String.split(body, "\n", trim: true)
if length(body_list) > Application.get_env(:wsdataselect, :post_max_lines) do
{:error, :too_large, conn}
else
case Enum.reduce(body_list, %DataRequest{}, &parse_line/2) do
{:error, m} ->
Logger.error(m)
{:next, conn}
datareq ->
Logger.debug("Body parsed: #{inspect(datareq)}")
{:ok, datareq, conn}
end
end
end
And Here my router:
defmodule Wsdataselect.Router do
@moduledoc """
This module is the entrypoint of HTT requests from the user. It uses Plug to manage the requests.
"""
require Logger
require DateTime
alias Wsdataselect.Controller
use Plug.Router
use Plug.ErrorHandler
use Plug.Builder
plug Plug.Logger
plug Plug.Static,
at: "/",
from: {:wsdataselect, "priv/static"},
content_types: %{"application.wadl" => "text/xml; charset=utf-8", "index.html" => "text/html"}
plug Plug.RequestId
plug :match
plug Plug.Parsers, parsers: [Wsdataselect.BodyParser]
plug :dispatch
post "/query" do
Logger.debug("Body: #{inspect(conn.body_params)}")
conn
|> Controller.post_query()
end
When a body with wrong format is POSTed, the logs are:
16:07:57.070 request_id=F4B8-uvNBI9DfjkAAACE [error] GenServer #PID<0.637.0> terminating
** (Plug.Parsers.UnsupportedMediaTypeError) unsupported media type application/json
Where in my code should I put a Plug.Conn.send_resp(conn, 400, "Malformed body")
?