如何覆盖 Dropwizard 的默认资源异常处理?

Ric*_*ast 8 java logging exception dropwizard

假设我在 Dropwizard 中有一个端点,比如说

@GET
public Response foo() { throw new NullPointerException(); }
Run Code Online (Sandbox Code Playgroud)

当我到达这个端点时,它会记录异常和所有内容,这太棒了!我喜欢它。我不太喜欢的是它向用户返回一个大的状态对象status: ERROR(这很好)以及一个巨大的堆栈跟踪,我对此不太感兴趣。

显然,最好自己捕获并处理异常,但有时它们会漏掉。每次围绕整个资源编写一个 try catch 块很好,但是(a)它很麻烦,并且(b)我总是更喜欢自动化解决方案而不是“你必须记住”的解决方案。

所以我想要的是执行以下操作的东西:

  1. 记录堆栈跟踪(我使用 slf4j 但我认为它适用于任何情况)
  2. 返回通用错误响应,它不会暴露有关我的服务器的潜在特权信息!

我觉得必须有一种内置的方法来做到这一点——它已经以一种相对较好的方式处理异常——但搜索文档并没有发现任何东西。对此有好的解决办法吗?

Ric*_*ast 5

正如 reek 在评论中提到的,答案是ExceptionMapper。你需要一个这样的课程:

@Provider
public class RuntimeExceptionMapper implements ExceptionMapper<RuntimeException> {
    @Override
    public Response toResponse(RuntimeException runtime) {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以在该方法中执行您喜欢的任何日志记录等操作toResponse,返回值是实际发送给请求者的值。这样您就可以完全控制,并且应该设置合理的默认值 - 请记住这是针对漏掉的错误,而不是针对您实际期望看到的错误!这也是根据您收到的异常类型设置不同行为的好时机。

run要真正使其执行任何操作,只需在主 dropwizard 应用程序的方法中插入以下行(或类似行) :

environment.jersey().register(new RuntimeExceptionMapper());
Run Code Online (Sandbox Code Playgroud)

其中environmentEnvironment应用程序run方法的参数。现在,当你RuntimeException在某个地方有一个未捕获的东西时,这将会触发,而不是之前掉落巫师所做的任何事情。

注意:这仍然不是不仔细捕获和处理异常的借口!


Nam*_*man 0

已经在评论下提到了这一点,但后来我想我会用一个用例来尝试一下。

建议你开始区分Exception你要扔的东西。对您知道的故障使用自定义异常,并通过漂亮的日志记录抛出异常。同时RuntimeException实际​​上应该修复。无论如何,如果您不想向最终用户显示堆栈跟踪,您可能可以捕获一般异常,记录详细信息并相应地自定义响应和实体。

您可以定义一个

public class ErrorResponse { 

    private int code;
    private String message;

    public ErrorResponse() {
    }

    public ErrorResponse(int code, String message) {
        this.code = code;
        this.message = message;
    }
    ... setters and getters
}
Run Code Online (Sandbox Code Playgroud)

然后在您的资源代码中,您可以将该方法修改为 -

@GET
public Response foo() { 
   try {
       ...
       return Response.status(HttpStatus.SC_OK).entity(response).build();
   } catch (CustomBadRequestException ce) {
       log.error(ce.printStackTrace());
       return Response.status(HttpStatus.SC_BAD_REQUEST).entity(new ErrorResponse(HttpStatus.SC_BAD_REQUEST, ce.getMessage())).build();
   } catch (Exception e) {
       log.error(e.printStackTrace(e));
       return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity(new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage())).build();
   }      
}
Run Code Online (Sandbox Code Playgroud)

  • 这也许是个好建议,但它并没有真正解决问题,即在处理未捕获的异常时如何改变 dropwizard 的行为。这个问题的隐含前提(您显然不同意)是,我不想将每个端点包围在 try-catch 块中,而 dropwizard 本质上是为我做的。 (2认同)