遇到 400、500 错误千万不要慌!
发布日期:2021-06-30 13:02:41 浏览次数:2 分类:技术文章

本文共 6563 字,大约阅读时间需要 21 分钟。

作者:fredalxin

地址:

很多人都会在平时开发过程中遇到400或500异常,并且也没有走到服务端controller中,就变得有些不知所措。

我们知道SpringMVC从DispatchServlet开始接收与分发请求,从入口开始debug,还能找不到问题所在么?

从DispatchServlet的doDispatch()方法开始处理请求:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {    //删除一些代码    try {        ModelAndView mv = null;        Exception dispatchException = null;        try {            // 删除一些代码            try {                // Actually invoke the handler.                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());            }            finally {                if (asyncManager.isConcurrentHandlingStarted()) {                    return;                }            }            applyDefaultViewName(request, mv);            mappedHandler.applyPostHandle(processedRequest, response, mv);        }        catch (Exception ex) {            dispatchException = ex;  // 这里捕获了异常TypeMismatchException        }        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);    }    catch (Exception ex) {    }    finally {        // 删除一些代码    }}

其实在这儿我们就能看到exception的具体异常栈,有兴趣的可以继续看springMVC的处理方法processDispatchResult。

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {        boolean errorView = false;        if (exception != null) {            if (exception instanceof ModelAndViewDefiningException) {                logger.debug("ModelAndViewDefiningException encountered", exception);                mv = ((ModelAndViewDefiningException) exception).getModelAndView();            }            else {                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);                mv = processHandlerException(request, response, handler, exception);// 执行这个方法                errorView = (mv != null);            }        }        // 方便阅读,删除了其他代码}

这个方法中对异常进行判断,发现不是“ModelAndViewDefiningException”就交给processHandlerException方法继续处理。

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {    // Check registered HandlerExceptionResolvers...    ModelAndView exMv = null;    for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {        exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);        if (exMv != null) {            break;        }    }    // 去掉了一些代码    throw ex;}

这里看到for循环来找一个handlerExceptionResolver来处理这个异常。handler列表有spring自带的ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver以及自定义的exceptionResolver。

这些都继承自AbstractHandlerExceptionResolver类,这个类是一个抽象类,它实现了HandlerExceptionResolver接口,它对HandlerExceptionResolver接口约定的方法的所实现代码如下:

public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {    if (shouldApplyTo(request, handler)) {        logException(ex, request);        prepareResponse(ex, response);        return doResolveException(request, response, handler, ex);    }    else {        return null;    }}

首先判断当前异常处理器是否可以处理当前的目标handler。例如通过for循环依次发现轮到DefaultHandlerExceptionResolver才能处理,那么最终会执行该handlerExceptionResolver的doResolveException方法。

protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {    try {        if (ex instanceof NoSuchRequestHandlingMethodException) {            return handleNoSuchRequestHandlingMethod(...);        }        // 删除部分else if   instanceof 判断        else if (ex instanceof TypeMismatchException) {            // 执行到了这里            return handleTypeMismatch((TypeMismatchException) ex, request, response, handler);        }        // 删除部分else if   instanceof 判断        else if (ex instanceof BindException) {            return handleBindException((BindException) ex, request, response, handler);        }    }    catch (Exception handlerException) {    }    return null;}

通过对异常类型的判断,来执行相应handleXXXException方法。而handleXXXException方法中,有很多是会抛出400错误的!

举个几个栗子:

protected ModelAndView handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {    response.sendError(400, ex.getMessage());    return new ModelAndView();}protected ModelAndView handleServletRequestBindingException(ServletRequestBindingException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {    response.sendError(400, ex.getMessage());    return new ModelAndView();}protected ModelAndView handleTypeMismatch(TypeMismatchException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {    response.sendError(400);    return new ModelAndView();}protected ModelAndView handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {    response.sendError(400);    return new ModelAndView();}protected ModelAndView handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {    response.sendError(400);    return new ModelAndView();}protected ModelAndView handleMissingServletRequestPartException(MissingServletRequestPartException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {    response.sendError(400, ex.getMessage());    return new ModelAndView();}protected ModelAndView handleBindException(BindException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {    response.sendError(400);    return new ModelAndView();}

那么抛出400错误的时候该怎么解决呢?

从服务端角度出发,可以定义完善的全局异常处理器exceptionHandler,把易抛出400的错误例如TypeMismatchException、BindException都给处理掉,返回能看得懂的信息。

从客户端请求过程中来看,可以自定义handlerExceptionResolver,只需实现HandlerExceptionResolver接口即可,例如:

public class ApiHandlerExceptionResolver implements HandlerExceptionResolver { @Override    public ModelAndView resolveException(HttpServletRequest request,            HttpServletResponse response, Object handler, Exception exception) {        ModelAndView model = new ModelAndView();       // do something ...      return model;    } }

所以遇到400错误的时候不要慌,毕竟400它是个标准的错误码,好好debug或者查阅一下相关资料便能迎刃而解。

近期热文推荐:

1.

2.

3.

4.

5.

觉得不错,别忘了随手点赞+转发哦!

转载地址:https://javastack.blog.csdn.net/article/details/118025997 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:LockSupport 线程工具类有啥用?
下一篇:从 Bitmap 到布隆过滤器,再到高并发缓存设计策略!

发表评论

最新留言

感谢大佬
[***.8.128.20]2024年04月25日 14时06分02秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

5-vagrant突然启动报错,The specified host network collides with a non-hostonly network! This will cause 2019-05-01
6-人人vue 中启动报错<% if (process.env.NODE_ENV === ‘production‘) { %> <% }else { %> <% } %> 2019-05-01
1-谷粒商城-Mac电脑启动项目报错(Windows启动不报错),Caused by: java.net.SocketException: Permission denied 2019-05-01
2-报错:source 1.5 中不支持 diamond 运算符[ERROR] (请使用 -source 7 或更高版本以启用 diamond运算符) 2019-05-01
牛客高级项目(二)-MySQL报错:Expression #1 of SELECT list is not in GROUP BY clause and contains 2019-05-01
8-MySQL启动报错解决方案ERROR! The server quit without updating PID file (Mysql怎么启动、重启、kill进程、看版本号的命令) 2019-05-01
mac Archive: /Users/apple/Library/Caches/Homebrew/portable-ruby-2.3.3.leopard_64.bottle.1.tar.gz To 2019-05-01
7-MySQL报错:jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nest 2019-05-01
5-solr连接Mysql警告Establishing SSL connection without server‘s identity verification is not recommende 2019-05-01
1、Idea单元测试时,程序报错:java.lang.NoSuchMethodError: org.junit.platform.commons.util. 2019-05-01
2、idea 启动项目JDK、JRE报错:Class JavaLaunchHelper ...One of the two will be used. Which one is undefined. 2019-05-01
3、mysql连接报错、mysql插入中文乱码,java.sql.SQLException: No timezone mapping entry for ‘GMT+81 2019-05-01
修改SpringBoot内置Tomcat的端口 2019-05-01
java中的锁 2019-05-01
I/O总结 2019-05-01
Layout Element与Horizontal Layout GrounpHor----------输入聊天框与标签的动态占比 2019-05-01
c++面经汇总(操作系统 互斥锁机制以及互斥锁和读写锁的区别、进程状态转换(动态就绪、静态就绪、动态阻塞、静态阻塞)软链接、硬链接、大端小端、用户态和内核态、死锁产生的必要条件、内存溢出和内存泄漏线 2019-05-01
c++面经整理(操作系统 协程、协程与线程、系统调用、fork实例、微内核与宏内核、僵尸进程、IO模型、异步编程的事件循环、page cache) 2019-05-01
mmorpg小地图系统制作 2019-05-01
Xlua之Bundle构建工具创建 2019-05-01