持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情
一、分组校验
如果同一个参数,需要在不同场景下应用不同的校验规则,就需要用到分组校验了。比如:新注册用户还没起名字,我们允许name字段为空,但是在更新时候不允许将名字更新为空字符。
分组校验有三个步骤:
1、定义一个接口类
public interface Update extends Default{
}
Default类是javax.validation提供的分组类,自定义的Update分组接口继承了Default接口。校验注解(如: @NotBlank)和@validated默认其他注解都属于Default.class分组,这一点在javax.validation.groups.Default注释中有说明
/**
* Default Bean Validation group.
* <p>
* Unless a list of groups is explicitly defined:
* <ul>
* <li>constraints belong to the {@code Default} group</li>
* <li>validation applies to the {@code Default} group</li>
* </ul>
* Most structural constraints should belong to the default group.
*
* @author Emmanuel Bernard
*/
public interface Default {
}
2、在校验注解上添加groups属性指定分组
public class UserVO {
@NotBlank(message = "name 不能为空",groups = Update.class)
private String name;
// 省略其他代码...
}
3、Controller方法的@Validated注解添加分组类
@PostMapping("update")
public ResultInfo update(@Validated({Update.class}) UserVO userVO) {
return new ResultInfo().success(userVO);
}
在编写Update分组接口时,如果继承了Default,下面两个写法就是等效的: @Validated({Update.class}),@Validated({Update.class,Default.class}) 如果Update不继承Default,@Validated({Update.class})就只会校验属于Update.class分组的参数字段
二、递归校验
如果 UserVO 类中增加一个 OrderVO 类的属性,而 OrderVO 中的属性也需要校验,就用到递归校验了,只要在相应属性上增加@Valid注解即可实现(对于集合同样适用)
public class OrderVO {
@NotNull
private Long id;
@NotBlank(message = "itemName 不能为空")
private String itemName;
// 省略其他代码...
}
public class UserVO {
@NotBlank(message = "name 不能为空",groups = Update.class)
private String name;
//需要递归校验的OrderVO
@Valid
private OrderVO orderVO;
// 省略其他代码...
}
三、自定义校验
validation 为我们提供了这么多特性,几乎可以满足日常开发中绝大多数参数校验场景了。Validation允许用户自定义校验,方便扩展,应对更多复杂的业务场景。
实现很简单,分两步:
1、自定义校验注解
package cn.soboys.core.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
/**
* @author kenx
* @version 1.0
* @date 2021/1/21 20:49
* 日期验证 约束注解类
*/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {IsDateTimeValidator.class}) // 标明由哪个类执行校验逻辑
public @interface IsDateTime {
// 校验出错时默认返回的消息
String message() default "日期格式错误";
//分组校验
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
//下面是我自己定义属性
boolean required() default true;
String dateFormat() default "yyyy-MM-dd";
}
注意:message用于显示错误信息这个字段是必须的,groups和payload也是必须的
@Constraint(validatedBy = { HandsomeBoyValidator.class})用来指定处理这个注解逻辑的类。
2、编写校验者类
package cn.soboys.core.validator;
import cn.hutool.core.util.StrUtil;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @author kenx
* @version 1.0
* @date 2021/1/21 20:51
* 日期验证器
*/
public class IsDateTimeValidator implements ConstraintValidator<IsDateTime, String> {
private boolean required = false;
private String dateFormat = "yyyy-MM-dd";
/**
* 用于初始化注解上的值到这个validator
* @param constraintAnnotation
*/
@Override
public void initialize(IsDateTime constraintAnnotation) {
required = constraintAnnotation.required();
dateFormat = constraintAnnotation.dateFormat();
}
/**
* 具体的校验逻辑
* @param value
* @param context
* @return
*/
public boolean isValid(String value, ConstraintValidatorContext context) {
if (required) {
return ValidatorUtil.isDateTime(value, dateFormat);
} else {
if (StrUtil.isBlank(value)) {
return true;
} else {
return ValidatorUtil.isDateTime(value, dateFormat);
}
}
}
}
public class ValidatorUtil {
/**
* 验证 日期
*
* @param date
* @param dateFormat
* @return
*/
public static boolean isDateTime(String date, String dateFormat) {
if (StrUtil.isBlank(date)) {
return false;
}
try {
DateUtil.parse(date, dateFormat);
return true;
} catch (Exception e) {
return false;
}
}
}
四、注意事项## @NotNull、@NotEmpty、@NotBlank
1、@NotNull
不能为 null,但可以为 empty,一般用在 Integer 类型的基本数据类型的非空校验上,而且被其标注的字段可以使用 @size、@Max、@Min 对字段数值进行大小的控制
2、@NotEmpty
不能为 null,且长度必须大于 0,一般用在集合类上或者数组上
3、@NotBlank
只能作用在接收的 String 类型上,注意是只能,不能为 null,而且调用 trim() 后,长度必须大于 0即:必须有实际字符
4、使用 @NotBlank 等注解时,一定要和 @valid 一起使用,否则 @NotBlank 不起作用。
5、一个 BigDecimal 的字段使用字段校验标签应该为 @NotNull。
6、在使用 @Length 一般用在 String 类型上可对字段数值进行最大长度限制的控制。
7、在使用 @Range 一般用在 Integer 类型上可对字段数值进行大小范围的控制。