• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
打工人丶
博客园    首页    新随笔    联系   管理    订阅  订阅

JSR-303 实现参数校验

参考:https://blog.csdn.net/qq_45076180/article/details/106219684
参考:https://zhuanlan.zhihu.com/p/97555913
参考:https://blog.csdn.net/u013934408/article/details/108872775
参考:https://blog.csdn.net/adparking/article/details/112918333

公众号文章:
https://mp.weixin.qq.com/s/xQRwjEDiVxe_t-tVxXmwzg
https://mp.weixin.qq.com/s/VNrs1zGPgx9GsIqXEDZP0Q

1. 什么是JSR-303

JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation,对 Java Bean 中的字段的值进行验证,官方参考实现是Hibernate Validator。

它是一个hibernete独立的jar包,所以使用这个jar包并不需要一定要集成Hibernete框架。。

Spring MVC 3.x 之中也大力支持 JSR-303,可以在控制器中对表单提交的数据方便地验证。

JSR-303官网



2. JSR-303内置校验规则

@Null 验证对象是否为null

@NotNull 验证对象是否不为null, 无法查检长度为0的字符串

@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格

@NotEmpty 检查约束元素是否为NULL或者是EMPTY

@AssertTrue 验证 Boolean 对象是否为 true

@AssertFalse 验证 Boolean 对象是否为 false

@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内

@Length(min=, max=) Validates that the annotated string is between min and max included

@Past 验证 Date 和 Calendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期

@Future 验证 Date 和 Calendar 对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期

@Pattern 验证 String 对象是否符合正则表达式的规则,被注释的元素符合制定的正则表达式,regexp:正则表达式 flags: 指定@Min 验证 Number 和 String 对象是否大等于指定的值,@Max 验证 Number 和 String 对象是否小等于指定的值

@DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度

@DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度

@Digits 验证 Number 和 String 的构成是否合法

@Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度

@Range(min=, max=) 被指定的元素必须在合适的范围内

@Range(min=10000,max=50000,message=”range.bean.wage”)

@Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)

@CreditCardNumber信用卡验证

@Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证

@ScriptAssert(lang= ,script=, alias=)

@URL(protocol=,host=, port=,regexp=, flags=)





3. SpringBoot整合JSR-303

3.1 导入依赖

<dependency> 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

3.2 在类的属性上加上相应校验注解

@NotNull(message="名字不能为空")
private String userName;

@Max(value=120,message="年龄最大不能查过120")
private int age;

@Email(message="邮箱格式错误")
private String email;

3.3 实现校验

在Controller的处理方法的参数前,加@Validated注解,出错后,错误信息会放置在 Errors或BindingResult 的对象参数中

@RequestMapping("/login")
public String testValid(@Validated User user, BindingResult result){
    if (result.hasErrors()){
         List<ObjectError> errorList = result.getAllErrors();
         for(ObjectError error : errorList){
         System.out.println(error.getDefaultMessage());
      }
    }           
  return "test";
}

3.4 通过全局统一异常替换BindingResult写法

3.3那种做法给controller后的需要校验的bean后加一个BindingResult类就可以获得校验的结果,但是相对麻烦,每次都要写BindingResult。在大项目中一般使用统一异常处理校验结果,返回json格式,如下所示。这样就不需要在controller加一个BindingResult类。

//后端校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public  RtnResult handValidException(MethodArgumentNotValidException e){
	BindingResult bindingResult = e.getBindingResult();
	Map<String, Object> map = new HashMap<>();
	bindingResult.getFieldErrors().forEach(fieldError -> {
		map.put(fieldError.getField(),fieldError.getDefaultMessage());
	});
	return RtnResult.fail(map);
}








3.5 分组校验(多场景的复杂校验)

分组校验应用场景:添加和更新需要不同的校验规则时,且你更新和添加用的实体都是同一个时。有些项目会要求必须新增实体是一个,更新实体是一个,那么这种情况下就不考虑这种。

3.5.1 先在你的实体上创建添加和更新的接口,该接口仅仅是多场景的一个标识,接口内容为空即可。
public interface AddGroup {
}

public interface UpdateGroup {
}
3.5.2 在 @NotBlank(message = “密码不能为空”) 后加上groups 用于表示给校验注解标注什么情况下需要校验(添加/更新)
//AddGroup.class是添加的标识组,可以有多个
@NotBlank(message = "密码不能为空", groups = {AddGroup.class})
private String password;

@NotBlank(message = "姓名不能为空", groups = {AddGroup.class,UpdateGroup.class})
private String name;
3.5.3 必须使用@Validated注解制定只是用哪个操作,否则不生效。
//添加用户   @Validated指定哪个操作使用校验
@PostMapping("/addUser")
public RtnResult addUser(@Validated(AddGroup.class) @RequestBody ClaimUser claimUser){
     return  claimUserService.addUser(claimUser);
}

如果使用分组校验,校验注解没有使用groups,则校验不生效,一定要使用groups!!


4. 自定义校验注解

当给出的校验规则不能满足需求,可以通过自定义校验来实现,如某个字段只能为0或1。
举例场景:当显示和隐藏的属性showStatus只能时0或1,需要自定义校验注解和自定义校验器

4.1 导入依赖

<dependency>
     <groupId>javax.validation</groupId>
     <artifactId>validation-api</artifactId>
     <version>2.0.1.Final</version>
</dependency>

4.2 编写自定义校验注解

PS:可以参考JSR-303原本的校验

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)   //运行环境
@Repeatable(List.class)
@Documented
@Constraint(validatedBy = { })  // 指定自定义校验器,可以适配多个校验器,一个注解完成多种校验
public @interface NotNull {
      String message() default "{javax.validation.constraints.NotNull.message}"; // 校验失败的提示信息
      Class<?>[] groups() default { };
      Class<? extends Payload>[] payload() default { };
      int[]  vals() default {};  //标注在属性上的vals={0,1}
        
}

4.3 编写自定义校验注解

//自定义校验器 必须实现ConstraintValidator
//ConstraintValidator中的 泛型1:绑定的泛型注解   泛型2:校验的数据类型
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {

    private Set<Integer> set = new HashSet<>();

    //初始化方法,获取在Entity类的属性上标注的符合条件的Integer的值:{0,1}
    @Override
    public void initialize(ListValue constraintAnnotation) {
        int[] vals = constraintAnnotation.vals();
        for (int val : vals) {
            set.add(val);
        }
    }

    //判断是否校验成功方法
    //integer:传进来的值
    //判断依据:看integer是否在初始化方法的数组中
    @Override
    public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
        if (set.contains(integer)) {
            return true;
        }
        return false;
    }
}

4.4 在需要校验的属性上添加自定义注解

@ListValue(vals={0,1},message = "开关状态只能是0或1")
private Integer showStatus;

4.5 自定义参数校验(这边以校验手机号为例)

  1. 编写注解类
//说明该注解将被包含在javadoc中
@Documented
// 注解的作用目标 类上、方法上、属性上
@Target({ElementType.FIELD, ElementType.PARAMETER})
// 注解的保留位置  
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {IsMobileValidator.class }) // 与约束注解关联的验证器
public @interface IsMobile {

    boolean required() default true;

    String message() default "手机号不正确";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

}
  1. 编写校验规则
public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {
    private boolean required;

    /**
     * 重写initialize方法获取注解实例
     * @param ca
     */
    @Override
    public void initialize(IsMobile ca) {
        // 重注解实例中获信息
        required = ca.required();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // value就是要校验的数据了
        if (value != null && required) {
            // 手机号校验规则
            System.out.println(value);
            String regexp= "^(((\\+\\d{2}-)?0\\d{2,3}-\\d{7,8})|((\\+\\d{2}-)?(\\d{2,3}-)?([1][3,4,5,7,8][0-9]\\d{8})))$";
            boolean matches = Pattern.matches(regexp, value);
            System.out.println(matches);
            return matches;
        }
        return false;
    }
}
  1. 使用自定义校验注解
    /**
     * 手机号
     */
    @IsMobile(message = "用户手机号不正确")
    private String tel;
posted @ 2021-01-15 10:45  &emsp;不将就鸭  阅读(861)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3