遇到 400、500 錯誤千萬不要慌!


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

很多人都會在平時開發過程中遇到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開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM