目錄
- 1. Validation 全局異常處理代碼
- 2. ValidatorUtils 工具類
-
- 2.1 使用方法
- 2.2 自定義異常和全局異常處理
1. Validation 全局異常處理代碼
package com.android.handler;
import com.android.common.domain.ResponseVo;
import com.android.common.exception.NotLoginException;
import com.android.common.exception.ParamNotIllegalException;
import com.android.common.sysenum.HttpResponseEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolationException;
import static com.android.constant.StringContants.SEPARATE;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* MethodArgumentNotValidException異常:
* 當 @Validated 修飾 java 對象時,如 (@RequestBody @Validated LoginDto loginDto)
* 此時當 javabean 中的屬性驗證失敗時,會抛出所有不符合屬性的msg
*
* @param ex
* @return
*/
@ExceptionHandler({MethodArgumentNotValidException.class})
public ResponseVo handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
BindingResult bindingResult = ex.getBindingResult();
// 日志将所有錯誤資訊完全列印出來
StringBuilder errorLog = new StringBuilder("校驗失敗——");
// 定義傳回給前端的内容,隻傳回給前端最先報錯的資訊
String msg = bindingResult.getFieldError().getDefaultMessage();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
errorLog.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
}
log.debug(SEPARATE + errorLog + SEPARATE);
return ResponseVo.errorResult(400, msg);
}
/**
* ConstraintViolationException異常:
* 當 @Validated 修飾單個參數時(此時 @Validated 在類上而不是在方法中),
* 會抛出ConstraintViolationException異常。
*
* @param ex
* @return
*/
@ExceptionHandler({ConstraintViolationException.class})
public ResponseVo handleConstraintViolationException(ConstraintViolationException ex) {
String message = ex.getMessage();
/*
處理格式。
ex.getMessage() 格式為:方法名.參數名: message
如:sendEmail.email: 郵箱格式錯誤,表示是 sendEmail 方法的 email 參數格式校驗錯誤。
*/
String msg = message.substring(message.indexOf(":") + 1).trim();
return ResponseVo.errorResult(400, msg);
}
/**
* 處理異常
* @param e
* @return
*/
@ExceptionHandler(value = BindException.class)
public ResponseVo validationExceptionHandler(BindException e) {
BindingResult bindingResult = e.getBindingResult();
List<String> errorList = new ArrayList<>();
String msg = bindingResult.getFieldError().getDefaultMessage();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
errorList.add(fieldError.getDefaultMessage());
}
log.debug(String.join(",", errorList));
return ResponseVo.errorResult(400, msg);
}
}
2. ValidatorUtils 工具類
該工具類用于解決 @Validated / @Valid 注解 全局異常處理較為麻煩的問題:
package com.android.util;
import com.android.common.exception.ParamNotIllegalException;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;
public class ValidatorUtils {
private static Validator validator;
static {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
/**
* 校驗對象
*
* @param object 待校驗對象
* @param groups 待校驗的組
* @throws ParamNotIllegalException 校驗不通過,則報ApiException異常
*/
public static void validateEntity(Object object, Class<?>... groups) {
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (!constraintViolations.isEmpty()) {
constraintViolations.forEach(e -> {
// 自定義異常,抛出後可以直接在全局異常進行中接收
throw new ParamNotIllegalException(e.getMessage());
});
}
}
}
2.1 使用方法
在控制器中使用 ValidateEntity方法取代 @Validated 注解,因為 @Validated 注解抛出的異常有多種情況,處理起來較為麻煩,而 ValidateEntity 可以抛出自定義異常,處理起來較為容易。
/**
* 郵箱登入
* @param emailLoginDto
* @return
*/
@ApiOperation("郵箱登入")
@PostMapping("/emailLogin")
public ResponseVo emailLogin(@RequestBody EmailLoginDto emailLoginDto) {
log.debug(SEPARATE + "emailLogin方法執行" + SEPARATE);
// 參數中不寫 @Validated,在此處進行驗證,抛出自定義異常,在全局異常進行中接收
ValidatorUtils.validateEntity(emailLoginDto);
return userService.emailLogin(emailLoginDto);
}
EmailLoginDto類:
package com.android.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
public class EmailLoginDto implements Serializable {
/**
* 序列化版本号
*/
private static final long serialVersionUID = 1253859525433214940L;
/**
* 使用者郵箱,抛出自定義異常中的 message 就是 @NotBlank 注解中的 message
*/
@NotBlank(message = "郵箱不能為空")
@Pattern(regexp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$", message = "郵箱格式錯誤")
@ApiModelProperty(value = "使用者郵箱", required = true)
private String email;
/**
* 郵箱驗證碼
*/
@NotBlank(message = "驗證碼不能為空")
@ApiModelProperty(value = "郵箱驗證碼", required = true)
private String code;
}
2.2 自定義異常和全局異常處理
自定義異常:
package com.android.common.exception;
public class ParamNotIllegalException extends RuntimeException{
public ParamNotIllegalException(String message) {
super(message);
}
}
全局異常處理:
package com.android.handler;
import com.android.common.domain.ResponseVo;
import com.android.common.exception.NotLoginException;
import com.android.common.exception.ParamNotIllegalException;
import com.android.common.sysenum.HttpResponseEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import static com.android.constant.StringContants.SEPARATE;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 處理ParamNotIllegalException參數不合法異常
* 該處理器是專門用于處理validation的400異常的
* @param e 捕獲的異常
* @return responseVo錯誤封裝錯誤資訊
*/
@ExceptionHandler(ParamNotIllegalException.class)
public ResponseVo paramNotIllegalExceptionHandler(Exception e) {
log.debug(SEPARATE + "paramNotIllegalException異常處理器執行" + SEPARATE);
return ResponseVo.errorResult(400, e.getMessage());
}
@ExceptionHandler(NotLoginException.class)
public ResponseVo notLoginException(Exception e) {
log.debug(SEPARATE + "notLoginException異常處理器執行" + SEPARATE);
return ResponseVo.errorResult(HttpResponseEnum.NEED_LOGIN);
}
/**
* 處理所有異常
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
public ResponseVo exception(Exception e) {
e.printStackTrace();
return ResponseVo.errorResult(HttpResponseEnum.SYSTEM_ERROR);
}
}
如果有什麼改進之處,感謝大家留言!