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

时间:2016-12-16 21:55:19

标签: java logging exception-handling dropwizard

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

@GET
public Response foo() { throw new NullPointerException(); }

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

显然,最好是自己捕捉和处理异常,但有时他们会不知所措。每次在整个资源周围编写一个try catch块都很好,但是(a)它很麻烦,(b)我总是喜欢自动解决方案,而且你必须记住"的解决方案。

所以我想要的是做以下事情:

  1. 记录堆栈跟踪(我使用slf4j,但我认为它可以用于任何事情)
  2. 返回通用错误响应,该响应不会公开有关我的服务器的潜在特权信息!
  3. 我觉得必须有一种内置的方法来实现这一点 - 它已经以相对不错的方式处理异常 - 但搜索文档并没有发现任何东西。对此有一个很好的解决方案吗?

5 个答案:

答案 0 :(得分:1)

正如评论中提到的那样,答案是ExceptionMapper。你需要这样一个类:

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

您可以在toResponse方法中执行您喜欢的任何日志记录等操作,返回值是实际发送给请求者的内容。通过这种方式,您可以完全控制,并且应该设置合理的默认值 - 请记住这是因为错过了,而不是您实际希望看到的错误!这也是根据您获得的异常类型设置不同行为的好时机。

要实际执行此操作,只需在主dropwizard应用程序的run方法中插入以下行(或类似内容):

environment.jersey().register(new RuntimeExceptionMapper());

其中environment是应用程序的Environment方法的run参数。现在当你在某个地方有一个未被捕获的RuntimeException时,这将触发,而不是之前的任何投掷蜥蜴。

注意:这仍然不是不小心捕捉和处理你的例外的借口!

答案 1 :(得分:1)

将以下内容添加到yaml文件中。请注意,它将删除dropwizard添加的所有默认异常映射器。

<head> <link href="https://ajax.googleapis.com/ajax/libs/dojo/1.10.4/dijit/themes/claro/claro.css" rel="stylesheet" /> <script src="https://ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js" data-dojo-config="async: false"></script> </head> <body class="claro"> <button id="btn">Click me</button> <input type="checkbox" />Can you still select me? </body>

编写自定义异常映射器,如下所示:

server:
    registerDefaultExceptionMappers: false

然后在泽西注册异常映射器: public class CustomExceptionMapper implements ExceptionMapper<RuntimeException> { @Override public Response toResponse(RuntimeException runtime) { // ... } }

答案 2 :(得分: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 } 。无论如何,如果您不想向最终用户显示堆栈跟踪,您可能会捕获一般异常,记录详细信息并相应地自定义响应和实体。

您可以定义

@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();
   }      
}

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

PageTabBarController

答案 3 :(得分:0)

本文详细介绍了具有自定义 ExceptionMapper 的Jersey的已检查和未检查的异常实现:

https://www.codepedia.org/ama/error-handling-in-rest-api-with-jersey/

Dropwizard官方文档还介绍了一种更简单的方法,只需使用 WebApplicationException 进行捕获:

@GET
@Path("/{collection}")
public Saying reduceCols(@PathParam("collection") String collection) {
    if (!collectionMap.containsKey(collection)) {
        final String msg = String.format("Collection %s does not exist", collection);
        throw new WebApplicationException(msg, Status.NOT_FOUND)
    }

    // ...
}

https://www.dropwizard.io/en/stable/manual/core.html#responses

答案 4 :(得分:0)

它对我有用,只需注册在主类的 run 方法中创建的自定义异常映射器。

environment.jersey().register(new CustomExceptionMapper());

CustomExceptionMapper 可以像这样实现 ExceptionMapper 类的地方

public class CustomExceptionMapperimplements ExceptionMapper<Exception>