防止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正文无效时,test PUT / cars返回400 (ApiTest)test / api_test.exs:157 **(Plug.Parsers.ParseError)格式错误的请求,引发了Jason.DecodeError异常,并显示消息“位置上的输入意外结束 10英寸代码:conn = Api.call(conn,@opts)stacktrace :(插头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:(测试)

我很傻。

修复失败

为避免这种情况,我尝试将handle_errors函数修改为以下内容,但仍然失败:

def handle_errors(conn,"Something went wrong")
    {:error,:something_went_wrong}
end

我似乎对错误没有任何控制。

问题

如何防止再次引发此错误,而仅返回测试中出现的错误消息?

解决方法

我认为您不应该阻止再次提出该错误。我认为问题在于您的测试没有预期的错误。

您可以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","")

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...