本文介绍了@ErrorHandler for Error仅在没有异常映射的情况下被调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用spring-web-4.2.6,我有以下控制器和ExceptionHandler:

Using spring-web-4.2.6, I have the following Controller and ExceptionHandler:

@ControllerAdvice
public class ExceptionsHandler {
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorDTO> HandleDefaultException(Exception ex) {
    ...
    }

    @ExceptionHandler(InternalError.class)
    public ResponseEntity<ErrorDTO> HandleInternalError(InternalError ex) {
    ...
    }
}

@RestController
@RequestMapping("/myController")
public class MyController {
    @RequestMapping(value = "/myAction", method = RequestMethod.POST)
    public boolean myAction() {
        throw new InternalError("");
    }
}

由于某种原因,ExceptionsHandler的HandleDefaultException(用于Exception。类)被调用,但类型为NestedServletException,而不是HandleInternalError调用。

For some reason, the ExceptionsHandler's HandleDefaultException (for Exception.class) method is invoked, with an exception of type NestedServletException, instead of the HandleInternalError call.

删除默认调用时,将使用适当的InternalError异常调用IntenalError调用

When removing the default call, the IntenalError call is called with the proper InternalError exception.

我不想删除默认调用,因为对我来说,拥有默认处理程序以为用户带来更好的体验非常重要。

I do not want to remove the default call as it is important to me to have a default handler to allow for a better experience for my users.

我在这里想念什么?

编辑:

显然我正在使用spring-web-4.3.3,没有要求。我不明白为什么会这样,这是我的Gradle依赖关系树:

Apparently I'm using spring-web-4.3.3, without asking for it. I don't understand why exactly, here's my Gradle dependencies tree: http://pastebin.com/h6KXSyp2

推荐答案

Spring MVC仅应显示您在4.3及更高版本中描述的行为。请参见。以前,Spring MVC不会将任何 Throwable 值公开给 @ExceptionHandler 方法。参见

Spring MVC should only exhibit the behavior you describe with version 4.3 and above. See this JIRA issue. Previously, Spring MVC would not expose any Throwable values to @ExceptionHandler methods. See



  • ExceptionHandler doesn't work with Throwable

从4.3版本开始MVC将捕获从处理程序方法中抛出的所有 Throwable 并将其包装在 NestedServletException 中,然后将其公开给正常的 ExceptionHandlerExceptionResolver 过程。

Since 4.3, Spring MVC will catch any Throwable thrown from your handler methods and wrap it in a NestedServletException, which it will then expose to the normal ExceptionHandlerExceptionResolver process.

以下是其工作原理的简短描述:

Here's a short description of how it works:


  1. 检查处理程序方法的 @Controller 类是否包含任何 @ExceptionHandler 方法。

  2. 如果这样做,则尝试解析可以处理 Exception 类型(包括 NestedServletException )。如果可以,它会使用它(如果找到多个匹配项,则会进行某种排序)。如果不能,并且 Exception 具有,它会解开包装并再次尝试为其找到处理程序。 原因现在可能是 Throwable (或其任何子类型)。

  3. 如果不是这样。它获取所有 @ControllerAdvice 类,并尝试查找 Exception 类型的处理程序(包括 NestedServletException )。如果可以的话,它将使用它。如果不能,并且 Exception 有一个原因,则将其解开并尝试使用该 Throwable 类型。

  1. Checks if the handler method's @Controller class contains any @ExceptionHandler methods.
  2. If it does, tries to resolve one that can handle the Exception type (including NestedServletException). If it can, it uses that (there's some sorting if multiple matches are found). If it can't, and the Exception has a cause, it unwraps and tries again to find a handler for that. That cause might now be a Throwable (or any of its subtypes).
  3. If it doesn't. It gets all the @ControllerAdvice classes and tries to find a handler for the Exception type (including NestedServletException) in those. If it can, it uses that. If it can't, and the Exception has a cause, it unwraps it and tries again with that Throwable type.

在您的示例中,您的 MyController 引发 InternalError 。由于这不是 Exception 的子类,因此Spring MVC将其包装在 NestedServletException 中。

In your example, your MyController throws an InternalError. Since this is not a subclass of Exception, Spring MVC wraps it in an NestedServletException.

MyController 没有任何 @ExceptionHandler 方法,因此Spring MVC会跳过它。您有一个 @ControllerAdvice 带注释的类 ExceptionsHandler ,因此Spring MVC会对此进行检查。 @ExceptionHandler 带注释的 HandleDefaultException 方法可以处理 Exception ,因此Spring MVC选择它来处理 NestedServletException

MyController doesn't have any @ExceptionHandler methods, so Spring MVC skips it. You have a @ControllerAdvice annotated class, ExceptionsHandler, so Spring MVC checks that. The @ExceptionHandler annotated HandleDefaultException method can handle Exception, so Spring MVC chooses it to handle the NestedServletException.

如果删除了 HandleDefaultException ,Spring MVC找不到可以处理 Exception 的东西。然后它将尝试解包 NestedServletException 并检查其原因。然后,它将找到 HandleInternalError 可以处理 InternalError

If you remove that HandleDefaultException, Spring MVC won't find something that can handle Exception. It will then attempt to unwrap the NestedServletException and check for its cause. It'll then find the HandleInternalError which can handle that InternalError.

这不是一个容易解决的问题。以下是一些选项:

This is not an easy issue to deal with. Here are some options:

创建一个 @ExceptionHandler 来处理 NestedServletException 并自己检查 InternalError

Create an @ExceptionHandler that handles NestedServletException and do the check for InternalError yourself.

@ExceptionHandler(NestedServletException.class)
public ResponseEntity<String> HandleNested(NestedServletException ex) {
    Throwable cause = ex.getCause();
    if (cause instanceof InternalError) {
        // deal with it
    } else if (cause instanceof OtherError) {
        // deal in some other way
    }
}

这很好,除非存在许多不同的 Error Throwable 类型。 (请注意,如果您不能或不知道如何处理它们,可以将它们扔掉。SpringMVC将默认使用其他行为,可能会返回500错误代码。)

This is fine unless there's a bunch of different Error or Throwable types you want to handle. (Note that you can rethrow these if you can't or don't know how to handle them. Spring MVC will default to some other behavior, likely returning a 500 error code.)

或者,您可以利用Spring MVC首先检查 @Controller (或 @RestController )首先使用 @ExceptionHandler 方法的类。只需将 InternalError @ExceptionHandler 方法移到控制器中即可。

Alternatively, you can take advantage of the fact that Spring MVC first checks the @Controller (or @RestController) class for @ExceptionHandler methods first. Just move the @ExceptionHandler method for InternalError into the controller.

@RestController
@RequestMapping("/myController")
public class MyController {
    @RequestMapping(value = "/myAction", method = RequestMethod.POST)
    public boolean myAction() {
        throw new InternalError("");
    }

    @ExceptionHandler(value = InternalError.class)
    public ResponseEntity<String> HandleInternalError(InternalError ex) {
         ...
    }
}



MyController
中为 NestedServletException 查找处理程序。它将找不到任何内容,因此它将解包 NestedServletException 并获得 InternalError 。它将尝试为 InternalError 找到一个处理程序,并找到 HandleInternalError

Now Spring will first attempt to find a handler for NestedServletException in MyController. It won't find any so it will unwrap NestedServletException and get an InternalError. It will try to find a handler for InternalError and find HandleInternalError.

其缺点是,如果多个控制器的处理程序方法抛出 InternalError ,则必须添加 @ExceptionHandler 每个。这也可能是一个优势。您的处理逻辑将更接近引发错误的事情。

This has the disadvantage that if multiple controllers' handler methods throw InternalError, you have to add an @ExceptionHandler to each. This might also be an advantage. Your handling logic will be closer to the thing that throws the error.

这篇关于@ErrorHandler for Error仅在没有异常映射的情况下被调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 20:50