• Spring Boot (31) 数据验证


    曾经参数的验证是这样的:

        public String test(User user){
            if(user == null){
                throw new NullPointerException("user 不能为空");
            }
    
            if(user.getUserName() == null){
                throw new NullPointerException("userName 不能为空");
            }
    
            if(user.getUserName().length() < 4 || user.getUserName().length()>10){
                throw new RuntimeException("userName 长度小于4位或大于10位");
            }
    
            return "success";
        }

    随着参数的增加,格式的变化,校验数据有效性的代码愈发繁琐。

    通过Spring boot来完成参数数据校验。

    JSR-303注解介绍

    这里只列举了javax.validation包下的注解,同理在spring-boot-starter-web包种也存在hibernate-validator验证包,里面包含了一些javax.validation没有的注解。

    注解说明
    @NotNull 限制必须不为null
    @NotEmpty 验证注解的元素值不为 null 且不为空(字符串长度不为0、集合大小不为0)
    @NotBlank 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
    @Pattern(value) 限制必须符合指定的正则表达式
    @Size(max,min) 限制字符长度必须在 min 到 max 之间(也可以用在集合上)
    @Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
    @Max(value) 限制必须为一个不大于指定值的数字
    @Min(value) 限制必须为一个不小于指定值的数字
    @DecimalMax(value) 限制必须为一个不大于指定值的数字
    @DecimalMin(value) 限制必须为一个不小于指定值的数字
    @Null 限制只能为null(很少用)
    @AssertFalse 限制必须为false (很少用)
    @AssertTrue 限制必须为true (很少用)
    @Past 限制必须是一个过去的日期
    @Future 限制必须是一个将来的日期
    @Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过 integer,小数部分的位数不能超过 fraction (很少用)

    实体类

    public class Book implements Serializable {
    
        private Integer id;
    
        @NotBlank(message = "name 不能为空")
        @Length(min = 2, max = 10, message = "name 长度必须在{min}-{max}之间")
        private String name;
    
        @NotNull(message = "price 不能为空")
        @DecimalMin(value = "0.1", message = "价格不能低于 {value}")
        private BigDecimal price;
    
        ...
    }

    控制层

    这些验证注解不仅仅可以放在controller上,也可以加在service层上。

    //todo 开启数据有效性校验,添加在类上即为验证方法,添加在方法参数中即为验证参数对象。(添加在方法上无效)
    @Validated
    @RequestMapping("/books")
    @RestController
    public class BookController { @GetMapping("/test1") public String test1(@NotBlank(message = "name不能为空") @Length(min = 2, max = 10 , message="name 长度必须在{min}-{max}之间") String name){ return "test1"; } @GetMapping("test2") public String test2(@Validated Book book){ return "test2"; } }

    这样就可以了,在浏览器中输入 /books/test1/ 不输入参数会抛异常 /books/test1?name=sdf 则可以通过验证

    /books/test2/ 异常 /books/test2?name=sdf&price=0.5就ok了

    Spring Boot 还允许我们自定义Validatior

    自定义注解

    package com.spring.boot.utils;
    
    
    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    //@Target定义范围 在字段 参数上使用
    @Target({ElementType.FIELD, ElementType.PARAMETER})
    //定义可见范围 RUNTIME整个运行阶段都可见
    @Retention(RetentionPolicy.RUNTIME)
    //Constraint指定具体校验器类
    @Constraint(validatedBy = DateTimeValidator.class)
    //@interface 定义注解
    public @interface DateTime {
        String message() default "格式错误";
    
        String format() default "yyyy-MM-dd";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    }

    具体的校验器

    package com.spring.boot.utils;
    
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    
    /**
     * 日期格式验证
     */
    public class DateTimeValidator implements ConstraintValidator<DateTime, String> {
    
        private DateTime dateTime;
    
        @Override
        //初始化,它可以获得当前注解的所有属性
        public void initialize(DateTime constraintAnnotation) {
            this.dateTime = constraintAnnotation;
        }
    
        @Override
        //约束验证的主体方法,其中s就是验证参数的具体事例,context代表约束执行的上下文
        public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
            //如果为空不用验证, 为空验证可以用@NotBlank、@NotNull等参数进行控制
            if (s == null) {
                return true;
            }
    
            String format = dateTime.format();
            if (s.length() != format.length()) {
                return false;
            }
    
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
            try {
                simpleDateFormat.parse(s);
            } catch (ParseException e) {
                return false;
            }
            return true;
        }
    }

    然后在控制层使用他

        @GetMapping("/test3")
        public String test3(@NotBlank(message = "date 不能为空") @DateTime(message = "您如的格式错误,正确的格式为:{format}",format = "yyyy-MM-dd HH:mm") String date){
            return "test3";
        }

    测试

    分组验证

      有时候我们需要对一个实体类有多种验证方式,在不同的情况下使用不同的验证方式,比如id,新增的时候是不需要的,更新时是必须的。

    定义一个验证组,里面写上不同的空接口类即可

    public class Groups {
        public interface Update{
    
        }
    
        public interface Default{
            
        }
    }

    实体类

      groups属性的作用就让@Validated注解只验证与自身value属性想匹配的字段,可多个,只要满足就会去纳入验证范围。

    public class Book {
    
        @NotNull(message = "id 不能为空" , groups = Groups.Update.class)
        private Integer id;
    
        @NotBlank(message = "name 不能为空" , groups = Groups.Default.class)
        private String name;
    
        @NotNull(message = "price 不能为空" , groups = Groups.Default.class)
        private BigDecimal price;

    控制层

      创建一个ValidateControoler类,然后定义好insert、update两个方法,由于insert方法并不关心id字段,所以这里的@Validate的value属性写成Groups.Default.class就可以了,而update方法需要Id,所以此处@Validated注解的value属性值就要写成Groups.Default.class,Groups.Update.Class。代表只要是这分组下的数据都需要进行数据有效性校验操作

    @RestController
    public class ValidateController {
        //default中没有id
        @GetMapping("/insert")
        public String insert(@Validated(value=Groups.Default.class) Book book){
            return "insert";
        }
    
        //update中有id校验
        @GetMapping("update")
        public String update(@Validated(value={Groups.Default.class,Groups.Update.class}) Book book){
            return "update";
        }
    
    }

  • 相关阅读:
    在R语言中轻松创建关联网络
    在R语言中显示美丽的数据摘要summary统计信息
    R语言中不同类型的聚类方法比较
    R语言中的划分聚类模型
    R语言解释生存分析中危险率和风险率的变化
    Stata估算观测数据的风险比
    Stata 中Mata的st_view函数
    R语言多臂试验
    R语言使用倾向评分提高RCT(随机对照试验)的效率
    R语言在RCT中调整基线时对错误指定的稳健性
  • 原文地址:https://www.cnblogs.com/baidawei/p/9187453.html
Copyright © 2020-2023  润新知