1. 程式人生 > 其它 >Spring之webMvc異常處理

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的介紹,可以參考: https://www.cnblogs.com/lzfhope/p/16103174.html

  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為了少讓我們的程式碼出現問題,還引入了一個機制:驗證。例如可以驗證引數驗證。不過驗證僅僅只是解決了少數的異常情形,雖然引數不合法的發生的數量還是比較多的。

但這個驗證一般情況下,也不需要,因為一般在客戶端就先處理了!