Spring Boot + thymeleaf 捕捉校驗參數異常並統一處理
捕捉校驗失敗異常信息類
import com.fyyc.jhyzm.blog.mayday.exception.BlogException;
import com.fyyc.jhyzm.blog.mayday.reuse.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.stream.Collectors;
/**
* @ ClassName: ControllerExceptionHandler
* @ Author: longxin
* @ CreatTime: 2020/4/13 0013 17:35
* @ version: 1.0
*/
//加了改註解會攔截所有拋出的異常
@ControllerAdvice
public class ControllerExceptionHandler {
//異常接收的前端頁面
private final static String error_path = "error/error";
//日誌
private final Logger logger = LoggerFactory.getLogger(this.getClass());
//BlogException 自定義通用異常處理
@ExceptionHandler(BlogException.class)
public ModelAndView exceptionHander(HttpServletRequest request, BlogException e) throws Exception {
logger.error("路由 : {},Blog錯誤信息 : {}", request.getRequestURL(), e.getStatus() + " " + e.getMessage());
//Data - 我定義的通用數據模型
Data<String> data = new Data(e.getMessage(), e.getCode(), false, request.getRequestURL());
ModelAndView mv = new ModelAndView();
mv.addObject("data", data);
mv.setViewName(error_path);
return mv;
}
//處理請求參數格式錯誤 @RequestBody上validate失敗後拋出的異常是MethodArgumentNotValidException異常。
@ExceptionHandler(MethodArgumentNotValidException.class)
public ModelAndView exceptionHander(HttpServletRequest request, MethodArgumentNotValidException e) throws Exception {
String message = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining());
logger.error("路由 : {},RequestBody-Valid錯誤信息 : {}", request.getRequestURL(), message);
//Data - 我定義的通用數據模型
Data<String> data = new Data(message, false, request.getRequestURL());
ModelAndView mv = new ModelAndView();
mv.addObject("data", data);
mv.setViewName(error_path);
return mv;
}
//處理請求參數格式錯誤 @RequestParam上validate失敗後拋出的異常是javax.validation.ConstraintViolationException
@ExceptionHandler(ConstraintViolationException.class)
public ModelAndView exceptionHander(HttpServletRequest request, ConstraintViolationException e) throws Exception {
String message = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining());
logger.error("路由 : {},RequestParam-Valid錯誤信息 : {}", request.getRequestURL(), message);
//Data - 我定義的通用數據模型
Data<String> data = new Data(message, false, request.getRequestURL());
ModelAndView mv = new ModelAndView();
mv.addObject("data", data);
mv.setViewName(error_path);
return mv;
}
//處理Get請求中 使用@Valid 驗證路徑中請求實體校驗失敗後拋出的異常,
@ExceptionHandler(BindException.class)
public ModelAndView exceptionHander(HttpServletRequest request, BindException e) throws Exception {
String message = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining());
logger.error("路由 : {},Get-Valid錯誤信息 : {}", request.getRequestURL(), message);
//Data - 我定義的通用數據模型
Data<String> data = new Data(message, false, request.getRequestURL());
ModelAndView mv = new ModelAndView();
mv.addObject("data", data);
mv.setViewName(error_path);
return mv;
}
//其它異常
@ExceptionHandler(Exception.class)
public ModelAndView exceptionHander(HttpServletRequest request, Exception e) throws Exception {
logger.error("路由 : {},錯誤信息 : {}", request.getRequestURL(), e.getMessage());
//Data - 我定義的通用數據模型
Data<String> data = new Data(e.getMessage(), false, request.getRequestURL());
ModelAndView mv = new ModelAndView();
mv.addObject("data", data);
mv.setViewName(error_path);
return mv;
}
}
示例說明
- 如下當我使用
@Valid
註解來使@Validated ,@Valid ,@NotBlank,@NotNull
等註解生效時,如果拋出異常將會被上面代碼中的MethodArgumentNotValidException
所攔截並處理
- 其他的不一一列舉
Data - 我定義的通用數據模型 (高複用返回數據類)
import org.apache.commons.lang.StringUtils;
/**
* @ ClassName: Reuse
* @ Author: longxin
* @ CreatTime: 2020/4/14 0014 15:41
* @ version: 1.0
*/
@lombok.Data
public class Data<T> {
private Integer code;
private String msg;
private Boolean success;
private T data;
public Data() {
}
public Data(T data) {
this.data = data;
reuseCheck();
}
public Data(String msg, T data) {
this.data = data;
this.msg = msg;
reuseCheck();
}
public Data(String msg, Integer code, T data) {
this.data = data;
this.code = code;
this.msg = msg;
reuseCheck();
}
public Data(String msg, Boolean success, T data) {
this.data = data;
this.msg = msg;
this.success = success;
reuseCheck();
}
public Data(String msg, Integer code, Boolean success, T data) {
this.data = data;
this.code = code;
this.msg = msg;
this.success = success;
reuseCheck();
}
private void reuseCheck(){
msg = StringUtils.isBlank(msg) ? "操作成功." : msg;
success = success == null ? true : success;
code = code == null ? success ? 200 : 0 : code;
}
}
BlogException 我定義的通用異常處理
import org.springframework.http.HttpStatus;
public class BlogException extends AbstractException {
//狀態
private HttpStatus httpStatus;
public BlogException(String message) {
super(message);
}
public BlogException(String message, HttpStatus httpStatus) {
super(message);
this.httpStatus = httpStatus;
}
public BlogException(String message, Throwable cause) {
super(message, cause);
}
@Override
public HttpStatus getStatus() {
return this.httpStatus == null ? HttpStatus.INTERNAL_SERVER_ERROR : this.httpStatus;
}
public Integer getCode(){
return this.httpStatus.value();
}
}
抽象異常類(BlogException 繼承它)
import org.springframework.http.HttpStatus;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
public abstract class AbstractException extends RuntimeException {
private Object errorData;
public AbstractException(String message) {
super(message);
}
public AbstractException(String message, Throwable cause) {
super(message, cause);
}
public abstract HttpStatus getStatus();
@Nullable
public Object getErrorData() {
return errorData;
}
@NonNull
public AbstractException setErrorData(@Nullable Object errorData) {
this.errorData = errorData;
return this;
}
}
前端錯誤頁接收代碼
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<body>
<span th:text="${data}">整體</span>
<span th:text="${data.code}">狀態碼</span>
<span th:text="${data.msg}">信息</span>
<span th:text="${data.data}">數據</span>
</body>
</html>
使用場景
代碼中拋出
try {
//你的代碼處
}catch (Exception e){
throw new BlogException("異常信息", "狀態碼");
}
註解中生效
@NotBlank(message = "用戶名或郵箱不能爲空")
@Size(max = 255, message = "用戶名或郵箱的字符長度不能超過 {max}")
private String username;
其他系統異常:服務器異常,請求參數異常等等,很多地方都可以用到
使用實例截圖
某Service登陸接口示例
登錄時未查詢到用戶信息或密碼不正確時拋出異常
測試效果
Blog錯誤:用戶名或密碼錯誤
- 後臺
- Swagger測試
參數空異常
-
後臺
-
Swagger測試
-
其他異常不一一列舉