SpringBoot使用@Vaild数据验证及自定义验证注解
常用注解
@NotNull:不能为null,但可以为empty
@NotEmpty:不能为null,而且长度必须大于0,用于集合
@NotBlank: 不能为null,而且不为空字符串 用于String
@AssertTrue //用于boolean字段,该字段只能为true
@AssertFalse//该字段的值只能为false
@CreditCardNumber//对信用卡号进行一个大致的验证
@DecimalMax//只能小于或等于该值
@DecimalMin//只能大于或等于该值
@Digits(integer=2,fraction=20)//检查是否是一种数字的整数、分数,小数位数的数字。
@Email//检查是否是一个有效的email地址
@Future//检查该字段的日期是否是属于将来的日期
@Length(min=,max=)//检查所属的字段的长度是否在min和max之间,只能用于字符串
@Max//该字段的值只能小于或等于该值
@Min//该字段的值只能大于或等于该值
@NotNull//不能为null
@NotBlank//不能为空,检查时会将空格忽略
@NotEmpty//不能为空,这里的空是指空字符串
@Null//检查该字段为空
@Past//检查该字段的日期是在过去
@Size(min=, max=)//检查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等
@URL(protocol=,host,port)//检查是否是一个有效的URL,如果提供了protocol,host等,则该URL还需满足提供的条件
@Valid//该注解只要用于字段为一个包含其他对象的集合或map或数组的字段,或该字段直接为一个其他对象的引用,//这样在检查当前对象的同时也会检查该字段所引用的对象
首先导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
第一种方式(@Validated+校验注解)
需要在Controller类头部添加@Validated注解,不加注解不会错误信息,这种方法不用手动抛出异常,不过这种方式感觉应用场景不是很多,毕竟我们接收数据大多都是使用dto封装的
/**
* 使用注解校验get请求平面参数,需要在Controller类头部添加@Validated注解,否则不能成功校验,这种方法不用手动抛出异常
*
* @param username
* @return
*/
@GetMapping("/testGetByValidated")
public ResponseResult testGetByValidated(@Length(max = 4) @RequestParam("username") String username) {
return new ResponseResult(ResultEnum.SUCCESS);
}
第二种方式(@Vaild+全局异常处理器+校验注解)最常用
post方法传入单个对象进行校验,在参数前添加@Valid注解,校验失败时会抛出异常并使用全局异常进行处理
/**
* post方法传入单个对象进行校验,在参数前添加@Valid注解,校验失败时会抛出异常并使用全局异常进行处理
*
* @param userInfo 用户信息
* @return ResponseResult
*/
@PostMapping("/testUserInfo")
public ResponseResult testUserInfo(@Valid @RequestBody UserInfo userInfo) {
return new ResponseResult(ResultEnum.SUCCESS);
}
@Data
public class UserInfo {
@NotBlank(message = "年龄不为空")
@Max(value = 18, message = "不能超过18岁")
private String age;
@NotBlank(message = "性别不能为空")
private String gender;
}
其中为了避免多次请求后错误信息不一致(注:年龄不为空和不能超过18岁随机出现),可以用以下配置
@Configuration
public class ValidationConfig {
@Bean
public Validator validator(final AutowireCapableBeanFactory autowireCapableBeanFactory) {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
.constraintValidatorFactory(new SpringConstraintValidatorFactory(autowireCapableBeanFactory))
//设置有一个错误就直接返回
.failFast(true)
.buildValidatorFactory();
return validatorFactory.getValidator();
}
}
第三种方式(工具类手动校验)
post方法传入对象,手动校验,此时参数前没有添加@Valid注解,所以不会自动进行校验,手动调用validate方法进行校验,失败时会抛出异常
/**
* post方法传入对象,手动校验,此时参数前没有添加@Valid注解,所以不会自动进行校验,手动调用validate方法进行校验,失败时会抛出异常
*
* @param userInfo
* @return ResponseResult
*/
@PostMapping("/checkByMethod")
public ResponseResult checkByMethod(@RequestBody UserInfo userInfo) {
//调用api校验
MyValidationUtils.validate(userInfo);
return new ResponseResult(ResultEnum.SUCCESS);
}
/**
* 手动调用api方法校验对象
*/
public class MyValidationUtils {
public static void validate(@Valid Object user) {
Set<ConstraintViolation<@Valid Object>> validateSet = Validation.buildDefaultValidatorFactory()
.getValidator()
.validate(user, new Class[0]);
if (!CollectionUtils.isEmpty(validateSet)) {
String messages = validateSet.stream()
.map(ConstraintViolation::getMessage)
.reduce((m1, m2) -> m1 + ";" + m2)
.orElse("参数输入有误!");
//抛出异常进入到异常处理器中
throw new IllegalArgumentException(messages);
}
}
}
第四种方式(校验集合+@Vaild+@Validated)
post方法传入多个对象,当使用@Valid校验对象集合时,要在控制层添加@Validated注解,否则不会对集合中的每个对象进行校验
/**
* post方法传入多个对象,当使用@Valid校验对象集合时,要在控制层添加@Validated注解,否则不会对集合中的每个对象进行校验
*
* @param userInfo
* @return ResponseResult
*/
@PostMapping("/testUserList")
public ResponseResult testUserList(@Valid @RequestBody List<UserInfo> userInfo) {
return new ResponseResult(ResultEnum.SUCCESS);
}
第五种方式(BindingResult+异常处理器)
将校验结果放进BindingResult里面,用户自行判断并处理
/**
* 将校验结果放进BindingResult里面,用户自行判断并处理
*
* @param userInfo
* @param bindingResult
* @return
*/
@PostMapping("/testBindingResult")
public String testBindingResult(@RequestBody @Valid UserInfo userInfo, BindingResult bindingResult) {
// 参数校验
if (bindingResult.hasErrors()) {
String messages = bindingResult.getAllErrors()
.stream()
.map(ObjectError::getDefaultMessage)
.reduce((m1, m2) -> m1 + ";" + m2)
.orElse("参数输入有误!");
//这里可以抛出自定义异常,或者进行其他操作
throw new IllegalArgumentException(messages);
}
return "操作成功!";
}
自定义校验注解
随着业务不断壮大,基本的注解已不能满足需求,这时候我们需要自定义一些校验注解,例如需要身份证号校验
@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdentityCardNumber.IdentityCardNumberValidator.class)
public @interface IdentityCardNumber {
String message() default "身份证号码不合法";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
class IdentityCardNumberValidator implements ConstraintValidator<IdentityCardNumber, Object> {
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
return IdCardValidatorUtils.isValidatedAllIdcard(value.toString());
}
}
}
@Data
public class UserInfo {
@NotBlank(message = "年龄不为空")
@Max(value = 18, message = "不能超过18岁")
private String age;
@NotBlank(message = "性别不能为空")
private String gender;
@NotBlank(message = "身份证号不能为空")
@IdentityCardNumber
private String idCard;
}
右边附有源码,有兴趣的小伙伴可以下载下来看看custom-annotation.zip