Spring 的MethodArgumentTypeMismatchException 和 MissingServletRequestParameterException 異常處理


Spring 有幾個異常是在通過 url path 訪問 Controller 方法時,由於參數不匹配而拋出的,比如 MethodArgumentTypeMismatchExceptionMissingServletRequestParameterException 異常。由於拋出該異常的時候還沒有進入 path 指定的方法,因此方法內的 try...catch 是無法捕獲的。如果要對它們進行捕獲,有兩種解決方案。

通過 ExceptionHandler 進行捕獲

ExceptionHandler 可以指定對某個類型的異常進行定制化的處理:

@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public void handleTypeMismatch(MethodArgumentTypeMismatchException ex) {
    String name = ex.getName();
    String type = ex.getRequiredType().getSimpleName();
    Object value = ex.getValue();
    String message = String.format("'%s' should be a valid '%s' and '%s' isn't",name, type, value);
    // Do the graceful handling
}

ExceptionHandler 可以放在某個指定的 Controller 中,也可以放在一個 @ControllerAdvice 注解的類中對所有的 Controller 生效。

通過 HandlerExceptionResolver 進行捕獲

有些時候不方便通過 ExceptionHandler 進行捕獲,比如由於團隊框架的原因繼承了某個 BaseController,而 BaseController 中又被定義了 Throwable 級的處理(沒錯,很蠢的設計)。此時就需要更深一層的定制,即 HandlerExceptionResolver

這里要先介紹下 Spring 處理異常的邏輯。Spring 在捕獲到未處理的異常時,會通過一個 HandlerExceptionResolver 的列表,依次調用其中的每個元素的 resolveException 方法,如果返回 null,則會繼續下一個元素的進行調用,不為 null 即終止。其實上面的 ExceptionHandler 也是被 Spring 自己的一個通用 HandlerExceptionResolver 所使用。

因此自定義的 HandlerExceptionResolver 要加到 resolvers 的列表開頭,優先於通用的 HandlerExceptionResolver 進行調用:

package com.xxx;
  
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RequestParameterExceptionHandler implements HandlerExceptionResolver {
    private static final Logger LOGGER = LoggerFactory.getLogger(RequestParameterExceptionHandler.class);

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        if (ex instanceof MissingServletRequestParameterException) {
            MissingServletRequestParameterException e = (MissingServletRequestParameterException) ex;
            String body = String.format("缺少 %s 類型的參數 %s", e.getParameterType(), e.getParameterName());

            try {
                response.getWriter().write(body);
            } catch (IOException ioException) {
                LOGGER.error("{} 發生異常。", LOG_PREFIX, ioException);
            }

            return new ModelAndView();
        } else if (ex instanceof MethodArgumentTypeMismatchException) {
            MethodArgumentTypeMismatchException e = (MethodArgumentTypeMismatchException) ex;
            String body = String.format("%s 應該是 %s 類型", e.getName(), e.getRequiredType().getSimpleName()));

            try {
                response.getWriter().write(body);
            } catch (IOException ioException) {
                LOGGER.error("{} 發生異常。", LOG_PREFIX, ioException);
            }
            return new ModelAndView();
        }
        return null;
    }
}

接下來要注冊到 Mvc:

package com.xxx;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        resolvers.add(0, new RequestParameterExceptionHandler());
    }
}

這樣就可以了。

參考:


免責聲明!

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



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