防止 Plug.ErrorHandler 在回调后重新引发错误
Prevent Plug.ErrorHandler from re-raising error after callback
背景
我有一个简单的 Plug 路由器,它有一个接收 JSON 主体的 PUT 端点。为了解析它,我使用 Plug.Parsers.
问题
Plug.Parsers
插件工作正常并将 json 放在 conn.body_params
中。但是,如果我收到的 JSON 格式不正确,我的应用程序就会出现错误。为了防止这种情况,我正在使用 Plug.ErrorHandler 但由于它在之后重新引发错误,应用程序仍然爆炸。
代码
这是我的路由器。
defmodule Api do
use Plug.{Router, ErrorHandler}
alias Api.Controllers.{Products, NotFound}
plug Plug.Logger
plug :match
plug Plug.Parsers,
parsers: [:urlencoded, :json],
pass: ["text/*"],
json_decoder: Jason
plug :dispatch
put "/products", do: Products.process(conn)
match _, do: NotFound.process(conn)
def handle_errors(conn, %{kind: _kind, reason: _reason, stack: _stack}) do
send_resp(conn, conn.status, "Something went wrong")
end
end
需要注意的是,实际上 Products.process
没有(或不应该)被调用,因为 Plug.Parsers
之前加注了。
这是我的测试:
test "returns 400 when the request is not a valid JSON" do
# Arrange
body_params = "[{\"id\": 1}" # this is not valid JSON
conn =
:put
|> conn("/products", body_params)
|> put_req_header("accept", "application/json")
|> put_req_header("content-type", "application/json")
# Act
conn = Api.call(conn, Api.init([]))
# Assert
assert conn.state == :sent
assert conn.status == 400
assert conn.resp_body == "Invalid JSON in body request"
end
错误
正如您可能猜到的那样,我期待 return 400 请求和一条很好的错误消息。相反,我得到这个:
test PUT /cars returns 400 when the request has invalid JSON body
(ApiTest) test/api_test.exs:157
** (Plug.Parsers.ParseError) malformed request, a Jason.DecodeError exception was raised with message “unexpected end of input at position
10” code: conn = Api.call(conn, @opts) stacktrace: (plug 1.10.4)
lib/plug/parsers/json.ex:88: Plug.Parsers.JSON.decode/2 (plug 1.10.4)
lib/plug/parsers.ex:313: Plug.Parsers.reduce/8 (api 0.1.0)
lib/api.ex:1: Api.plug_builder_call/2 (api 0.1.0)
lib/plug/error_handler.ex:65: Api.call/2 test/api_test.exs:168: (test)
我比较傻眼
修复失败
为了避免这种情况,我尝试将 handle_errors
函数修改为以下内容,但仍然失败:
def handle_errors(conn, %{kind: _kind, reason: _reason, stack: _stack}) do
send_resp(conn, conn.status, "Something went wrong")
{:error, :something_went_wrong}
end
我所做的一切似乎都无法控制错误。
问题
如何防止此错误再次出现并简单地 return 我在测试中收到的错误消息?
我认为你不应该阻止来自 re-raising 的错误。我认为问题是您的测试没有预料到错误。
你可以catch the error:
assert %Plug.Parsers.ParseError{} =
catch_error(Api.call(conn, Api.init([])))
或者您可以通过使用 HTTP 客户端测试您的端点来避免该问题,而不是直接使用您的插件。例如,使用特斯拉:
assert %{status: 400, body: "Invalid JSON in body request"} =
Tesla.put!(@base_url <> "/products", "")
背景
我有一个简单的 Plug 路由器,它有一个接收 JSON 主体的 PUT 端点。为了解析它,我使用 Plug.Parsers.
问题
Plug.Parsers
插件工作正常并将 json 放在 conn.body_params
中。但是,如果我收到的 JSON 格式不正确,我的应用程序就会出现错误。为了防止这种情况,我正在使用 Plug.ErrorHandler 但由于它在之后重新引发错误,应用程序仍然爆炸。
代码
这是我的路由器。
defmodule Api do
use Plug.{Router, ErrorHandler}
alias Api.Controllers.{Products, NotFound}
plug Plug.Logger
plug :match
plug Plug.Parsers,
parsers: [:urlencoded, :json],
pass: ["text/*"],
json_decoder: Jason
plug :dispatch
put "/products", do: Products.process(conn)
match _, do: NotFound.process(conn)
def handle_errors(conn, %{kind: _kind, reason: _reason, stack: _stack}) do
send_resp(conn, conn.status, "Something went wrong")
end
end
需要注意的是,实际上 Products.process
没有(或不应该)被调用,因为 Plug.Parsers
之前加注了。
这是我的测试:
test "returns 400 when the request is not a valid JSON" do
# Arrange
body_params = "[{\"id\": 1}" # this is not valid JSON
conn =
:put
|> conn("/products", body_params)
|> put_req_header("accept", "application/json")
|> put_req_header("content-type", "application/json")
# Act
conn = Api.call(conn, Api.init([]))
# Assert
assert conn.state == :sent
assert conn.status == 400
assert conn.resp_body == "Invalid JSON in body request"
end
错误
正如您可能猜到的那样,我期待 return 400 请求和一条很好的错误消息。相反,我得到这个:
test PUT /cars returns 400 when the request has invalid JSON body (ApiTest) test/api_test.exs:157 ** (Plug.Parsers.ParseError) malformed request, a Jason.DecodeError exception was raised with message “unexpected end of input at position 10” code: conn = Api.call(conn, @opts) stacktrace: (plug 1.10.4) lib/plug/parsers/json.ex:88: Plug.Parsers.JSON.decode/2 (plug 1.10.4) lib/plug/parsers.ex:313: Plug.Parsers.reduce/8 (api 0.1.0) lib/api.ex:1: Api.plug_builder_call/2 (api 0.1.0) lib/plug/error_handler.ex:65: Api.call/2 test/api_test.exs:168: (test)
我比较傻眼
修复失败
为了避免这种情况,我尝试将 handle_errors
函数修改为以下内容,但仍然失败:
def handle_errors(conn, %{kind: _kind, reason: _reason, stack: _stack}) do
send_resp(conn, conn.status, "Something went wrong")
{:error, :something_went_wrong}
end
我所做的一切似乎都无法控制错误。
问题
如何防止此错误再次出现并简单地 return 我在测试中收到的错误消息?
我认为你不应该阻止来自 re-raising 的错误。我认为问题是您的测试没有预料到错误。
你可以catch the error:
assert %Plug.Parsers.ParseError{} =
catch_error(Api.call(conn, Api.init([])))
或者您可以通过使用 HTTP 客户端测试您的端点来避免该问题,而不是直接使用您的插件。例如,使用特斯拉:
assert %{status: 400, body: "Invalid JSON in body request"} =
Tesla.put!(@base_url <> "/products", "")