使用spring-web-4.2.6,我有以下Controller和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.class)方法,但类型为NestedServletException,而不是HandleInternalError调用。
删除默认调用时,将使用正确的InternalError异常调用IntenalError调用。
我不想删除默认调用,因为对我来说,拥有一个默认处理程序以便为我的用户提供更好的体验非常重要。
我在这里缺少什么?
编辑:
显然我正在使用spring-web-4.3.3,而没有要求它。我不明白为什么,这是我的Gradle依赖树:http://pastebin.com/h6KXSyp2
答案 0 :(得分:4)
Spring MVC应该只展示您在4.3及更高版本中描述的行为。见this JIRA issue。以前,Spring MVC不会向Throwable
方法公开任何@ExceptionHandler
值。参见
从4.3开始,Spring MVC将捕获从处理程序方法中抛出的任何Throwable
,并将其包装在NestedServletException
中,然后将其暴露给正常的ExceptionHandlerExceptionResolver
进程。
以下简要介绍了它的工作原理:
@Controller
类是否包含任何@ExceptionHandler
方法。 Exception
类型的问题(包括NestedServletException
)。如果可以,它会使用它(如果找到多个匹配,则会进行一些排序)。如果它不能,Exception
有cause
,它会解包并再次尝试为其找到处理程序。 cause
现在可能是Throwable
(或其任何子类型)。 @ControllerAdvice
个类,并尝试在其中找到Exception
类型(包括NestedServletException
)的处理程序。如果可以,它会使用它。如果它不能,并且Exception
有一个cause
,则会将其解包并再次使用Throwable
类型进行尝试。在您的示例中,您的MyController
会引发InternalError
。由于这不是Exception
的子类,因此Spring MVC将其包装在NestedServletException
中。
MyController
没有任何@ExceptionHandler
方法,因此Spring MVC会跳过它。你有一个@ControllerAdvice
带注释的类ExceptionsHandler
,所以Spring MVC会检查它。 @ExceptionHandler
带注释的HandleDefaultException
方法可以处理Exception
,因此Spring MVC会选择它来处理NestedServletException
。
如果删除HandleDefaultException
,则Spring MVC无法找到可以处理Exception
的内容。然后,它将尝试打开NestedServletException
并检查其cause
。然后,它会找到可以处理HandleInternalError
的{{1}}。
这不是一个容易处理的问题。以下是一些选项:
创建一个处理InternalError
的{{1}}并自行检查@ExceptionHandler
。
NestedServletException
除非你想要处理一堆不同的InternalError
或@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
}
}
类型,否则这很好。 (请注意,如果您不能或者不知道如何处理它们,您可以重新抛出它们.Spring MVC将默认为其他一些行为,可能会返回500错误代码。)
或者,您可以利用Spring MVC首先检查Error
(或Throwable
)类@Controller
方法的事实。只需将@RestController
的{{1}}方法移动到控制器中即可。
@ExceptionHandler
现在,Spring将首先尝试在@ExceptionHandler
中找到InternalError
的处理程序。它找不到任何内容,因此它将展开@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) {
...
}
}
并获得NestedServletException
。它会尝试找到MyController
的处理程序并找到NestedServletException
。
这有一个缺点,如果多个控制器&#39;处理程序方法抛出InternalError
,你必须为每个方法添加InternalError
。这也可能是一个优势。你的处理逻辑将更接近抛出错误的东西。