参数校验
用法
SpringBoot 中的 bean validation 是集成了hibernate-validator和Jakarta Bean Validation,集成在 spring-context 包。
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>xxxx</version>
</dependency>
简单的校验
-
@Valid:常见用在方法、字段属性、参数上进行校验,不提供分组功能,但是支持字段属性的嵌套校验。
-
@Validated:是spring提供的对@Valid的封装,常见用在方法参数上进行分组校验。
校验注解
如果使用比较少的话,你可能不太熟悉都有哪些注解校验,分别是校验什么功能,所以作为暖男的我在这里把所有校验注解都列出来~~
| 注解 | 验证的数据类型 | 含义 |
|---|---|---|
| @Valid | 任何非原子类型 | 被注释的元素是一个对象,需要检查此对象的所有字段值;指定递归验证关联的对象;如用户对象中有个地址对象属性,如果想在验证用户对象时一起验证地址对象的话,在地址对象上加@Valid注解即可级联验证 |
| @NotNull | 任意类型 | 验证字段不为 null |
| @NotBlank | CharSequence子类型(CharBuffer、String、StringBuffer、StringBuilder) | 验证注解的元素值不为空(不为null、去除首尾空格后长度不为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首尾空格 |
| @NotEmpty | CharSequence子类型、Collection、Map、数组 | 验证字段不为null且不为空,常用于校验集合元素不为空 |
| @AssertTrue | Boolean,boolean | 被注释的元素必须为 true |
| @AssertFalse | Boolean,boolean | 被注释的元素必须为 false |
| @Min | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型 | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
| @Max | 和@Min要求一样 | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值,与 @Max 类似,不同的是它限定值可以带小数,一般用于 double 和 Bigdecimal 类型 |
| @DecimalMin | 和@Min要求一样 | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
| @DecimalMax | 和@Min要求一样 | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
| @Size(min, max) | 字符串、Collection、Map、数组等 | 验证注解的元素值的在min和max(包含)指定区间之内,如字符长度、集合大小 |
| @Length(min, max) | CharSequence子类型 | 被注释的字符串的大小必须在指定的范围内 |
| @Range(min, max) | BigDecimal,BigInteger,CharSequence, byte, short, int, long等原子类型和包装类型 | 验证数字类型字段值在最小值和最大值之间 |
| @Digits(integer=整数, fraction=小数) | 和@Min要求一样 | 被注释的元素必须是一个数字,验证注解的元素值的整数位数和小数位数上限(必须在可接受的范围内) |
| @Past | java.util.Date,java.util.Calendar;Joda Time类库的日期类型 | 被注释的元素必须是一个过去的日期(元素值(日期类型)比当前时间早) |
| @Future | 与@Past要求一样 | 被注释的元素必须是一个将来的日期(元素值(日期类型)比当前时间晚) |
| @PastOrPresent | 验证日期类型字段值比当前时间早或者是当前日期 | |
| @FutureOrPresent | 验证日期类型字段值比当前时间晚或者是当前日期 | |
| @PositiveOrZero | 校验必须是正数或 0 | |
| @NegativeOrZero | 校验必须是负数或 0 | |
| @Negative | 校验必须是负数 | |
| @Positive | 校验必须是正数 | |
| @Pattern(regexp,flag) | CharSequence的子类型 | 被注释的元素必须符合指定的正则表达式 |
| CharSequence的子类型 | 验证注解的元素值是Email,也可以通过regexp和flag指定自定义的email格式 | |
| @URL(protocol,host, port,regexp, flags) | CharSequence的子类型 | 被注释的字符串必须是一个有效的url |
| @UniqueElements | Collection | 验证所提供的Collection中的每个对象都是唯一的,即在集合中找不到2个相等的元素。 |
| @ScriptAssert(lang= ,script=) | 业务类 | 校验复杂的业务逻辑 |
| @CreditCardNumber |
分组校验
简单使用
新增用户信息和修改用户信息所需要验证的字段是不同的.
public interface AppUserVaildC extends Default {
}
public interface AppUserVaildU extends Default {
}
Model中
@Range(min = 0,max = 100,message = "年龄必须在[0,100]",groups={Default.class})
/**年龄*/
private Integer age;
@Range(min = 0,max = 2,message = "性别必须在[0,2]",groups = {AppUserVaildC.class})
/**性别 0:未知;1:男;2:女*/
private Integer sex;
Controller中使用
@PostMapping("save")
public void v1(@RequestBody @Validated({AppUserVaildC.class, AppUserVaildU.class}) AppUser appUser,BindingResult result){
if(result.hasErrors()){
for (ObjectError error : result.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
}
}
普通使用
@Autowired
private Validator validator;
@PostMapping
public void apply(@RequestBody @Validated AppInvoiceApplyRequest request) {
Set<ConstraintViolation<FundApplyVerifyRequest>> constraintViolations = null;
if (isCorporation) { //企业开票
constraintViolations = validator.validate(request,AppInvoiceApplyRequest.CompanyInvoiceGroup.class);
} else { //个人开票
constraintViolations = validator.validate(request,AppInvoiceApplyRequest.PersonalCompanyInvoiceGroup.class);
}
for (ConstraintViolation<AppUser> model : violationSet) {
System.out.println(model.getMessage());
}
if (CollectionUtils.isNotEmpty(constraintViolations)) { //如果没有通过校验,抛出异常
throw new ConstraintViolationException(constraintViolations);
}
appInvoiceService.save(request);
}
组序列
@GroupSequence,除了按组指定是否验证之外,还可以指定组的验证顺序,前面组验证不通过的,后面组不进行验证.
@GroupSequence({First.class, Second.class})
public interface Group {
}
Controller中使用
@PostMapping("save")
public void v1(@RequestBody @Validated({Group.class}) AppUser appUser){
}
自己定义注解
下面是一个自定义大小写的验证
public enum CaseMode {
UPPER,
LOWER;
}
// 首先定义一个校验注解
@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckCaseValidator.class) //用哪个校验器校验
@Documented
public @interface CheckCase {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
CaseMode value();
}
// 校验逻辑,我们只需要去实现 ConstraintValidator 这个接口重写 isValid 方法即可
public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> {
private CaseMode caseMode;
@Override
public void initialize(CheckCase checkCase) {
this.caseMode = checkCase.value();
}
@Override
public boolean isValid(String s, ConstraintValidatorContext context) {
if (s == null) {
return true;
}
if (caseMode == CaseMode.UPPER) {
return s.equals(s.toUpperCase());
} else {
return s.equals(s.toLowerCase());
}
}
}
使用
@CheckCase(value = CaseMode.LOWER ,message = "年必须是小写",groups={Default.class})
private String loginName;