JSR303请求数据校验练习

1、什么时JSR303?

JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。

JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint(约束) 的实现,除此之外还有一些附加的 constraint。

官网:Jakarta Bean Validation 3.0

2、引入依赖

JSR303可以单独使用,也可以跟Spring boot整合使用

1)JSR单独使用依赖包:单独引入jdk提供的 validation

<!--引入数据校验依赖 -->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>

2)JSR整合spring boot 依赖包(常用):

此时要注意springboot与validation 的版本号,若当前

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

注意:其实 spring-boot-starter-validation 也是为了引入下面2个依赖,主要是 hibernate-validator
  <dependency>
      <groupId>jakarta.validation</groupId>
      <artifactId>jakarta.validation-api</artifactId>
      <version>2.0.2</version>
      <scope>compile</scope>
  </dependency>
  <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.0.18.Final</version>
      <scope>compile</scope>
      <exclusions>
        <exclusion>
          <artifactId>validation-api</artifactId>
          <groupId>javax.validation</groupId>
        </exclusion>
      </exclusions>
    </dependency>

3、常用注解

     1)JSR提供的注解在包javax.validation.constraints包下边,如下图所示:

2)常用注解说明:

注解说明
@Null用于验证对象为null
@NotNull用于验证对象不为null
@NotBlank用于验证字符串不为空(只用于字符串)
@NotEmpty用于校验集合容器,String字符串不能为null,且size>0(带空格的字符串校验不出来)
@AssertTrue用于验证被标注的字段为true
@AssertFalse用于验证被标注的字段为false
@Min(value)用于验证被标注的属性值最小是value
@Max(value)用于验证被标注的属性值最大是value
@DecimalMin(value)被标注的元素必须是一个数字(包含CharSequence),其数值必须大于等于指定的value
@DecimalMax(value)被标注的元素必须是一个数字(包含CharSequence),其数值必须小于等于指定的value
@Digits(integer, fraction)被标注的元素必须是一个数字,其值必须在可接受的范围内
@Size(min,max)被标注的元素是一个容器对象或String,判断容器对象(Array、Collection、map)和 字符串String 的长度是否在指定范围之内
@Length(min,max)判断字符串String 的长度是否在指定范围之内
@Pattern(regexp)判断被标注的元素值是否符合正则表达式regexp
@Email用于判断被标注的字段是否符合邮箱格式
@Past被注释的元素必须是一个过去的日期
@Future被注释的元素必须是一个将来的日期
@Valid标记注解,表示开启JSR303校验
@Validated

标记注解,表示开启JSR303校验

4、注解@Valid 和 @Validated 的区别

     @Valid 和 @Validated 都是标记注解,目的都是开启 validation 校验

      4.1)@Valid

            由JDK提供的,是标准的JSR303标准

           不能分组校验(@Valid 是无参数的)

            可以作用在方法、构造函数、方法参数和属性字段上

            可以独自完成级联校验

       4.2)@Validated

             由spring提供

              可以作用在类型、方法和方法参数上,不能作用在属性字段上

              由于无法作用在属性上,所以无法独自完成级联校验(需要跟@Valid 组合完成级联校验)

              支持分组校验

4.3)级联校验(嵌套校验)测试

@Data
public class Person {

    @NotNull(message = "名称不能为空")
    private String name;
    @NotBlank(message = "手机号不能为空")
    private String phone;
    @Min(value = 1,message = "年龄一定大于0")
    private int age;
    private String email;
    @Valid    //开启嵌套校验
    //@NotNull(message = "son不能为空") //只有触发当前字段校验时,才会进行嵌套校验(即校验Son的属性),但测试后不需要触发也可以校验Son 中的属性
    private List<Son> sons;
}

@Data
public class Son implements Serializable {

    @NotBlank(message = "son名称不能为空")
}

//controller类:
@RestController
@RequestMapping("/product/voa")
public class VolationTestController {

    //BindingResult绑定了数据 category 的校验结果
    @RequestMapping("/test")
    public R test(@Validated @RequestBody Person person, BindingResult result){
        System.out.println(person.toString());

        if(result.hasErrors()){
            Map<String,String> errMap = new LinkedHashMap<>();
            result.getAllErrors().forEach( err -> {
                String key = err.getObjectName();
                String value = err.getDefaultMessage();
                errMap.put(key,value);
            });
            return R.error().put("error",errMap);
        }else {
            return R.ok();
        }
    }
}

//R是统一应答类
public class R extends HashMap<String,Object> implements Serializable {

    private static final long serialVersionUID = 1L;

    public R() {
        put("code", 0);
        put("msg", "success");
    }

    public static R error() {
        return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");
    }

    public static R error(String msg) {
        return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
    }

    public static R error(int code, String msg) {
        R r = new R();
        r.put("code", code);
        r.put("msg", msg);
        return r;
    }

    public static R ok(String msg) {
        R r = new R();
        r.put("msg", msg);
        return r;
    }

    public static R ok(Map<String, Object> map) {
        R r = new R();
        r.putAll(map);
        return r;
    }

    public static R ok() {
        return new R();
    }

    public R put(String key, Object value) {
        super.put(key, value);
        return this;
    }
}

总结:

     级联校验时,在controller 方法中加上 @Valid/@Validated 开启对指定对象的validation校验,
     然后在需要进行级联校验的属性上加上注解@Valid开启级联校验,
    注意:
           一般进行级联校验的属性上(即@Valid标注的属性)需要触发validation校验时

        (即该字段  需要标注validation注解)


          才会触发级联校验;但经过测试发现 @Valid标注的属性不加validation注解也可

          以触发级联校验,如下边代码:

@Valid    //开启嵌套校验
//@NotNull(message = "son不能为空") //只有触发当前字段校验时,才会进行嵌套校验(即校验Son的属性),但测试后不需要触发也可以校验Son 中的属性
private List<Son> sons;

   4.4)分组校验

            分组校验时,在controller方法中只能用@Valicated 开启voledation 校验

//1、首先创建2个接口用来标记分组
//标记分组
public interface First {
}
//标记分组
public interface Second {
}

//2、在volidation 注解中的group 属性指定分组
@Data
public class Person {

    @NotBlank(message = "First 名称不能为空",groups = {First.class})
    @NotBlank(message = "Second 名称不能空",groups = {Second.class})
    private String name;
}

//3、在controller 方法中使用@Validated 开启voldation校验,并在@Validated中指定当前进行校验的分组
@RestController
@RequestMapping("/product/voa")
public class VolationTestController {

    @RequestMapping("/test")
    public R test(@Validated(value = Second.class) @RequestBody Person person, BindingResult result){
        System.out.println(person.toString());

        if(result.hasErrors()){
            Map<String,String> errMap = new LinkedHashMap<>();
            result.getAllErrors().forEach( err -> {
                String key = err.getObjectName();
                String value = err.getDefaultMessage();
                errMap.put(key,value);
            });
            return R.error().put("error",errMap);
        }else {
            return R.ok();
        }
    }
}

       注意:

              在分组校验清空下(即在@Validated中指定了分组),若在校验对象中的字段上

              的注解没有指定分组,则不触发校验,

                 如:在 Person 中,若@NotBlank(message = "First 名称不能为空")  不指定分组则

                        不会触发校验

5、自定义voldation 校验注解

5.1)ConstraintValidator 接口

     volidation 注解的处理器需要实现接口 ConstraintValidator,ConstraintValidator结构如下:

public interface ConstraintValidator<A extends Annotation, T> {
    default void initialize(A constraintAnnotation) {
    }

    boolean isValid(T var1, ConstraintValidatorContext var2);
}

     实现ConstraintValidator 接口时需要指定2个泛型:

     A:是一个注解,即我们自定义的注解

     T:我们处理的类型(即我们自定义注解需要处里的类型)

5.2)自定义volidation 注解代码如下:

(1)定义一个注解@StringVal,用来判断字符串是否由自定字符组成

          @StringVal 除了自定义注解的通用的@Documented、@Retention、@Target这三个注解

         之外,还多了一个注解@Constraint,该注解是用来指定@StringVal 的处理器类。

@Documented
@Retention(RetentionPolicy.RUNTIME) //运行时执行
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.CONSTRUCTOR,ElementType.PARAMETER,ElementType.ANNOTATION_TYPE,ElementType.TYPE_USE})
@Constraint(validatedBy = {StringValValidator.class})
public @interface StringVal {

    String validVal();
    //下边是 valodation 校验注解默认的三属性(值)
    String message() default "volidation test message";
    Class<?>[] group() default {};
    Class<? extends Payload>[] payload() default {};
}

注意:

      @Constraint 只能用于volidation,因为@Constraint 的值只能是 ConstraintValidator 的子类;

      @Constraint 结构如下:

@Documented
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraint {
    Class<? extends ConstraintValidator<?, ?>>[] validatedBy();
}

(2)定义@StringVal 的处理器类 StringValValidator

public class StringValValidator implements ConstraintValidator<StringVal,String> {
    //
    private String vaidVal;

    public StringValValidator(){}

    @Override
    public void initialize(StringVal ann) {
        vaidVal = ann.validVal();
    }

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        if(s == null || "".equals(s.trim())){
            return false;
        }
        for(int i=0;i<s.length();i++){
            String c = String.valueOf(s.charAt(i));
            if(vaidVal.indexOf(c) < 0){
                return false;
            }
        }
        return true;
    }
}

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值