目录
- 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);
}
}
如果有什么改进之处,感谢大家留言!