首页 > 解决方案 > 防止 Plug.ErrorHandler 在回调后重新引发错误

问题描述

背景

我有一个简单的 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

错误

正如您可能猜到的,我希望请求返回 400 和一个很好的错误消息。相反,我得到了这个:

当请求具有无效的 JSON 正文 (ApiTest) test/api_test.exs:157 ** (Plug.Parsers.ParseError) 格式错误的请求时,测试 PUT /cars 返回 400,引发 Jason.DecodeError 异常,并显示消息“意外结束于位置 10” 代码: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 测试/api_test.exs:168: (测试)

我比较傻眼。

修复失败

为了避免这种情况,我尝试将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

我所做的一切似乎都无法控制错误。

问题

我怎样才能防止这个错误再次出现并简单地返回我在测试中得到的很好的错误消息?

标签: jsonelixirplug

解决方案


我认为你不应该阻止错误重新加注。我认为问题在于您的测试没有预料到错误。

您可能会发现错误

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", "")

推荐阅读