我们在写Controller或者Service时经常在方法中写入大量的数据校验代码,如下
if(StringUtils.isEmpty(pass)){
return Result.error(ErrorCodeEnum.PASSWORD_EMPTY);
}else if(!ValidatorUtil.isMobile(mobile)){
return Result.error(ErrorCodeEnum.MOBILE_PATTERN_WRONG);
}
不仅浪费时间,还让业务逻辑代码更复杂,下面我们使用JSR303为我们提供的校验工具避免过多的代码校验
JSR303
JSR 303 – Bean Validation 是一个数据验证的规范,不符合规范的将返回一条具体的信息说明该规范的错误
- 有以下注解为我们提供校验
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式
- 例如:一个封装的登录信息类
LoginVo
@Data
public class LoginVo {
@NotNull
@Length(max = 11,min = 11)
private String mobile;//手机号
@NotNull
@Length(min = 32)
private String password;//密码
}
我们使用@NotNull注解为该字段声明为非空,也就是前端传过来的该参数必须有具体的值,不能为null@Length注解为字段声明字符串最长最短的临界条件,前端传来的数据必须符合要求
- 最后我们在使用到登录封装类的地方(如Controller参数)打上
@Valid
注解,就会帮我们的封装类进行校验
public Result doLogin( @Valid LoginVo loginVo)
自定义验证器
为了深入了解JSR303,我们将实现一个自定义验证器(自定义注解实现具体的验证代码),观察它是如何工作的
- 现在我们需要为登录信息类里的
mobile
字段进行手机号校验(校验是否为手机号,格式错误返回格式错误信息) - 我们将其声明为
@IsMobile
注解
通过观察上述众多注解,发现验证器的一般代码如下
- validatedBy:就是要传入一个具体的自定义验证器类(实现验证器逻辑)
- message方法:这里返回具体的校验失败的错误信息
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(IsMobile.List.class)
@Documented
@Constraint(
validatedBy = {MobileValidator.class}//这里的validatedBy就是要传入一个具体的自定义验证器类(实现验证器逻辑)
)
public @interface IsMobile {
boolean required() default true;//可以使用该字段声明是否必须
String message() default "手机号格式不合法";//这里返回具体的校验失败的错误信息
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface List {
IsMobile[] value();
}
}
-
所以我们需要编写一个类实现验证器逻辑
MobileValidator
验证器需要实现
ConstraintValidator
接口,该接口用泛型指定两个参数
重写其中的isValid
方法
public class MobileValidator implements ConstraintValidator<IsMobile, String> {
private boolean required = true;
@Override
public void initialize(IsMobile constraintAnnotation) {
}
@Override
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
if(required){//如果必须 说明以传入 则进行效验
return ValidatorUtil.isMobile(s);
}else {//如果不必须 说明可以没有 先判断是否为空再校验
if (StringUtils.isEmpty(s)) {
return false;
} else return ValidatorUtil.isMobile(s);
}
}
}
- ValidatorUtil 工具类 用户验证
public class ValidatorUtil {
//使用正则匹配1开头的11位数字
private static final Pattern mobile_pattern = Pattern.compile("1\\d{10}");
//如果匹配返回true,手机号格式正确
public static boolean isMobile(String s){
if(StringUtils.isEmpty(s)){
return false;
}
Matcher m = mobile_pattern.matcher(s);
return m.matches();
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。