Spring Boot REST 服务异常处理

新手上路,请多包涵

我正在尝试建立一个大型 REST 服务服务器。我们正在使用 Spring Boot 1.2.1 Spring 4.1.5 和 Java 8。我们的控制器正在实现 @RestController 和标准的 @RequestMapping 注释。

我的问题是 Spring Boot 为控制器异常设置了一个默认重定向到 /error 。从文档:

Spring Boot 默认提供一个 /error 映射,以合理的方式处理所有错误,并在 servlet 容器中注册为“全局”错误页面。

多年来使用 Node.js 编写 REST 应用程序,这对我来说是不明智的。服务端点生成的任何异常都应在响应中返回。我不明白为什么你会发送重定向到最有可能是 Angular 或 JQuery SPA 消费者的地方,他们只是在寻找答案,不能或不会对重定向采取任何行动。

我想要做的是设置一个全局错误处理程序,它可以接受任何异常——有目的地从请求映射方法中抛出或由 Spring 自动生成(如果没有找到请求路径签名的处理程序方法,则为 404),并返回一个标准格式的错误响应(400、500、503、404)到客户端,没有任何 MVC 重定向。具体来说,我们将获取错误,使用 UUID 将其记录到 NoSQL,然后使用 JSON 正文中日志条目的 UUID 向客户端返回正确的 HTTP 错误代码。

文档对如何执行此操作含糊不清。在我看来,您必须创建自己的 ErrorController 实现或以某种方式使用 ControllerAdvice ,但我看到的所有示例仍然包括将响应转发到某种错误映射,这无济于事。其他示例建议您必须列出要处理的每个异常类型,而不是仅列出“Throwable”并获取所有内容。

任何人都可以告诉我我错过了什么,或者在不建议 Node.js 更容易处理的情况下为我指明正确的方向吗?

原文由 ogradyjd 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.1k
2 个回答

新答案 (2016-04-20)

使用 Spring Boot 1.3.1.RELEASE

新的第 1 步 - 将以下属性添加到 application.properties 中既简单又不那么麻烦:

 spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

比修改现有的 DispatcherServlet 实例(如下所示)容易得多! - 乔’

如果使用完整的 RESTful 应用程序,禁用静态资源的自动映射非常重要,因为如果您使用 Spring Boot 的默认配置来处理静态资源,那么资源处理程序将处理请求(它最后排序并映射到 / ** 这意味着它会拾取应用程序中任何其他处理程序尚未处理的任何请求),因此调度程序 servlet 没有机会抛出异常。


新答案 (2015-12-04)

使用 Spring Boot 1.2.7.RELEASE

新步骤 1 - 我发现了一种设置“throExceptionIfNoHandlerFound”标志的侵入性更小的方法。在您的应用程序初始化类中将下面(步骤 1)的 DispatcherServlet 替换代码替换为:

 @ComponentScan()
@EnableAutoConfiguration
public class MyApplication extends SpringBootServletInitializer {
    private static Logger LOG = LoggerFactory.getLogger(MyApplication.class);
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
        DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    }

在这种情况下,我们在现有的 DispatcherServlet 上设置标志,它保留 Spring Boot 框架的任何自动配置。

我发现的另一件事 - @EnableWebMvc 注释对 Spring Boot 来说是致命的。是的,该注释启用了诸如能够捕获所有控制器异常之类的功能,如下所述,但它也扼杀了 Spring Boot 通常提供的很多有用的自动配置。使用 Spring Boot 时要格外小心地使用该注释。


原答案:

经过大量研究并跟进此处发布的解决方案(感谢您的帮助!)以及对 Spring 代码进行大量运行时跟踪后,我终于找到了一个可以处理所有异常(不是错误,但请继续阅读)的配置包括 404。

第 1 步 - 告诉 SpringBoot 在“找不到处理程序”的情况下停止使用 MVC。我们希望 Spring 抛出一个异常,而不是向客户端返回一个重定向到“/error”的视图。为此,您需要在其中一个配置类中有一个条目:

 // NEW CODE ABOVE REPLACES THIS! (2015-12-04)
@Configuration
public class MyAppConfig {
    @Bean  // Magic entry
    public DispatcherServlet dispatcherServlet() {
        DispatcherServlet ds = new DispatcherServlet();
        ds.setThrowExceptionIfNoHandlerFound(true);
        return ds;
    }
}

这样做的缺点是它取代了默认的调度程序 servlet。这对我们来说还不是问题,没有出现副作用或执行问题。如果您出于其他原因要对调度程序 servlet 执行任何其他操作,那么这里就是执行它们的地方。

第 2 步 - 现在 spring boot 将在找不到处理程序时抛出异常,该异常可以在统一的异常处理程序中与任何其他异常一起处理:

 @EnableWebMvc
@ControllerAdvice
public class ServiceExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    ResponseEntity<Object> handleControllerException(HttpServletRequest req, Throwable ex) {
        ErrorResponse errorResponse = new ErrorResponse(ex);
        if(ex instanceof ServiceException) {
            errorResponse.setDetails(((ServiceException)ex).getDetails());
        }
        if(ex instanceof ServiceHttpException) {
            return new ResponseEntity<Object>(errorResponse,((ServiceHttpException)ex).getStatus());
        } else {
            return new ResponseEntity<Object>(errorResponse,HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Map<String,String> responseBody = new HashMap<>();
        responseBody.put("path",request.getContextPath());
        responseBody.put("message","The URL you have reached is not in service at this time (404).");
        return new ResponseEntity<Object>(responseBody,HttpStatus.NOT_FOUND);
    }
    ...
}

请记住,我认为“@EnableWebMvc”注释在这里很重要。没有它,这一切似乎都行不通。就是这样——您的 Spring boot 应用程序现在将捕获上述处理程序类中的所有异常,包括 404,您可以随意处理它们。

最后一点——似乎没有办法让它捕获抛出的错误。我有一个古怪的想法,即使用方面来捕获错误并将它们转化为上述代码随后可以处理的异常,但我还没有时间实际尝试实现它。希望这对某人有帮助。

任何意见/更正/改进将不胜感激。

原文由 ogradyjd 发布,翻译遵循 CC BY-SA 4.0 许可协议

在 Spring Boot 1.4+ 中,添加了新的酷类以简化异常处理,这有助于删除样板代码。

为异常处理提供了一个新的 @RestControllerAdvice ,它是 @ControllerAdvice@ResponseBody 的组合。使用此新注释时,您可以删除 @ResponseBody @ExceptionHandler

IE

 @RestControllerAdvice
public class GlobalControllerExceptionHandler {

    @ExceptionHandler(value = { Exception.class })
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiErrorResponse unknownException(Exception ex, WebRequest req) {
        return new ApiErrorResponse(...);
    }
}

为了处理 404 错误,添加 @EnableWebMvc 注释和以下内容到 application.properties 就足够了:

spring.mvc.throw-exception-if-no-handler-found=true

您可以在此处找到并使用这些资源:

https://github.com/magiccrafter/spring-boot-exception-handling

原文由 magiccrafter 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题