作者:fredalxin\
地址:https://fredal.xin/400-error-...

很多人都会在平时开发过程中遇到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.1,000+ 道 Java面试题及答案整顿(2021最新版)

2.终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!

3.阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式公布,全新颠覆性版本!

5.《Java开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞+转发哦!