Spring Validation验证框架对参数的验证机制提供了@Validated;
javax提供了@Valid,配合BindingResult可以直接提供参数验证结果;
检验Controller入参规范时,@Validated和@Valid在基本验证功能上没有太多区别。但在分组、注解地方、嵌套验证等功能上有所不同:
1. 分组
@Validated:提供分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制。
@Valid:没有分组功能。
2. 注解
@Validated:用在类型、方法和方法参数上。但不能用于成员属性(field)。
@Valid:可以用在方法、构造函数、方法参数和成员属性(field)上。
3. 嵌套验证
一个待验证的pojo类,其中还包含了待验证的对象,需要在待验证对象上注解@valid,才能验证待验证对象中的成员属性。
@Validated分组校验示例:
假设小学生身高范围在【100, 150】,初中生身高范围在【130,170】
public class Student {
//针对两种情况,新建对应接口类
public interface Pupil{}
public interface Junior{}
@NotNull(message = "姓名不能为空")
private String name;
@Range(min = "100", max = "150", message = "小学生身高不在正常范围内", groups = {Pupil.class})
@Range(min = "130", max = "170", message = "初中生身高不在正常范围内", groups = {Junior.class})
private int height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
@PostMapping("/pupil")
public Object ulti (@Validated(Student.Pupil.class) @RequestBody Student person) {
return person;
}
特殊情况下,如果@Range里的min和max要求在配置中心进行配置,更灵活机动,就需求自定义校验注解@MyRange
###配置中心
student:
height:
#小学
pupil:
min: 100
max: 150
#初中
junior:
min: 150
max: 180
----------------------------------------------------------
public class Student {
public interface Pupil{}
public interface Junior{}
@NotNull(message = "姓名不能为空")
private String name;
@MyRange(min = "student.height.pupil.min", max = "student.height.pupil.max", message = "小学生身高不在正常范围内", groups = {Pupil.class})
@MyRange(min = "student.height.junior.min", max = "student.height.junior.max", message = "初中生身高不在正常范围内", groups = {Junior.class})
private String height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
}
--------------------------------------------------------------------
###自定义校验注解
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@Repeatable(MyRange.List.class)
@Constraint(validatedBy = {MyRangeValidator.class})
public @interface MyRange {
String message() default "{数值范围校验}";
/**
* @return value the element must be higher or equal to
*/
String min();
String max();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@interface List {
MyRange[] value();
}
}
------------------------------------------------------------
@Component
@RefreshScope
public class RangeValidator implements ConstraintValidator<Range, String> {
@Resource
private Environment environment;
private String minStr = "";
private String maxStr = "1";
@Override
public void initialize(Range constraintValidator) {
minStr = constraintValidator.min();
maxStr = constraintValidator.max();
}
// 这里必须要实现 isValid(),校验逻辑就是这里实现
@Override
public boolean isValid(String valStr, ConstraintValidatorContext constraintValidatorContext) {
if (valStr == null || "".equals(valStr)) {
return false;
}
//判断数值字符串是否都是数值,非负整数
final String regex = "^[1-9]\\d*|0$";
boolean isMatch = Pattern.matches(regex, valStr);
if (!isMatch) {
//不是非负整数
return false;
}
long min = 0L;
long max = 9223372036854775807L;
final int val = Integer.valueOf(valStr);
try {
min = Integer.valueOf(environment.getProperty(minStr));
max = Integer.valueOf(environment.getProperty(maxStr));
} catch (Exception e) {
e.printStackTrace();
}
if (min > val || val > max) {
return false;
}
return true;
}
}
通过groovy表达式拓展oval——实现根据同一实体中的其他属性值对某个字段进行校验
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>3.0.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sf.oval/oval -->
<dependency>
<groupId>net.sf.oval</groupId>
<artifactId>oval</artifactId>
<version>3.2.1</version>
</dependency>
public class Student {
@NotNull(message = "姓名不能为空")
private String name;
@net.sf.oval.constraint.Range(min = 10, max = 60, when = "groovy:_this.name=='jack'", message = "jack数据异常")
@net.sf.oval.constraint.Range(min = 8, max = 100, when = "groovy:_this.name=='tom'", message = "tom数据异常")
private String height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
}
-------------------------------------------------------------------------
Validator validator = new Validator();
List<ConstraintViolation> messages = validator.validate(person);
StringBuilder sb = new StringBuilder();
messages.stream().forEach(message -> sb.append(message.getMessage()));