天天看點

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

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或者查閱一下相關資料便能迎刃而解。