上篇文章中的處理方法確實只能保證進入我們的Controller之後的異常都進行捕獲,並返回統一的格式。
但是在沒進入Controller前的一些異常是無法捕獲的。比如404,400、還有一些500。
關於這些錯誤的處理方法大概有下面這些:
方式一、前端統一處理
其實對於這些錯誤,不建議後端全部攔截,使用SpringBoot框架的應用應該都進行了前後端分離。可以在前端統一進行網路請求的攔截處理,如果返回的http status 不是200,前端對於不同的status進行相應的處理即可。
方式二、對於404的處理
我們可以在Springboot的配置中設定404時丟擲異常。這樣我們就可以在GlobalExceptionInterceptor類中進行捕獲了。
配置:
# 出現404錯誤時, 直接丟擲異常 spring.mvc.throw-exception-if-no-handler-found=true # 不要對資原始檔建立對映 spring.resources.add-mappings=false
然後我們直接在 類中捕獲該型別的異常即可:
package com.xtoad.study.common.web.interceptor;import com.xtoad.study.common.web.ResultDTO;import com.xtoad.study.common.web.exception.BusinessException;import com.xtoad.study.common.web.exception.ResultCodeEnum;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.bind.MethodArgumentNotValidException;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.RestControllerAdvice;/** * 全域性異常捕捉處理 * * @author xtoad * @date 2021/02/18 */@RestControllerAdvicepublic class GlobalExceptionInterceptor { private final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionInterceptor.class); /** * 全域性業務異常捕捉處理 * * @param ex 業務異常 * @return 結果 */ @ExceptionHandler(value = BusinessException.class) public ResultDTO businessExceptionHandler(BusinessException ex) { LOGGER.info(ex.getMsg(), ex); return ResultDTO.failed(ex.getCode(), ex.getMsg()); } /** * 全域性異常捕捉處理 * * @param ex Exception * @return 結果 */ @ExceptionHandler(value = Exception.class) public ResultDTO exceptionHandler(Exception ex) { LOGGER.error(ex.getMessage(), ex); if (ex instanceof MethodArgumentNotValidException) { return ResultDTO.failed(ResultCodeEnum.INVALID_PARAMETER); } else if (ex instanceof org.springframework.web.servlet.NoHandlerFoundException) { // 這裡判斷異常是404時 return ResultDTO.failed("404", "請求的url不存在"); } else { return ResultDTO.failed(ResultCodeEnum.UNKNOW_ERROR); } }}
方式三、重寫ErrorController,任何時候都返回統一格式
上面方式二是能處理404了,但是也不能對映靜態資源了,而且開篇我們說了,除了404,還有他的一些異常,如何捕獲呢?
所以我們重寫ErrorController:
@RestControllerpublic class GlobalExceptionController extends AbstractErrorController { private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionController.class); private static final String ERROR_PATH = "/error"; public GlobalExceptionController(ErrorAttributes errorAttributes) { super(errorAttributes); } @Override public String getErrorPath() { return ERROR_PATH; } @RequestMapping(value = ERROR_PATH) public ResultDTO error(HttpServletRequest request) { WebRequest webRequest = new ServletWebRequest(request); Throwable e = getError(webRequest); if (e == null) { Map<String, Object> attributes = getErrorAttributes(request, false); Object timestamp = attributes.get("timestamp"); Object status = attributes.get("status"); String error = attributes.get("error").toString(); Object path = attributes.get("path"); LOGGER.error("status {} error {} path{} timestamp {}", status, error, path, timestamp); return ResultDTO.failed(Integer.parseInt(status.toString()), error); } if (e instanceof TokenExpiredException) { TokenExpiredException tokenExpiredException = (TokenExpiredException) e; return ResultDTO.failed(tokenExpiredException.getHttpStatus().value(), tokenExpiredException.getHttpStatus().getReasonPhrase()); } else if (e instanceof CodeException) { CodeException codeException = (CodeException) e; String message = e.getMessage(); if (StringUtils.isEmpty(message)) { message = String.format("[%s][%s]", codeException.getErrCode(), codeException.getErrMsg()); } return ResultDTO.failed(codeException.getErrCode(), message); } else { return ResultDTO.failed("500", "系統繁忙,請稍後再試"); } } private Throwable getError(WebRequest webRequest) { return (Throwable) this.getAttribute(webRequest, "javax.servlet.error.exception"); } private Object getAttribute(RequestAttributes requestAttributes, String name) { return requestAttributes.getAttribute(name, 0); }}
該方式,比較簡單粗暴,除非你確定你的應用無論什麼場合都返回統一的JSON,不否不建議使用。
更推薦方式一,前端統一攔截,對http status 進行判斷處理。
最新評論