关于我当前面临的情况,有一些事实
我最近用各种RestControllerAdvice
构建了一个ExceptionHandler
作为Spring RestController的全局异常处理程序。
由于我想返回用于处理ResponseEntityExceptionHandler
中指定的预定义HTTP错误的自定义响应json,因此我的RestControllerAdvice
类继承了ResponseEntityExceptionHandler
和类似方法handleHttpRequestMethodNotSupported()
,handleHttpMessageNotReadable()
被覆盖。
我已经成功覆盖了handleHttpMediaTypeNotSupported()
和handleHttpMessageNotReadable()
,但是当涉及handleHttpRequestMethodNotSupported()
时,我却没有这样做。
这是我的代码的摘录:
@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice(annotations=RestController.class)
public class TestRestExceptionHandler extends ResponseEntityExceptionHandler{
@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request){
BaseResponseJson response = new BaseResponseJson();
response.setRespCode(BaseResponseJson.JSON_RESP_CODE_ERROR);
response.setRespMsg("Request Method Not Supported");
return handleExceptionInternal(ex, response, headers, status, request);
}
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request){
BaseResponseJson response = new BaseResponseJson();
response.setRespCode(BaseResponseJson.JSON_RESP_CODE_ERROR);
response.setRespMsg("Message Not Readable");
return handleExceptionInternal(ex, response, headers, status, request);
}
@Override
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request){
BaseResponseJson response = new BaseResponseJson();
response.setRespCode(BaseResponseJson.JSON_RESP_CODE_ERROR);
response.setRespMsg("Media Type Not Supported");
return handleExceptionInternal(ex, response, headers, status, request);
}
}
handleHttpRequestMethodNotSupported()
的日志如下所示:
[2019-06-05T17:49:50.368+0800][XNIO-74 task-7][WARN ][o.s.w.s.m.s.DefaultHandlerExceptionResolver] Resolved exception caused by Handler execution: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
handleHttpMessageNotReadable()
的日志如下所示:
[2019-06-05T17:50:21.915+0800][XNIO-74 task-8][WARN ][o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver] Resolved exception caused by Handler execution
如您所见,成功代码由ExceptionHandlerExceptionResolver
处理,而故障代码由DefaultHandlerExceptionResolver
处理。
我想知道根本原因是什么,如果有人可以推荐任何可用的解决方案,我将不胜感激。谢谢。
答案 0 :(得分:2)
从 jackycflau 答案中,我们可以总结为2个问题。
问题1。为什么删除
annotations=RestController.class
将适用于HttpRequestMethodNotSupportedException第二季度。为什么只抓不到
HttpRequestMethodNotSupportedException
?
要回答这两个问题,我们需要看一下关于spring如何处理异常的代码。以下源代码基于spring 4.3.5。
在春季DispatcherServlet
处理请求期间,发生错误时,HandlerExceptionResolver
将尝试解决该异常。在给定的情况下,将异常委托给ExceptionHandlerExceptionResolver
。确定解决异常的方法的方法是(getExceptionHandlerMethod
行417中的ExceptionHandlerExceptionResolver.java
)
/**
* Find an {@code @ExceptionHandler} method for the given exception. The default
* implementation searches methods in the class hierarchy of the controller first
* and if not found, it continues searching for additional {@code @ExceptionHandler}
* methods assuming some {@linkplain ControllerAdvice @ControllerAdvice}
* Spring-managed beans were detected.
* @param handlerMethod the method where the exception was raised (may be {@code null})
* @param exception the raised exception
* @return a method to handle the exception, or {@code null}
*/
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = (handlerMethod != null ? handlerMethod.getBeanType() : null);
if (handlerMethod != null) {
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
}
for (Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(entry.getKey().resolveBean(), method);
}
}
}
return null;
}
由于我们使用的是@RestControllerAdvice
,因此我们只需要关注for循环即可确定要使用哪个ControllerAdviceBean
。我们可以看到方法isApplicableToBeanType
将确定ControllerAdviceBean
是否适用,并且相关代码为(ControllerAdviceBean.java
第149行)
/**
* Check whether the given bean type should be assisted by this
* {@code @ControllerAdvice} instance.
* @param beanType the type of the bean to check
* @see org.springframework.web.bind.annotation.ControllerAdvice
* @since 4.0
*/
public boolean isApplicableToBeanType(Class<?> beanType) {
if (!hasSelectors()) {
return true;
}
else if (beanType != null) {
for (String basePackage : this.basePackages) {
if (beanType.getName().startsWith(basePackage)) {
return true;
}
}
for (Class<?> clazz : this.assignableTypes) {
if (ClassUtils.isAssignable(clazz, beanType)) {
return true;
}
}
for (Class<? extends Annotation> annotationClass : this.annotations) {
if (AnnotationUtils.findAnnotation(beanType, annotationClass) != null) {
return true;
}
}
}
return false;
}
private boolean hasSelectors() {
return (!this.basePackages.isEmpty() || !this.assignableTypes.isEmpty() || !this.annotations.isEmpty());
}
通过阅读代码,我们可以解释发生了什么:
删除annotations=RestController.class
时,hasSelectors
将返回true,因此isApplicableToBeanType
也将返回true。因此,在这种情况下,HttpRequestMethodNotSupportedException
将由TestRestExceptionHandler
处理。
对于HttpRequestMethodNotSupportedException
,DispatcherSerlvet
找不到控制器方法来处理请求。因此,传递给handlerMethod
的{{1}}是getExceptionHandlerMethod
,然后传递给null
的{{1}}也为null并返回false。
另一方面,beanType
可以找到isApplicableToBeanType
或DispatcherSerlvet
的控制器方法。因此,其余控制器处理程序方法将传递给HttpMessageNotReadableException
,而HttpMediaTypeNotSupportedException
将返回true。
答案 1 :(得分:1)
我发现了问题的根源,该问题与@RestControllerAdvice
注释有关。
最初,我用@RestControllerAdvice(annotations=RestController.class)
注释了该类。
删除annotations
键值对(即仅用@RestControllerAdvice
注释类)后,HttpRequestMethodNotSupportedException
现在已成功捕获。
这是我只能分享的解决方案。我不了解其根本原因,这种行为对我来说似乎很奇怪……可能是因为HttpRequestMethodNotSupportedException
不受@RestController
的控制吗??? (只是一个疯狂的猜测)。如果有人可以对这种行为提供完整的解释,我将很高兴。