SpringBoot数据校验
前提:要先引入validation依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Validation 2.x 的22个注解
注解 | 验证的数据类型 | 说明 |
---|---|---|
Null | 所有类型 | 验证元素值必须为 null |
NotNull | 所有类型 | 验证元素值必须不为 null |
NotBlank | CharSequence | 验证元素值不能为 null,并且至少包含一个非空白字符。 |
NotEmpty | CharSequence、Collection、Map、Array | 验证元素值不能为 null,且不能为空 |
Size(min = min, max = max) | 同 NotEmpty | 验证元素的 size 必须在 min 和 max 之间(包含边界),认为 null 是有效的 |
AssertFalse | boolean、Boolean | 验证元素值必须为 false,认为 null 是有效的 |
AssertTrue | 同 AssertFalse | 验证元素值必须为 true,认为 null 是有效的 |
DecimalMax(value=, inclusive=) | BigDecimal、BigInteger、CharSequence,byte、 short、int、long 及其包装类型,由于舍入错误,不支持double和float | 验证元素值必须小于等于指定的 value 值,认为 null 是有效的 |
DecimalMin | 同 DecimalMax | 验证元素值必须大于等于指定的 value 值,认为 null 是有效的 |
Max | 同 DecimalMax,不支持CharSequence | 验证元素值必须小于等于指定的 value 值,认为 null 是有效的 |
Min | 同 DecimalMax,不支持CharSequence | 验证元素值必须大于等于指定的 value 值,认为 null 是有效的 |
Digits(integer =, fraction =) | 同 DecimalMax | 验证元素整数位数的上限 integer 与小数位数的上限 fraction,认为 null 是有效的 |
Positive | BigDecimal、BigInteger,byte、short、int、long、float、double 及其包装类型 | 验证元素必须为正数,认为 null 是有效的 |
PositiveOrZero | 同Positive | 验证元素必须为正数或 0,认为 null 是有效的 |
Negative | 同Positive | 验证元素必须为负数,认为 null 是有效的 |
NegativeOrZero | 同Positive | 验证元素必须为负数或 0,认为 null 是有效的 |
Future | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 验证元素值必须是一个将来的时间,认为 null 是有效的 |
FutureOrPresent | 同 Future | 验证元素值必须是当前时间或一个将来的时间,认为 null 是有效的 |
Past | 同 Future | 验证元素值必须是一个过去的时间,认为 null 是有效的 |
PastOrPresent | 同 Future | 验证元素值必须是当前时间或一个过去的时间,认为 null 是有效的 |
Email(regexp = 正则表达式,flag = 标志的模式) | CharSequence | 验证注解的元素值是Email,可以通过 regexp 和 flag 指定自定义的 email 格式,认为 null 是有效的 |
Pattern | 同 Email | 验证元素值符合正则表达式,认为 null 是有效的 |
一、实体类DTO校验
1、第一种Controller内部捕捉校验信息
①第一步定义dto,所有校验都在dto的属性上进行。
public class ValidatorDto {
@NotEmpty(message = "名字不能为空!")
public String name;
@Email(message = "email格式不正确!")
public String email;
@Min(value = 10)
public int age;
@NotEmpty(message = "家庭住址至少填写一个")
List<String> homList;
public List<String> getHomList() {
return homList;
}
public void setHomList(List<String> homList) {
this.homList = homList;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
②第二步定义Controller,验证的参数dto属性要加@Validated,BindingResult参数中包含捕捉校验信息,程序执行过程会进入该方法体内。
@RestController
public class TestValidatorController {
@RequestMapping("/testValidator")
@ResponseBody
public ValidatorDto testValidator(@Validated ValidatorDto dto, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
List<ObjectError> listObjectError = bindingResult.getAllErrors();
for (ObjectError objectError : listObjectError) {
System.out.println(objectError.getDefaultMessage());
}
}
return dto;
}
}
③访问参数及System.out.println输出的校验结果:
http://localhost:8080/testValidator?name=&email=@qq.com&age=5
最小不能小于10
家庭住址至少填写一个
名字不能为空!
email格式不正确!
2、第二种校验信息在抛出的异常中获取
①第一步定义dto,所有校验都在dto的属性上进行。
public class ValidatorDto {
@NotEmpty(message = "名字不能为空!")
public String name;
@Email(message = "email格式不正确!")
public String email;
@Min(value = 10)
public int age;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
②第二步定义Controller,验证的参数dto属性要加@Validated,程序不会进入该方法体内,校验信息会在抛出的异常中被捕获(我使用的是全局异常@ControllerAdvice+@ExceptionHandler捕获异常信息)。
@RestController
public class TestValidatorController {
@RequestMapping("/testValidator2")
@ResponseBody
public ValidatorDto testValidator(@Validated ValidatorDto dto) {
return dto;
}
}
③访问参数及校验结果:
http://localhost:8080/testValidator2?name=&email=@qq.com&age=5
3、分组校验
在不同情况下,可能对JavaBean对象的数据校验规则有所不同,有时需要根据数据状态对JavaBean中的某些属性字段进行单独验证,groups 属性将验证进行分组。
①第一步定义dto,所有校验都在dto的属性上进行。
分组一校验:name和age
分组二校验:email和age
public class ValidatorDto2 {
@NotEmpty(message = "名字不能为空!", groups = ValidatorGroup1.class)
public String name;
@Email(message = "email格式不正确!", groups = ValidatorGroup2.class)
public String email;
@Min(value = 10, groups = { ValidatorGroup1.class, ValidatorGroup2.class })
public int age;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
②第二步定义分组(2个空接口类),这两空接口类,就是上面dto中的groups 分组。
public interface ValidatorGroup1 {}
public interface ValidatorGroup2 {}
③第三步定义Controller,验证的参数dto属性要加@Validated,分组的class要放到Validated中,BindingResult参数中包含捕捉校验信息,程序执行过程会进入该方法体内。
RestController
public class TestValidatorController {
@RequestMapping("/testValidatorGroup1")
@ResponseBody
public ValidatorDto2 testValidatorGroup1(@Validated(ValidatorGroup1.class) ValidatorDto2 dto,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
List<ObjectError> listObjectError = bindingResult.getAllErrors();
for (ObjectError objectError : listObjectError) {
System.out.println(objectError.getDefaultMessage());
}
}
return dto;
}
@RequestMapping("/testValidatorGroup2")
@ResponseBody
public ValidatorDto2 testValidatorGroup2(@Validated(ValidatorGroup2.class) ValidatorDto2 dto,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
List<ObjectError> listObjectError = bindingResult.getAllErrors();
for (ObjectError objectError : listObjectError) {
System.out.println(objectError.getDefaultMessage());
}
}
return dto;
}
}
④访问参数及校验结果:
分组一:
http://localhost:8080/testValidatorGroup1?name=&email=@qq.com&age=5
名字不能为空!
最小不能小于10
分组二:
http://localhost:8080/testValidatorGroup2?name=&email=@qq.com&age=5
email格式不正确!
最小不能小于10
结果说明:name校验只属于分组一,email校验只属于分组二,age校验属于分组一和分组二,跟定义的dto定义一致。
二、直接参数校验
直接参数校验就是把dto中要校验的属性当做参数放到controller的参数内,所有的校验也都放到controller中参数中,其余处理和实体类DTO校验无差异。
三、自定义校验注解
预期:做一个判断非0的注解
①定义一个注解(仿照已有的现成注解做),其中@Constraint(validatedBy = NotEqualZeroValidator.class)的NotEqualZeroValidator为自己定义的校验类
@Documented
@Constraint(validatedBy = NotEqualZeroValidator.class)
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE })
@Retention(RetentionPolicy.RUNTIME)
public @interface NotEqualZero
String message() default "必须不等于0";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
②定义自己的校验类,需要实现ConstraintValidator<NotEqualZero, Object>接口
public class NotEqualZeroValidator implements ConstraintValidator<NotEqualZero, Object>{
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
return (Integer)value != 0;
}
}
③使用自定义注解@NotEqualZero
public class ValidatorDto {
@NotEmpty(message = "名字不能为空!")
public String name;
@Email(message = "email格式不正确!")
public String email;
@NotEqualZero
public int age;
@NotEmpty(message = "家庭住址至少填写一个")
List<String> homeList;
public List<String> getHomeList() {
return homeList;
}
public void setHomeList(List<String> homeList) {
this.homeList = homeList;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
④controller
@RestController
public class TestValidatorController {
@RequestMapping("/testValidator")
@ResponseBody
public ValidatorDto testValidator(@Validated ValidatorDto dto, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
List<ObjectError> listObjectError = bindingResult.getAllErrors();
for (ObjectError objectError : listObjectError) {
System.out.println(objectError.getDefaultMessage());
}
}
return dto;
}
}
⑤结果
http://localhost:8080/testValidator?name=&email=@qq.com&age=0
名字不能为空!
email格式不正确!
家庭住址至少填写一个
必须不等于0