Spring ControllerAdvice-无法在ResponseEntityExceptionHandler中重写handleHttpRequestMethodNotSupported()
以下是我目前面临的一些事实Spring ControllerAdvice-无法在ResponseEntityExceptionHandler中重写handleHttpRequestMethodNotSupported(),spring,api,spring-mvc,microservices,spring-restcontroller,Spring,Api,Spring Mvc,Microservices,Spring Restcontroller,以下是我目前面临的一些事实 我最近用各种ExceptionHandler构建了一个RestControllerAdvice,作为我的Spring RestController的全局异常处理程序 由于我想返回自定义响应json以处理ResponseEntityExceptionHandler中指定的预定义HTTP错误,因此我的RestControllerAdvice类继承ResponseEntityExceptionHandler和handleHttpRequestMethodNotSupport
ExceptionHandler
构建了一个RestControllerAdvice
,作为我的Spring RestController的全局异常处理程序ResponseEntityExceptionHandler
中指定的预定义HTTP错误,因此我的RestControllerAdvice
类继承ResponseEntityExceptionHandler
和handleHttpRequestMethodNotSupported()等方法,handleHttpMessageNodeRable()
被重写
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);
}
}
handlehttpmessagenoteradable()
的日志如下所示:
[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
[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
处理
我想知道潜在的原因是什么,如果有人能推荐任何可用的解决方案,我将不胜感激。谢谢。我已经找到了问题的罪魁祸首,这与
@RestControllerAdvice
注释有关
最初,我用@RestControllerAdvice(annotations=RestController.class)
对该类进行了注释
删除注释
键值对(即仅使用@RestControllerAdvice
注释类)后,HttpRequestMethodNotSupportedException
现在被成功捕获
这是我只能分享的解决方案。我不明白背后的原因,这种行为对我来说很奇怪。。。可能是因为
HttpRequestMethodNotSupportedException
不受@RestController
的控制???(只是胡乱猜测)。如果有人能对这种行为做出充分的解释,我会很高兴的 从jackycflau的回答中,我们可以总结为两个问题
Q1。为什么删除annotations=RestController.class
将适用于HttpRequestMethodNotSupportedException
Q2。为什么只有HttpRequestMethodNotSupportedException
未被捕获?
为了回答这两个问题,我们需要看一看关于spring如何处理异常的代码。以下源代码基于spring 4.3.5
在springDispatcherServlet
处理请求期间,当出现错误时,HandlerExceptionResolver
将尝试解决异常。在给定的情况下,异常被委托给ExceptionHandlerExceptionResolver
。确定解决异常的方法是(ExceptionHandlerExceptionResolver.java
第417行中的getExceptionHandlerMethod
)
通过阅读代码,我们可以解释发生了什么:
第一季度的答案
当删除annotations=RestController.class
时,hasselector
将返回false,因此isapplicatedtobeantype
将返回true。因此,在这种情况下,HttpRequestMethodNotSupportedException
将由TestRestExceptionHandler
处理
第二季度的答案
对于HttpRequestMethodNotSupportedException
,DispatcherSerlvet
无法找到控制器方法来处理请求。因此,传递给getExceptionHandlerMethod
的handlerMethod
为null
,然后传递给isAppliedtoBeanType
的beanType
也为null,并返回false
另一方面,
DispatcherSerlvet
可以找到httpmessagenetradableexception
或HttpMediaTypeNotSupportedException的控制器方法。因此,rest控制器处理程序方法将被传递到getExceptionHandlerMethod
,并且IsAppliedtoBeanType
将返回true。为了澄清,请求成功返回了HTTP405,但我无法获得BaseResponseJson,我正试图覆盖它以获取帮助!回答得好。您是否介意进一步解释如何确定handlerMethod
何时为null?如果在查找handlerMethod
之前/期间引发异常,它将作为null传递,否则它将不为null。您可以在RequestMappingInfoHandlerMapping
第192行中查找HttpRequestMethodNotSupportedException
的情况。
/**
* 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;
}
/**
* 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());
}