Spring Validation 实战
利用 AOP 实现数据校验(不用处理异常)
当访问接口时,切面会先判断是否存在校验错误项
- 若存在错误项则返回错误
- 若不存在错误项则正常访问接口
环境搭建
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
实体类
@Data
public class User {
@NotBlank(message = "用户名不能为空")
@Length(max = 10, message = "用户名最大长度为10")
private String userName;
@Phone(message = "手机号不合法")
private String phoneNumber;
}
接口
@RestController
public class UserController {
@PostMapping("/user")
public String save(@Validated User user, BindingResult result) {
return "数据校验成功";
}
}
AOP
@Aspect
@Component
public class BindingResultAspect {
/**
* 只切入controller中有两个个参数,且第二个参数为BindingResult类型的方法
* 此处根据项目特点选择切入点
*/
@Pointcut("execution(public * com.mystudy.springvalidation.controller..*.*(*,org.springframework.validation.BindingResult))")
public void controllerBindingResult() {
}
@Around("controllerBindingResult() ")
public Object BindingResult(ProceedingJoinPoint joinPoint) throws Throwable {
// 参数值
Object[] args = joinPoint.getArgs();
//取第二个参数即 BindingResult
BindingResult bindingResult = (BindingResult) args[1];
//如果存在校验失败错误
if (bindingResult.hasErrors()) {
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
List<String> list = new ArrayList<>();
fieldErrors.forEach(x -> list.add(x.getDefaultMessage()));
return list.toString();
}
return joinPoint.proceed();
}
}
测试
项目代码
/**
* @description: 切入使用BindingResult的接口
*
* @author weijb
* @since 2020.12.04 15:21
*/
@Aspect
@Component
public class BindingResultAspect {
private final Logger logger = LoggerFactory.getLogger(BindingResultAspect.class);
//只切入controller中有三个参数,且第二个参数为BindingResult类型的方法
@Pointcut("execution(public * com.haier.tms.*.controller..*.*(*,org.springframework.validation.BindingResult,*))")
public void controllerBindingResult() {
}
@Around("controllerBindingResult() ")
public Object BindingResult(ProceedingJoinPoint joinPoint) throws Throwable {
// 参数值
Object[] args = joinPoint.getArgs();
BindingResult bindingResult = (BindingResult) args[1];
if (bindingResult.hasErrors()) {
return jsr303RestFul(bindingResult, joinPoint);
}
return joinPoint.proceed();
}
private Result jsr303RestFul(BindingResult bindingResult, ProceedingJoinPoint joinPoint) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//日志处理Util
StringBuilder sb = CommonLogInfoUtil.commonLog(joinPoint, requestAttributes);
//打印错误字段
sb.append("{[FIELD_ERROR: ");
sb.append(bindingResult.getFieldError().getField());
sb.append("=");
//打印错误字段的值
String value;
if (bindingResult.getFieldError().getRejectedValue() == null){
value = "null";
}else {
value = bindingResult.getFieldError().getRejectedValue().toString();
}
sb.append(value);
//打印自定义错误信息
sb.append(" ------>校验错误: ");
sb.append(bindingResult.getFieldError().getDefaultMessage());
RestFulStatus status;
switch (bindingResult.getFieldError().getCode()) {
case "NotNull":
case "NotBlank":
status = RestFulStatus.JSR303_IS_NULL;
break;
case "Length":
status = RestFulStatus.JSR303_TOO_LONG;
break;
case "Digits":
status = RestFulStatus.JSR303_DIGITS;
break;
case "DecimalMin":
case "DecimalMax":
status = RestFulStatus.JSR303_TOO_BIG;
break;
case "typeMismatch":
status = RestFulStatus.JSR303_TYPE_MISMATCH;
break;
default:
status = RestFulStatus.API_PARAM;
}
//打印国际化信息
sb.append("]; [FIELD_ERROR_RESTFUL: ");
sb.append(status.getKey());
sb.append("]}");
//打印错误注解
sb.append(" ---").append(bindingResult.getFieldError().getCode()).append("---");
logger.error(sb.toString());
//返回国际化信息
return Result.error(status);
//测试阶段返回日志信息,方便前端同事对接
//return Result.error(sb.toString());
}
}