编写自定义插件,该插件可以处理并返回正文中格式错误的JSON的正确错误

编写自定义插件,该插件可以处理并返回正文中格式错误的JSON的正确错误,json,parsing,elixir,phoenix-framework,plug,Json,Parsing,Elixir,Phoenix Framework,Plug,我正在尝试编写一个插件,如果请求的JSON格式不正确,它将生成一个自定义错误,这在我们的场景中非常常见(因为我们在postman中使用变量。例如,有时值之外没有引号,这会导致格式不正确的JSON)。我得到的唯一帮助当然是无效的 defmodule PogioApi.Plug.PrepareParse do import Plug.Conn @env Application.get_env(:application_api, :env) def init(opts) do o

我正在尝试编写一个插件,如果请求的JSON格式不正确,它将生成一个自定义错误,这在我们的场景中非常常见(因为我们在postman中使用变量。例如,有时值之外没有引号,这会导致格式不正确的JSON)。我得到的唯一帮助当然是无效的

defmodule PogioApi.Plug.PrepareParse do
  import Plug.Conn
  @env Application.get_env(:application_api, :env)

  def init(opts) do
    opts
  end

  def call(conn, opts) do
    %{method: method} = conn
    # TODO: check for PUT aswell
    if method in ["POST"] and not(@env in [:test]) do
      {:ok, body, _conn} = Plug.Conn.read_body(conn)
      case Jason.decode(body) do
        {:ok, _result} -> conn
        {:error, _reason} ->
          error = %{message: "Malformed JSON in the body"}
          conn
          |> put_resp_header("content-type", "application/json; charset=utf-8")
          |> send_resp(400, Jason.encode!(error))
          |> halt
      end
    else
      conn
    end
  end
end

这条线

{:ok, body, _conn} = Plug.Conn.read_body(conn)
如何正确读取和解析正文。我知道在POST中,我们总是会得到format=JSON请求


问题:问题是正文,只能阅读一次。如果我以前在endpoint.ex文件中的自定义插件中阅读过body,那么Plug.Parses将无法找到body。请按以下顺序添加自定义body reader和自定义插件

plug Api.Plug.PrepareParse # should be called before Plug.Parsers

plug Plug.Parsers,
  parsers: [:urlencoded, :multipart, :json],
  pass: ["*/*"],
  body_reader: {CacheBodyReader, :read_body, []}, # CacheBodyReader option is also needed
  json_decoder: Phoenix.json_library()
定义一个

然后,您的自定义解析将准备就绪

defmodule Api.Plug.PrepareParse do
  import Plug.Conn
  @env Application.get_env(:application_api, :env)
  @methods ~w(POST PUT PATCH PUT)

  def init(opts) do
    opts
  end

  def call(conn, opts) do
    %{method: method} = conn

    if method in @methods and not (@env in [:test]) do
      case Plug.Conn.read_body(conn, opts) do
        {:error, :timeout} ->
          raise Plug.TimeoutError

        {:error, _} ->
          raise Plug.BadRequestError

        {:more, _, conn} ->
          # raise Plug.PayloadTooLargeError, conn: conn, router: __MODULE__
          error = %{message: "Payload too large error"}
          render_error(conn, error)

        {:ok, "" = body, conn} ->
          body = "{}" // otherwise error
          update_in(conn.assigns[:raw_body], &[body | &1 || []])

        {:ok, body, conn} ->
          case Jason.decode(body) do
            {:ok, _result} ->
              update_in(conn.assigns[:raw_body], &[body | &1 || []])

            {:error, _reason} ->
              error = %{message: "Malformed JSON in the body"}
              render_error(conn, error)
          end
      end
    else
      conn
    end
  end

  def render_error(conn, error) do
    conn
    |> put_resp_header("content-type", "application/json; charset=utf-8")
    |> send_resp(400, Jason.encode!(error))
    |> halt
  end
end
参考文献很少:


  • 请说明您的问题是什么,以及具体失败的是什么(例如,您收到的错误消息等。)确定更新问题
    defmodule Api.Plug.PrepareParse do
      import Plug.Conn
      @env Application.get_env(:application_api, :env)
      @methods ~w(POST PUT PATCH PUT)
    
      def init(opts) do
        opts
      end
    
      def call(conn, opts) do
        %{method: method} = conn
    
        if method in @methods and not (@env in [:test]) do
          case Plug.Conn.read_body(conn, opts) do
            {:error, :timeout} ->
              raise Plug.TimeoutError
    
            {:error, _} ->
              raise Plug.BadRequestError
    
            {:more, _, conn} ->
              # raise Plug.PayloadTooLargeError, conn: conn, router: __MODULE__
              error = %{message: "Payload too large error"}
              render_error(conn, error)
    
            {:ok, "" = body, conn} ->
              body = "{}" // otherwise error
              update_in(conn.assigns[:raw_body], &[body | &1 || []])
    
            {:ok, body, conn} ->
              case Jason.decode(body) do
                {:ok, _result} ->
                  update_in(conn.assigns[:raw_body], &[body | &1 || []])
    
                {:error, _reason} ->
                  error = %{message: "Malformed JSON in the body"}
                  render_error(conn, error)
              end
          end
        else
          conn
        end
      end
    
      def render_error(conn, error) do
        conn
        |> put_resp_header("content-type", "application/json; charset=utf-8")
        |> send_resp(400, Jason.encode!(error))
        |> halt
      end
    end