问题描述
使用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:
- 检查处理程序方法的
@Controller
类是否包含任何@ExceptionHandler
方法。 - 如果这样做,则尝试解析可以处理
Exception
类型(包括NestedServletException
)。如果可以,它会使用它(如果找到多个匹配项,则会进行某种排序)。如果不能,并且Exception
具有,它会解开包装并再次尝试为其找到处理程序。原因
现在可能是Throwable
(或其任何子类型)。 - 如果不是这样。它获取所有
@ControllerAdvice
类,并尝试查找Exception
类型的处理程序(包括NestedServletException
)。如果可以的话,它将使用它。如果不能,并且Exception
有一个原因
,则将其解开并尝试使用该Throwable
类型。
- Checks if the handler method's
@Controller
class contains any@ExceptionHandler
methods. - If it does, tries to resolve one that can handle the
Exception
type (includingNestedServletException
). If it can, it uses that (there's some sorting if multiple matches are found). If it can't, and theException
has acause
, it unwraps and tries again to find a handler for that. Thatcause
might now be aThrowable
(or any of its subtypes). - If it doesn't. It gets all the
@ControllerAdvice
classes and tries to find a handler for theException
type (includingNestedServletException
) in those. If it can, it uses that. If it can't, and theException
has acause
, it unwraps it and tries again with thatThrowable
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仅在没有异常映射的情况下被调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!