在日常业务开发过程中会涉及到很多业务参数的校验,其中空值判断又是其中最多的内容。但在每个业务方法中都进行校验,即便将业务要素相近的校验进行合并,仍然有些繁琐。于是想利用@Valid进行简单校验,减少简单重复代码。
Valid注解的使用有几个不同的方法,其中一个是直接修饰入参对象,这种方法可以参考@Valid注解是什么
这次是直接修饰具体方法的使用示例
相关包依赖
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
其中jakarta.validation-api是相关校验类型定义包;hibernate-validator是具体实现包,可以单独使用,搭配使用时建议不要降低其版本,否则会出现某些校验类型不可使用的问题
具体代码
Valid注解捕获切面
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.ValidationException;
import javax.validation.Validator;
import java.util.Set;
import java.util.stream.Collectors;
@Component
@Aspect
public class ValidAspectInterceptor {
@Autowired
private Validator validator;
@Before(value = "@annotation(valid)")
public void validateArgs(JoinPoint joinPoint, Valid valid) {
Object[] objects = joinPoint.getArgs();
Set<ConstraintViolation<Object>> set = validator.validate(objects[0]);
if (set.size() > 0) {
String errorInfo = set.stream().map(x -> x.getMessage())
.collect(Collectors.toSet()).stream()
.collect(Collectors.joining(","));
throw new ValidationException(errorInfo);
}
}
}
测试实体类
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class TestEntity {
@NotBlank(message = "value1值为空")
private String value1;
private String value2;
private String value3;
}
测试请求类
import com.alibaba.fastjson.JSON;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
@RequestMapping("/Valid")
public class TestController {
@RequestMapping("/test")
@Valid
public String newEntity(@RequestBody TestEntity testEntity) {
System.out.println(JSON.toJSONString(testEntity));
return "success";
}
}
此时向测试接口post json :{“value2”: “2”},后台报错:
此时添加异常捕获方法,则可以实现统一的错误信息返回,示例代码如下:
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ValidationException;
import java.util.List;
@ControllerAdvice
public class BadRequestExceptionHandler {
/**
* 校验错误拦截处理
*
* @param exception 错误信息集合
* @return 错误信息
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public String validationBodyException(Exception exception) {
return exception.getMessage();
}
}
此时响应如下:
自定义验证类型
除了使用包里面提供的验证类型外,还可以自定义验证类型,满足相对复杂的校验场景。
自定义类型主要包括两个类
1.注解定义
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {NotZeroValidator.class})
public @interface NotZero {
String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2.验证类型实现
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Objects;
public class NotZeroValidator implements ConstraintValidator<NotZero, Object> {
@Override
public void initialize(NotZero constraintAnnotation) {
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
if (Objects.isNull(o)) {
return false;
}
if (o instanceof Double) {
return (Double) o != 0D;
} else if (o instanceof Long) {
return (Long) o != 0L;
} else if (o instanceof Float) {
return (Float) o != 0F;
} else if (o instanceof Integer) {
return (Integer) o != 0;
}
return false;
}
}
此时实体类修改如下:
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class TestEntity {
@NotBlank(message = "value1值为空")
private String value1;
private String value2;
private String value3;
@NotZero(message = "value4不可为零")
private Integer value4;
@NotZero(message = "value5不可为零")
private Float value5;
@NotZero(message = "value6不可为零")
private Double value6;
}
验证请求如下: