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

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

 

posted @ 2022-05-16 20:47  正在战斗中  阅读(404)  评论(0编辑  收藏  举报