Springboot2.x處理404、500等異常
阿新 • • 發佈:2021-12-05
404錯誤
404錯誤是不經過Controller的,所以使用@ControllerAdvice或@RestControllerAdvice無法獲取到404錯誤
springboot2處理404錯誤的兩種方式
第一種:直接配置
#出現錯誤時, 直接丟擲異常
spring.mvc.throw-exception-if-no-handler-found=true
這種方式不太適用實際開發,比如和swagger整合時,訪問/swagger-ui.html會出現404異常
第二種:繼承ErrorController來處理錯誤
import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Controller public class MyErrorController implements ErrorController { @RequestMapping("/error") public String handleError(HttpServletRequest request){ //獲取statusCode:401,404,500 Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); if(statusCode == 500){ return "/error/500"; }else if(statusCode == 404){ //對應的是/error/404.html、/error/404.jsp等,檔案位於/templates下面 return "/error/404"; }else if(statusCode == 403){ return "/403"; }else{ return "/500"; } } @Override public String getErrorPath() { return "/error"; } }
import com.bettn.common.util.Result; import com.bettn.common.util.WebUtils; import org.apache.shiro.ShiroException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.NoHandlerFoundException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 全域性異常捕獲處理 */ @RestControllerAdvice public class ExceptionControllerAdvice { private static final Logger logger= LoggerFactory.getLogger(ExceptionControllerAdvice.class); // 捕捉shiro的異常 @ExceptionHandler(ShiroException.class) public Result handle401() { return new Result(401,"您沒有許可權訪問!",null); } // 捕捉其他所有異常 @ExceptionHandler(Exception.class) public Object globalException(HttpServletRequest request, HandlerMethod handlerMethod, Throwable ex) { if(WebUtils.isAjax(handlerMethod)){ return new Result(getStatus(request).value(), "訪問出錯,無法訪問: " + ex.getMessage(), null); }else { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("/error/500"); //這裡需要在templates資料夾下新建一個/error/500.html檔案用作錯誤頁面 modelAndView.addObject("errorMsg",ex.getMessage()); return modelAndView; } } /** * 判斷是否是Ajax請求 * * @param request * @return */ public boolean isAjax(HttpServletRequest request) { return (request.getHeader("X-Requested-With") != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With").toString())); } // @ExceptionHandler(Exception.class) // public Result globalException(HttpServletRequest request, Throwable ex) { // return new Result(getStatus(request).value(),"訪問出錯,無法訪問: " + ex.getMessage(),null); // } /** * 獲取響應狀態碼 * @param request * @return */ private HttpStatus getStatus(HttpServletRequest request) { Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); if (statusCode == null) { return HttpStatus.INTERNAL_SERVER_ERROR; } return HttpStatus.valueOf(statusCode); } /** * 捕捉404異常,這個方法只在配置 * spring.mvc.throw-exception-if-no-handler-found=true來後起作用 * */ @ResponseStatus(HttpStatus.NOT_FOUND) @ExceptionHandler(NoHandlerFoundException.class) public Result handle(HttpServletRequest request,NoHandlerFoundException e) { System.out.println(12); return new Result(404,"沒有【"+request.getMethod()+"】"+request.getRequestURI()+"方法可以訪問",null); } }
這個異常類與ExceptionControllerAdvice連用,ExceptionControllerAdvice類除了不能處理404異常以外,其他異常都可以處理,其中
globalException異常這個方法會捕獲500錯誤,導致MyErrorController無法捕獲到500錯誤,從而跳轉到500頁面,也就是說MyErrorController在這個專案中
只能捕獲404異常
500異常捕獲
500異常分為ajax和直接跳轉500頁面
具體的異常捕獲,程式碼如何下:
// 捕捉其他所有異常 @ExceptionHandler(Exception.class) public Object globalException(HttpServletRequest request, HandlerMethod handlerMethod, Throwable ex) { if(WebUtils.isAjax(handlerMethod)){ return new Result(getStatus(request).value(), "訪問出錯,無法訪問: " + ex.getMessage(), null); }else { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("/error/500"); //這裡需要在templates資料夾下新建一個/error/500.html檔案用作錯誤頁面 modelAndView.addObject("errorMsg",ex.getMessage()); return modelAndView; } }
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
public class WebUtils extends org.springframework.web.util.WebUtils {
/**
* 判斷是否ajax請求
* spring ajax 返回含有 ResponseBody 或者 RestController註解
* @param handlerMethod HandlerMethod
* @return 是否ajax請求
*/
public static boolean isAjax(HandlerMethod handlerMethod) {
ResponseBody responseBody = handlerMethod.getMethodAnnotation(ResponseBody.class);
if (null != responseBody) {
return true;
}
// 獲取類上面的Annotation,可能包含組合註解,故採用spring的工具類
Class<?> beanType = handlerMethod.getBeanType();
responseBody = AnnotationUtils.getAnnotation(beanType, ResponseBody.class);
if (null != responseBody) {
return true;
}
return false;
}
public static String getCookieValue(HttpServletRequest request, String cookieName) {
Cookie cookie=getCookie(request, cookieName);
return cookie==null?null:cookie.getValue();
}
public static void removeCookie(HttpServletResponse response, String cookieName) {
setCookie(response, cookieName, null, 0);
}
public static void setCookie(HttpServletResponse response, String cookieName, String cookieValue,
int defaultMaxAge) {
Cookie cookie=new Cookie(cookieName,cookieValue);
cookie.setHttpOnly(true);
cookie.setPath("/");
cookie.setMaxAge(defaultMaxAge);
response.addCookie(cookie);
}
}
TIP
404、500頁面需要放在/templates下,且確認配置了檢視,如jsp、thymeleaf等,否則也會出現找不到頁面,例如整合thymeleaf
依賴
<!-- thymeleaf模板引擎和shiro框架的整合,這個是與shiro整合的,一般不整合shiro就不需要這個依賴 -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>${thymeleaf.extras.shiro.version}</version>
</dependency>
<!-- SpringBoot整合thymeleaf模板 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
配置
spring:
# 模板引擎
thymeleaf:
mode: HTML5
encoding: utf-8
# 禁用快取
cache: false
404、500頁面地址(目錄結構)
src/main/resources/templates/error/404.html
src/main/resources/templates/error/500.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>500</title>
</head>
<body>
<!--
直接使用${el}無法解析出el的值
${errorMsg}
-->
<h3>糟糕! 伺服器出錯啦~~(>_<)~~</h3>
<div>
異常資訊如下:<br/>
<p th:text="${errorMsg}"></p>
</div>
</body>
</html>