Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Spring ControllerAdvice-无法在ResponseEntityExceptionHandler中重写handleHttpRequestMethodNotSupported()_Spring_Api_Spring Mvc_Microservices_Spring Restcontroller - Fatal编程技术网

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的全局异常处理程序

  • 由于我想返回自定义响应json以处理
    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

    在spring
    DispatcherServlet
    处理请求期间,当出现错误时,
    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());
    }