• Spring之webMvc异常处理


    异常处理可以前端处理,也可以后端处理。

    从稳妥的角度出发,两边都应该进行处理。

    本文专门阐述如何在服务端进行http请求异常处理。

    一、常见的异常类型

    当我们做http请求的时候,会有各种各样的可能错误,比较常见的例如:

    1.服务类异常

    2.接口异常,而接口异常有各种各样的情况

    究极就是接口的异常。

    异常可以发生在请求的各个步骤之中,这里主要阐述网络通讯正常的情况。

    这些通讯异常在方法 org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.doResolveException(HttpServletRequest, HttpServletResponse, Object, Exception)中

    有详细的描述:

    if (ex instanceof HttpRequestMethodNotSupportedException) {
        return handleHttpRequestMethodNotSupported(
                (HttpRequestMethodNotSupportedException) ex, request, response, handler);
    }
    else if (ex instanceof HttpMediaTypeNotSupportedException) {
        return handleHttpMediaTypeNotSupported(
                (HttpMediaTypeNotSupportedException) ex, request, response, handler);
    }
    else if (ex instanceof HttpMediaTypeNotAcceptableException) {
        return handleHttpMediaTypeNotAcceptable(
                (HttpMediaTypeNotAcceptableException) ex, request, response, handler);
    }
    else if (ex instanceof MissingPathVariableException) {
        return handleMissingPathVariable(
                (MissingPathVariableException) ex, request, response, handler);
    }
    else if (ex instanceof MissingServletRequestParameterException) {
        return handleMissingServletRequestParameter(
                (MissingServletRequestParameterException) ex, request, response, handler);
    }
    else if (ex instanceof ServletRequestBindingException) {
        return handleServletRequestBindingException(
                (ServletRequestBindingException) ex, request, response, handler);
    }
    else if (ex instanceof ConversionNotSupportedException) {
        return handleConversionNotSupported(
                (ConversionNotSupportedException) ex, request, response, handler);
    }
    else if (ex instanceof TypeMismatchException) {
        return handleTypeMismatch(
                (TypeMismatchException) ex, request, response, handler);
    }
    else if (ex instanceof HttpMessageNotReadableException) {
        return handleHttpMessageNotReadable(
                (HttpMessageNotReadableException) ex, request, response, handler);
    }
    else if (ex instanceof HttpMessageNotWritableException) {
        return handleHttpMessageNotWritable(
                (HttpMessageNotWritableException) ex, request, response, handler);
    }
    else if (ex instanceof MethodArgumentNotValidException) {
        return handleMethodArgumentNotValidException(
                (MethodArgumentNotValidException) ex, request, response, handler);
    }
    else if (ex instanceof MissingServletRequestPartException) {
        return handleMissingServletRequestPartException(
                (MissingServletRequestPartException) ex, request, response, handler);
    }
    else if (ex instanceof BindException) {
        return handleBindException((BindException) ex, request, response, handler);
    }
    else if (ex instanceof NoHandlerFoundException) {
        return handleNoHandlerFoundException(
                (NoHandlerFoundException) ex, request, response, handler);
    }
    else if (ex instanceof AsyncRequestTimeoutException) {
        return handleAsyncRequestTimeoutException(
                (AsyncRequestTimeoutException) ex, request, response, handler);
    }

    我们整理为表格:

    编码 名称 备注
     handleHttpRequestMethodNotSupported  请求方法不支持  例如RequestMapping只指定了post,但用get请求
     HttpMediaTypeNotSupportedException  媒体类型不支持  例如消息转换器可能只处理JSON,但请求头确设置为xml之类的
     HttpMediaTypeNotAcceptableException  媒体类型不可接受  请求处理器无法生成客户端可以支持的响应内容
     MissingPathVariableException  缺失路径变量

     请求url缺乏路径变量。例如

     @RequestMapping("/show/{name}")

     public Object show(@PathVariable String name){}

     工程师可能少写了{name}

     MissingServletRequestParameterException  缺失Servlet请求参数  例如使用了@RequstParam,但是url并没有提供对应的参数
     ServletRequestBindingException  Servlet请求绑定异常  在执行绑定的时候发生的异常
     ConversionNotSupportedException  不支持的转换异常  无法为bean找到匹配的编辑器或者转换器
     TypeMismatchException  类型不匹配异常  给bean复制的时候,发现类型不匹配
     HttpMessageNotReadableException  http消息无法读取  当HttpMessageConverter#read方法发生错误的时候
     HttpMessageNotWritableException  http消息无法写异常  当HttpMessageConverter#write方法发生错误的时候
     MethodArgumentNotValidException  方法参数不可用异常  验证 @Valid过的参数时候所发生的异常
     MissingServletRequestPartException  缺失Servlet请求part异常  请求的媒体类型是文件类型(或者是附件),但内容没有
     BindException  绑定异常  
     NoHandlerFoundException  没有找到处理器

     By default when the DispatcherServlet can't find a handler for a request it sends a 404 response. However if its property "throwExceptionIfNoHandlerFound" is set to true this exception is raised and may be handled with a configured HandlerExceptionResolver.

    默认情况下,分发器处理器程序无法找到请求的处理器的时候(例如静态资源或者请求控制器方法的时候),会发送一个404响应。此外会设置属性throwExceptionIfNoHandlerFound=true,并引发没有处理器异常。用户可以考虑配置一个HandlerExceptionResolver来处理

     AsyncRequestTimeoutException    异步请求超时异常。即503 错误。

     spring和springboot中和异常/错误有关的类远不止上表那么多。

    出于代码的严谨性要求,几乎所有的函数/过程都会有异常处理(try catch throw),限于篇幅,不会讨论所有的代码,也没有必要。

    二、WMS中的异常配置

      关于wms的介绍,可以参考: wms配置简介

      wms可以处理异常的地方有几个,但我们专门考虑exception字眼的部分。

    @Bean
    public HandlerExceptionResolver handlerExceptionResolver(
            @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
        List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
        configureHandlerExceptionResolvers(exceptionResolvers);
        if (exceptionResolvers.isEmpty()) {
            addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
        }
        extendHandlerExceptionResolvers(exceptionResolvers);
        HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
        composite.setOrder(0);
        composite.setExceptionResolvers(exceptionResolvers);
        return composite;
    }
    
    /**
     * Override this method to configure the list of
     * {@link HandlerExceptionResolver HandlerExceptionResolvers} to use.
     * <p>Adding resolvers to the list turns off the default resolvers that would otherwise
     * be registered by default. Also see {@link #addDefaultHandlerExceptionResolvers}
     * that can be used to add the default exception resolvers.
     * @param exceptionResolvers a list to add exception resolvers to (initially an empty list)
     */
    protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
    }
    
    
    
    /**
     * A method available to subclasses for adding default
     * {@link HandlerExceptionResolver HandlerExceptionResolvers}.
     * <p>Adds the following exception resolvers:
     * <ul>
     * <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through
     * {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.
     * <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with
     * {@link org.springframework.web.bind.annotation.ResponseStatus}.
     * <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring exception types
     * </ul>
     */
    protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,
            ContentNegotiationManager mvcContentNegotiationManager) {
    
        ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
        exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);
        exceptionHandlerResolver.setMessageConverters(getMessageConverters());
        exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
        exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
        if (jackson2Present) {
            exceptionHandlerResolver.setResponseBodyAdvice(
                    Collections.singletonList(new JsonViewResponseBodyAdvice()));
        }
        if (this.applicationContext != null) {
            exceptionHandlerResolver.setApplicationContext(this.applicationContext);
        }
        exceptionHandlerResolver.afterPropertiesSet();
        exceptionResolvers.add(exceptionHandlerResolver);
    
        ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
        responseStatusResolver.setMessageSource(this.applicationContext);
        exceptionResolvers.add(responseStatusResolver);
    
        exceptionResolvers.add(new DefaultHandlerExceptionResolver());
    }    

       通过wms机制,可以覆盖bean(handlerExceptionResolver),或者覆盖方法 configureHandlerExceptionResolvers添加异常处理解析器。

       不过一般情况下,我们都没有那个必要。

       一般情况下,使用DefaultHandlerExceptionResolver进行处理即可,即前文介绍的内容。

    三、springboot的http请求异常

        如果使用springboot开发,那么springboot会通过自动配置引入一个 org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController

        这类控制器类实现了接口 org.springframework.boot.web.servlet.error.ErrorController。

        根据springboot的有关描述,我们可以实现它接口org.springframework.boot.web.servlet.error.ErrorController,以便覆盖默认的实现。

        例如:

    @Controller
    public class ErrController implements ErrorController {
    
        @RequestMapping("/error")
        public String handleError(HttpServletRequest request,HttpServletResponse response) {
            int statusCode=response.getStatus();
            if (statusCode == 404) {
                return "/main/err.html";
            } 
            else  if (statusCode == 444) {
                return "/main/login.html";
            }
            return "";
        }
    }

      需要注意的是,ErrorController 的代码是在wms的异常处理之后执行的。

      此外,也可以只用控制器advice注解处理, 例如:SpringBoot系列——自定义统一异常处理 - huanzi-qch - 博客园 (cnblogs.com)

     

    四、小结

      spring为了少让我们的代码出现问题,还引入了一个机制:验证。例如可以验证参数验证。不过验证仅仅只是解决了少数的异常情形,虽然参数不合法的发生的数量还是比较多的。

    但这个验证一般情况下,也不需要,因为一般在客户端就先处理了!

  • 相关阅读:
    强化学习——Q-learning算法
    嵌入(embedding)层的理解
    福昕PDF电子文档处理套装软件中文企业版9.01
    奇异值分解(SVD)原理与在降维中的应用
    C++ Primer 笔记——迭代器
    Windows Internals 笔记——线程局部存储区
    C/C++中二进制与文本方式打开文件的区别
    C++ Primer 笔记——固有的不可移植的特性
    C++ Primer 笔记——union
    C++ Primer 笔记——嵌套类 局部类
  • 原文地址:https://www.cnblogs.com/lzfhope/p/16249559.html
Copyright © 2020-2023  润新知