正确使用Spring Boot的ErrorController和Spring的ResponseEntityExceptionHandler

and*_*ero 4 java error-handling spring exception spring-boot

问题

在Spring Boot中创建控制器以自定义方式处理所有错误/异常(包括自定义异常)时,应首选哪种技术?

  1. 控制器是否应该实现Spring Boot的ErrorController

  2. 控制器是否应该扩展Spring的ResponseEntityExceptionHandler

  3. 两者:实现和扩展两个类(包括它们的功能)的单个控制器?

  4. 两者:两个单独的控制器,一个执行ErrorController,另一个扩展ResponseEntityExceptionHandler

目标

这篇文章的原因是在Spring Boot中找到一种具有以下所有属性的异常处理方法:

  • Throwable在处理请求期间,所有出现在控制器/过滤器/拦截器中的信号都应被捕获。
  • 在受凉的情况下Throwable,我们希望暴露任何堆栈跟踪或其他实施细则,以客户永远(除非明确编码的方式)。
  • 应该有可能Throwable按其类分别处理所有发生的。对于任何其他未指定的类型,可以指定默认响应。(我肯定知道,这可以实现的@ExceptionHandler。但是ErrorController?)
  • 该代码应尽可能简洁明了,没有丑陋的解决方法或UB来实现此目标。

更多细节

我注意到,两个控制器(请参见上面的1和2)都可能包含返回ResponseEntity对象的方法,从而处理发生的异常并将响应返回给客户端。因此,它们在理论上可以产生相同的结果?

那里有关于如何使用技术1和2的一些教程。但是,我发现没有文章同时考虑这两种选择,将它们进行比较或一起使用,这引发了一些其他问题:

  1. 他们甚至应该一起考虑吗?

  2. 这两种提议的技术之间的主要区别是什么?有何相似之处?

  3. 一个是另一个的更强大的版本吗?有什么可以做的而另一个不能做的,反之亦然?

  4. 它们可以一起使用吗?在某些情况下有必要这样做吗?

  5. 如果将它们一起使用,将如何处理异常?它是通过两个处理程序还是通过一个处理程序?在后者的情况下,哪个?

  6. 如果将它们一起使用,并且在异常处理期间在控制器内部(一个或另一个)抛出异常,该异常将如何处理?是否发送到另一个控制器?从理论上讲,异常可以在控制器之间开始反弹还是创建其他类型的不可恢复循环吗?

  7. 关于Spring Boot在ResponseEntityExceptionHandler内部如何使用Spring 或如何期望它在Spring Boot应用程序中使用,是否有任何可信/官方文档?

  8. 如果ResponseEntityExceptionHandler一个人已经足够了,那为什么ErrorController存在呢?

ResponseEntityExceptionHandler@ExceptionHandler注解一起查看Spring时,它似乎在分别处理不同类型的异常和使用更简洁的代码方面更强大。但是由于Spring Boot是基于Spring构建的,因此这意味着:

  • 当使用Spring Boot时,我们应该实现ErrorController而不是扩展ResponseEntityExceptionHandler
  • 是否可以ErrorController做所有事情ResponseEntityExceptionHandler,包括分别处理不同类型的异常?

编辑:相关:Spring @ControllerAdvice与ErrorController

da-*_*ha1 9

Spring Boot应用程序具有用于错误处理的默认配置-ErrorMvcAutoConfiguration

如果没有提供其他配置,它的基本作用是:

  • 它创建默认的全局错误控制器-BasicErrorController
  • 它会创建默认的“错误”静态视图“ Whitelabel错误页面”。

BasicErrorController默认情况下连接到“ /错误”。如果应用程序中没有自定义的“错误”视图,则在任何控制器引发异常的情况下,用户将进入/ error whitelabel页面,该页面由BasicErrorController填充信息。

如果应用程序具有实现的控制器,ErrorController它将替换 BasicErrorController

如果在错误处理控制器中发生任何异常,它将通过Spring异常过滤器(请参阅下面的更多详细信息),最后,如果没有发现任何异常,则将由基础应用程序容器(例如Tomcat)处理该异常。基础容器将处理异常,并根据其实现显示一些错误页面/消息。

BasicErrorController javadoc中有一条有趣的信息:

基本的全局错误控制器,呈现ErrorAttributes。可以使用Spring MVC抽象(例如@ExceptionHandler)或添加servlet服务器错误页面来处理更具体的错误。

BasicErrorController或者ErrorController实现是一个全局错误处理程序。它可以与@ExceptionHandler结合使用。

在这里,我们来到ResponseEntityExceptionHandler

@ControllerAdvice类的便捷基类,这些类希望通过@ExceptionHandler方法在所有@RequestMapping方法中提供集中式异常处理。这个基类提供了一个@ExceptionHandler方法,用于处理内部Spring MVC异常。

换句话说,这意味着这ResponseEntityExceptionHandler只是一个便利类,其中已经包含Spring MVC异常处理。我们可以将其用作自定义类的基类,以处理控制器的异常。为了使我们的自定义类起作用,必须使用对其进行注释@ControllerAdvice

带有注释的类@ControllerAdvice可以与全局错误处理程序(BasicErrorControllerErrorController实现)同时使用。如果我们带@ControllerAdvice注释的类(不能扩展ResponseEntityExceptionHandler)不能处理某些异常,则该异常进入全局错误处理程序。

到目前为止,我们已经看过ErrorHandler控制器以及任何带有注释的东西@ControllerAdvice。但这要复杂得多。我在问题- 设置多个@ControllerAdvice @ExceptionHandlers的优先级中发现了一个非常有价值的见解。

编辑:

为简单起见:

  1. First Spring在@ControllerAdvice类中搜索异常处理程序(以@ExceptionHandler注释的方法)。请参见ExceptionHandlerExceptionResolver
  2. Then it checks if the thrown exception is annotated with @ResponseStatus or derives from ResponseStatusException. See ResponseStatusExceptionResolver.
  3. Then it goes through Spring MVC exceptions' default handlers. See DefaultHandlerExceptionResolver.
  4. And at the end if nothing is found, the control is forwarded to the error page view with the global error handler behind it. This step is not executed if the exception comes from the error handler itself.
  5. If no error view is found (e.g. global error handler is disabled) or step 4 is skipped, then the exception is handled by the container.

  • 好的,我想我现在明白了。`@ExceptionHandler` 只捕获控制器抛出的异常。但在一种情况下,我的 `javax.servlet.Filter` 实现在预处理期间抛出异常。在这种情况下,它会立即映射到 `/error` 并发送到我的 `ErrorController` 实现。但是因为 `ErrorController` 是一个控制器,如果它进一步抛出异常,它可以由 `@ExceptionHandler` 处理。 (4认同)