美文网首页
优雅的参数校验 JSR303 javax.validation.

优雅的参数校验 JSR303 javax.validation.

作者: 程序员Darker | 来源:发表于2021-10-31 10:06 被阅读0次

    1. controller层直接使用校验注解校验简单参数。比如@Max等等

    类上面打注解 @Validated
    参数前面打注解 @Max

    @Api(value = "分类", tags = "分类模块")
    @RestController
    @RequestMapping("/category")
    @Validated
    public class CategoryController {
    
        // 举例:id限制最大为10
        @ApiOperation(value = "单个查询", notes = "1.0版本")
        @GetMapping("/id/{id}")
        public CategoryVO getById(@PathVariable @Max(10) Long id) {
            return null;
        }
    }
    

    2. controller层要校验请求对象

    要校验的对象前面打注解 @Validated
    对象内部打上需要的注解
    如果对象内还有对象,则在内部对象字段打注解@Valid,然后内部对象类里的属性打上各种校验注解

        @PostMapping("/add")
        public Boolean add(@RequestBody @Validated CategoryDTO categoryDTO) {
            CategoryDO categoryDO = new CategoryDO();
            BeanUtils.copyProperties(categoryDTO, categoryDO);
            return categoryService.save(categoryDO);
        }
    
    /**
     * @author wangningbo
     */
    @Getter
    @Setter
    @ApiModel(value = "CategoryDTO对象", description = "新增或修改对象")
    public class CategoryDTO {
    
        @NotBlank
        @ApiModelProperty("名称")
        private String name;
    
        @URL
        @ApiModelProperty("图片")
        private String img;
    
        @ApiModelProperty("描述")
        private String description;
    
        @ApiModelProperty("父id")
        private Long parentId;
    
        @ApiModelProperty("上线:0->下架;1->上线;")
        private Boolean online;
    
        @ApiModelProperty("排序")
        private Integer sort;
    }
    

    3. @Validated 和 @Valid

    有一定的相似性,他们都是开启或关闭校验的开关

    4. 自定义校验注解

    场景举例:假设用户注册账号,要校验它2次输入的密码是否一致,并且密码的长度要在8~16位

    自定义校验注解

    /**
     * 自定义校验注解
     * true 密码长度要在8~16位,且两次密码相同
     *
     * @author wangningbo
     */
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Constraint(validatedBy = PasswordEqualValidator.class)
    public @interface PasswordEqual {
        int min() default 8;
    
        int max() default 16;
    
        String message() default "passwords are not equal";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    }
    

    自定义注解的验证器

    /**
     * passwordEqual 注解的验证器
     *
     * @author wangningbo
     */
    public class PasswordEqualValidator implements ConstraintValidator<PasswordEqual, Map<String, String>> {
    
        int min;
        int max;
    
        @Override
        public void initialize(PasswordEqual constraintAnnotation) {
            this.min = constraintAnnotation.min();
            this.max = constraintAnnotation.max();
        }
    
        @Override
        public boolean isValid(Map<String, String> map, ConstraintValidatorContext constraintValidatorContext) {
            String password1 = map.get("password1");
            String password2 = map.get("password2");
            if (password1.length() < min || password1.length() > max) {
                return false;
            }
            return password1.equals(password2);
        }
    }
    

    以后在使用的时候,直接把注解打在类上即可

    5. 分组校验

    分组

    /**
     * @author wangningbo
     */
    public interface AddGroup {
    }
    
    /**
     * 空接口,用于 JSR303 分组校验
     *
     * @author wangningbo
     */
    public interface UpdateGroup {
    }
    

    分组校验分为以下3种情况

    5.1 单分组校验

    场景举例:我要指定新增新增的时候校验哪些字段
    解决思路:新增接口入参的时候指定对象使用的分组,对象内的字段也指定分组

    指定对象使用哪个分组进行校验 @Validated({AddGroup.class})

        @PostMapping("/add")
        public Boolean add(@RequestBody @Validated({AddGroup.class}) CategoryDTO categoryDTO) {
        }
    

    注解的groups属性指定在哪个分组的情况下进行校验。没有指定分组的注解在分组校验下不生效。

    /**
     * @author wangningbo
     */
    @Getter
    @Setter
    @ApiModel(value = "CategoryDTO对象", description = "新增或修改对象")
    public class CategoryDTO {
    
        @Null(groups = {AddGroup.class})
        @ApiModelProperty("主键id")
        private Long id;
    
        @NotBlank(groups = {AddGroup.class})
        @ApiModelProperty("名称")
        private String name;
    
        @NotBlank(groups = {AddGroup.class})
        @URL(groups = {AddGroup.class)
        @ApiModelProperty("图片")
        private String img;
    
        @ApiModelProperty("描述")
        private String description;
    
        @ApiModelProperty("父id")
        private Long parentId;
    
        @ApiModelProperty("上线:0->下架;1->上线;")
        private Boolean online;
    
        @ApiModelProperty("排序")
        private Integer sort;
    }
    

    结果:从上面可以看字段id、name、img这三个字段指定了分组校验的AddGroup分组,到进行新增的时候,就会校验这三个字段

    5.2 多分组校验

    场景举例:img字段,新增的时候必须填写,且必须为url。修改的时候可以不填写,但是填写了就必须为url。
    解决思路:新增接口入参的时候指定对象使用的分组,新增和修改都要对img这个字段有一定的限制,根据不同的场景限制不同的分组即可

    指定对象使用哪个分组进行校验 @Validated({UpdateGroup.class})

        @PostMapping("/update")
        public Boolean update(@RequestBody @Validated({UpdateGroup.class}) CategoryDTO categoryDTO) {
        }
    

    注解的groups属性指定在哪个分组的情况下进行校验。没有指定分组的注解在分组校验下不生效。

    @Getter
    @Setter
    @ApiModel(value = "CategoryDTO对象", description = "新增或修改对象")
    public class CategoryDTO {
    
        @NotNull(groups = {UpdateGroup.class})
        @Null(groups = {AddGroup.class})
        @ApiModelProperty("主键id")
        private Long id;
    
        @NotBlank(groups = {AddGroup.class})
        @ApiModelProperty("名称")
        private String name;
    
        @NotBlank(groups = {AddGroup.class})
        @URL(groups = {AddGroup.class, UpdateGroup.class})
        @ApiModelProperty("图片")
        private String img;
    
        @ApiModelProperty("描述")
        private String description;
    
        @ApiModelProperty("父id")
        private Long parentId;
    
        @ApiModelProperty("上线:0->下架;1->上线;")
        private Boolean online;
    
        @ApiModelProperty("排序")
        private Integer sort;
    }
    

    结果:从上面可以看出字段id和字段img上有的注解指定了update分组。在修改的时候,id必传递,img可以不传,但是如果传了就必须为url格式。否则就会报参数异常错

    5.3 不指定分组校验

    场景举例:@Validated 注解在controller没有指定校验对象的分组

        @PostMapping("/add")
        public Boolean add(@RequestBody @Validated CategoryDTO categoryDTO) {
        }
    
    /**
     * @author wangningbo
     */
    @Getter
    @Setter
    @ApiModel(value = "CategoryDTO对象", description = "新增或修改对象")
    public class CategoryDTO {
    
        @NotNull(groups = {UpdateGroup.class})
        @Null(groups = {AddGroup.class})
        @ApiModelProperty("主键id")
        private Long id;
    
        @NotBlank(groups = {AddGroup.class})
        @ApiModelProperty("名称")
        private String name;
    
        @NotBlank(groups = {AddGroup.class})
        @URL(groups = {AddGroup.class, UpdateGroup.class})
        @ApiModelProperty("图片")
        private String img;
    
        @ApiModelProperty("描述")
        private String description;
    
        @ApiModelProperty("父id")
        private Long parentId;
    
        @ApiModelProperty("上线:0->下架;1->上线;")
        private Boolean online;
    
        @NotNull
        @ApiModelProperty("排序")
        private Integer sort;
    }
    

    结果:可以看到controller层的@Validated没有指定分组,所以校验的对象的属性上的校验注解没有指定分组的才会生效,所以只会校验sort字段。

    6. 常用校验注解

    @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(regex=,flag=) 被注释的元素必须符合指定的正则表达式
    Hibernate Validator 附加的 constraint
    @NotBlank(message =) 验证字符串非null,且长度必须大于0
    @Email 被注释的元素必须是电子邮箱地址
    @Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
    @NotEmpty 被注释的字符串的必须非空
    @Range(min=,max=,message=) 被注释的元素必须在合适的范围内

    相关文章

      网友评论

          本文标题:优雅的参数校验 JSR303 javax.validation.

          本文链接:https://www.haomeiwen.com/subject/bbrnaltx.html