zoukankan      html  css  js  c++  java
  • 优雅的参数校验

    添加依赖

    如果使用的是Springboot就不需要手动添加依赖了。Springboot已经依赖了。

    <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
    </dependency>
    

    参数检验的使用

    注解名 含义
    AssertFalse 带注释的元素必须为false
    AssertTrue 带注释的元素必须为true
    DecimalMax 带注释的元素必须是一个数字,其值必须小于或等于指定的最大值
    DecimalMin 带注释的元素必须是一个数字,其值必须大于或等于指定的最小值
    Digits 字段必须为数值,且正数部分不能超过 i 位,小数部分不能超过 j 位,null 元素被视为有效。
    Email 所注解的元素需满足Email格式
    Future 带注释的元素必须是将来的日期
    FutureOrPresent 字段必须为未来的时间或当前的时间
    Past 所注解的元素必须是某个过去的日期
    PastOrPresent 字段必须为过去的时间或当前的时间从
    Max 带注释的元素必须是一个数字,其值必须小于或等于指定的最大值
    Min 带注释的元素必须是一个数字,其值必须大于或等于指定的最小值
    NotBlank 所注解的元素值有内容
    NotEmpty 字段不能为 null 且不能为空,可以作用于字符串,其长度不能为 0,可作用于 Array、Collection、Map,其大小不能为 0
    NotNull 所注解的元素值不能为null
    Null 所注解的元素值为null
    Pattern 所注解的元素必须满足给定的正则表达式
    Positive 字段必须为正数,即数值大于 0
    PositiveOrZero 字段必须为正数或 0,即数值大于等于 0
    Negative 字段必须为负数,即数值小于 0
    NegativeOrZero 字段必须为负数或 0,即数值小于等于 0
    Size 所注解的元素必须是String、集合或数组,且长度大小需保证在给定范围之内
    • 实体类增加属性注解
        @Size(max= 64, message = "长度不能超过64")
        private String orderNo;
        @NotBlank(message = "类型不能为空")
        private String orderType;
        @Past(message = "创建时间在当前时间之前才可成功")
        private Date createDate;
        @Future(message = "更新时间在当前时间之后才可成功")
        private Date updateDate;
        @Email
        private String email;
        @Pattern(regexp = "^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$",message = "身份证格式不正确")
        private String idNum;
    
    • 方法使用实体类入参的时候增加校验注解
        public Rs save(@RequestBody @Validated MiTestEntity miTest) {
            return Rs.ok();
        }
        public Rs update(@RequestBody @Valid MiTestEntity miTest) {
            return Rs.ok();
        }
    
    • 使用全局异常拦截错误的字段校验
    @RestControllerAdvice
    public class BindExceptionHandler {
        @ExceptionHandler(Exception.class)
        public Rs handleException(Exception e) {
            Rs r = new Rs();
            r.put("code" , 500);
            r.put("msg" , e.getMessage());
            return r;
        }
        /**
         * 方法参数校验
         */
        @ExceptionHandler(MethodArgumentNotValidException.class)
        public Rs handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
            return Rs.error(e.getBindingResult().getFieldError().getDefaultMessage());
        }
    }
    

    @NotNull, @NotEmpty和@NotBlank之间的区别是什么?

    • @NotEmpty

      • 不能是null
      • 不能是空字符
      • 集合框架中的元素不能为空
    • @NotNull

      • 被修饰元素不能为null,可用在基本类型上
    • @NotBlank

      • String不能是null,且去除两端空白字符后的长度0
    • @NotEmpty或者@NotBlank不可以用在基本数据类型上,会报错

      • rg.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.NotBlank' validating type 'java.lang.Long'. Check configuration for 'offset'

    @validated和@valid的区别

    • @Valid:标准JSR-303规范的标记型注解,用来标记验证属性和方法返回值,进行级联和递归校验。
    • @Validated:Spring的注解,是标准JSR-303的一个变种(补充),提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制
    • 在Controller中校验方法参数时,使用@Valid和@Validated并无特殊差异,但是@Validated不可以嵌套校验,
    public class A{
        @NotNull(message = "id不能为空")
        @Min(value = 1, message = "id必须为正整数")
        private Long id;
        @NotNull(message = "B不能为空")
        @Size(min = 1, message = "至少要有一个属性")
        private List<B> bList;
    }
    public class B{
        @NotNull(message = "id不能为空")
        @Min(value = 1, message = "id必须为正整数")
        private Long id;
        @NotBlank(message = "name不能为空")
        private String name;
    }
    
    //这样的情况下如果使用@Validated校验A的参数,那么bList泛型里面的属性则不会校验成功
    //需要在private List<B> bList;上面加上@Valid来支持嵌套验证
    
    • @Validated注解可以用于类级别,用于支持Spring进行方法级别的参数校验。@Valid可以用在属性级别约束,用来表示级联校验
    • @Validated只能用在类、方法和参数上,而@Valid可用于方法、字段、构造器和参数上

    自定义注解验证

    某些业务场景下,上面的注解未必可以提供很好的校验,validation也为我们提供了自定义注解参数校验。

    • 注解
    @Documented
    @Target({ElementType.METHOD, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = {StringLengthValidImpl.class})
    public @interface StringLength {
        /**
         * 在注解没有显示申明,则min值默认是 0
         */
        int min() default 0;
        /**
         * 在注解没有显示申明,则max值默认是18
         *
         * @return
         */
        int max() default 18;
        /**
         * 错误信息
         */
        String message() default "";
    
        /**
         * 分组
         */
        Class<?>[] groups() default {};
    
         //TODO 暂时不知道是干嘛的 看接口的注释是约束注解的负载(可用来保存一些数据)
        Class<? extends Payload>[] payload() default {};
    
        /**
         * 定义验证集合,可以为属性配置多套的验证规则
         */
        @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @interface List {
            StringLength[] value();
        }
    }
    
    • 校验实现类
    //<注解类,注解作用的参数类型(泛型)>
    public class StringLengthValidImpl implements ConstraintValidator<StringLength, String> {
    
        private int min;
        private int max;
        private String message;
        /**
         * @Description: 初始化参数
         * @param: integerValid
         */
        @Override
        public void initialize(StringLength integerValid) {
            max = integerValid.max();
            min = integerValid.min();
            message = integerValid.message();
        }
        
        /**
         * @param: value 参数
         * @param: constraintValidatorContext 在应用给定的约束验证器时提供上下文数据和操作
         * @return: boolean
         */
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            //禁止默认消息返回
            context.disableDefaultConstraintViolation();
            if (null == value) {
                context.buildConstraintViolationWithTemplate(message + ":参数值为空").addConstraintViolation();
                return false;
            } else if (value.length() > max) {
                context.buildConstraintViolationWithTemplate(message + ":不能超过" + max).addConstraintViolation();
                return false;
            } else if (value.length() <= min) {
                context.buildConstraintViolationWithTemplate(message + ":不能小于" + min).addConstraintViolation();
                return false;
            }
            return true;
        }
    }
    
    • 分组校验
    //用于新增校验
    public interface save {
    }
    //用于修改校验
    public interface update {
    }
    
    • 参数校验
    @Data
    public class TestEntity {
        @StringLength.List({
                @StringLength(groups = {save.class}, message = "name",max = 10),
                @StringLength(groups = {update.class}, message = "name",max = 5)
        })
        private String name;
    }
    //不可以超过10个字符
        @PostMapping("add")
        public String add(@RequestBody @Validated({save.class}) TestEntity testEntity) {
            return JSON.toJSONString(testEntity);
        }
        //不可以超过5个字符
        @PostMapping("update")
        public String update(@RequestBody @Validated({update.class}) TestEntity testEntity) {
            return JSON.toJSONString(testEntity);
        }
        //没有限制
        @PostMapping("save")
        public String save(@RequestBody @Validated TestEntity testEntity) {
            return JSON.toJSONString(testEntity);
        }
    
  • 相关阅读:
    POJ-3254 + POJ-1185 状压DP入门题
    POJ-3667 线段树区间合并入门题
    HDU-4507 数位DP 记录一个毒瘤错误orz
    HDU-4734 F(x)数位dp
    HDU-3709 Balanced Number 数位dp+枚举
    分块入门 LibreOJ分块九题
    HDU-4389 X mod f(x) && 2018上海大都会邀请赛J 数位dp
    HDU-3038 How Many Answers Are Wrong (带权并查集)
    Codeforces 608B Hamming Distance Sum (前缀和)
    (二十六 )数据库:水平切分,数据库秒级扩容!
  • 原文地址:https://www.cnblogs.com/yangk1996/p/12731456.html
Copyright © 2011-2022 走看看