编写自定义插件,它可以处理 return 正文中格式错误 JSON 的正确错误
Write custom Plug which can handle and return proper error on malformed JSON in the body
我正在尝试编写一个插件,如果请求格式不正确,它将生成自定义错误 JSON 这在我们的场景中很常见(因为我们在邮递员中使用变量。例如,有时没有在值之外引用它会导致格式错误 JSON)。我得到的唯一帮助是 https://groups.google.com/forum/#!topic/phoenix-talk/8F6upFh_lhc,这当然不起作用。
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 request
问题:问题是正文只能读取一次。 Plug.Parses 如果我之前在我的自定义插件中阅读它,将无法找到正文
在 endpoint.ex 文件中添加一个自定义主体 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 CacheBodyReader do
def read_body(conn, _opts) do
# Actual implementation
# {:ok, body, conn} = Plug.Conn.read_body(conn, opts)
# conn = update_in(conn.assigns[:raw_body], &[body | (&1 || [])])
# {:ok, body, conn}
{:ok, conn.assigns.raw_body, conn}
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
参考文献很少:
我正在尝试编写一个插件,如果请求格式不正确,它将生成自定义错误 JSON 这在我们的场景中很常见(因为我们在邮递员中使用变量。例如,有时没有在值之外引用它会导致格式错误 JSON)。我得到的唯一帮助是 https://groups.google.com/forum/#!topic/phoenix-talk/8F6upFh_lhc,这当然不起作用。
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 request
问题:问题是正文只能读取一次。 Plug.Parses 如果我之前在我的自定义插件中阅读它,将无法找到正文
在 endpoint.ex 文件中添加一个自定义主体 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 CacheBodyReader do
def read_body(conn, _opts) do
# Actual implementation
# {:ok, body, conn} = Plug.Conn.read_body(conn, opts)
# conn = update_in(conn.assigns[:raw_body], &[body | (&1 || [])])
# {:ok, body, conn}
{:ok, conn.assigns.raw_body, conn}
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
参考文献很少: