003-自定义注解实现数据校验

🧑‍🎓 个人主页Silence Lamb
📖 本章内容:【自定义注解实现数据校验


一、JSR303介绍

  • 在Java中提供了一系列的校验方式
  • 这些校验方式在javax.validation.constraints包中

1.1【引入依赖】

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

1.2【常用注解】

@Null 验证对象是否为null 
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串 
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格. 
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.
Booelan检查 
@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: 指定 Pattern.Flag 的数组,表示正则表达式的相关选项。
数值检查 
建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为”“,Integer为null 
@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=)

1.3【开启校验】

  • 🌳controller中加校验注解@Valid,开启校验
public R save(@Valid @RequestBody Student student, BindingResult result){}

二、数据校验测试

  • 步骤1:实体类字段上使用校验注解 @NotNull @NotEmpty @NotBlank @Pattern
  • 步骤2:controller中加校验注解@Valid,开启校验
  • 步骤3:给校验的Bean后,紧跟一个BindingResult,就可以获取到校验的结果

2.1【实现数据校验】

实现数据校验功能

  • 实体中添加注解

    @Data
    public class Student {
    
        @NotEmpty(message = "姓名不能为空")
        @ApiModelProperty("姓名")
        private String name;
        
        @ApiModelProperty("绰号")
        private  String nickName;
        
        @ApiModelProperty("学号")
        private String id;
    
        @ApiModelProperty("年龄")
        private String age;
        
        @ApiModelProperty("年龄上限")
        private  String retireAge;
    }
    
  • controller层中保存方法添加:@Valid

       /**
         * 数据校验异常
         *
         * @param student 学生信息
         */
        @PostMapping("/jsr")
        public AjaxResult testJrs(@Valid @RequestBody Student student, BindingResult result) {
            String name = student.getName();
            HashMap<String, Object> map = new HashMap<>();
            map.put("name", name);
            map.put("errors", result.getFieldErrors());
            return AjaxResult.success("数据校验", map);
        }
    
  • 📢🌥️ 数据校验测试:测试:http://localhost:8080/test/jsr

    {
        "name": "",
        "String": "1",
        "age":"18"
    }
    
  • 📢🌥️ 返回信息

    {
        "msg": "数据校验",
        "code": 200,
        "data": {
            "name": "",
            "errors": [
                {
                    "codes": [
                        "NotEmpty.student.name",
                        "NotEmpty.name",
                        "NotEmpty.java.lang.String",
                        "NotEmpty"
                    ],
                    "arguments": [
                        {
                            "codes": [
                                "student.name",
                                "name"
                            ],
                            "arguments": null,
                            "defaultMessage": "name",
                            "code": "name"
                        }
                    ],
                    "defaultMessage": "姓名不能为空",
                    "objectName": "student",
                    "field": "name",
                    "rejectedValue": "",
                    "bindingFailure": false,
                    "code": "NotEmpty"
                }
            ]
        }
    }
    

2.2【封装错误信息】

  • 封装错误信息

        /**
         * 封装异常信息
         *
         * @param student 学生信息
         */
        @PostMapping("/encapsulation")
        public AjaxResult testEncapsulation(@Valid @RequestBody Student student, BindingResult result) {
            String name = student.getName();
            HashMap<String, Object> map = new HashMap<>();
            map.put("name", name);
            if (result.hasErrors()) {
                //1.获取错误的校验结果
                result.getFieldErrors().forEach((fieldError) -> {
                    //2.获取发生错误的字段
                    String field = fieldError.getField();
                    //3.获取发生错误时的message
                    String message = fieldError.getDefaultMessage();
                    map.put(field, message);
                });
                return AjaxResult.error("数据校验", map);
            } else {
                return AjaxResult.success(map);
            }
        }
    
  • 📢🌥️ 自定义的封装错误信息:http://localhost:80/test/jrs

{
    "name": "",
    "String": "1",
    "age":"18"
}
  • 📢🌥️ 错误信息
{
    "msg": "数据校验",
    "code": 500,
    "data": {
        "name": "姓名不能为空"
    }
}

2.3【统一异常处理】

  • 统一异常处理

    /**
     * @author Silence Lamb
     * @apiNote 统一异常处理
     */
    @Slf4j
    @RestControllerAdvice(basePackages = "com.silencelamb.jsr.controller")
    public class ControllerAdvice {
    
        /**
         * 数据检验异常
         */
        @ExceptionHandler(value = MethodArgumentNotValidException.class)
        public AjaxResult handlerException(MethodArgumentNotValidException e) {
            log.info("数据校验异常:{},异常类型:{}", e.getMessage(), e.getCause());
            BindingResult bindingResult = e.getBindingResult();
            Map<String, String> errorMap = new HashMap();
            bindingResult.getFieldErrors().forEach((fieldError -> {
                errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
            }));
            return AjaxResult.error("数据校验异常", errorMap);
        }
    
        /**
         * 处理其他异常
         */
        @ExceptionHandler(value = Throwable.class)
        public AjaxResult handleException(Throwable throwable) {
            return AjaxResult.error("throwable",throwable);
        }
    }
    
        /**
         * 统一异常处理
         *
         * @param student 学生信息
         */
        @RequestMapping("/exception")
        public AjaxResult testException(@Valid @RequestBody Student student) {
            String name = student.getName();
            Map<String, String> map = new HashMap<>();
            map.put("name", name);
            return AjaxResult.success(map);
        }   
    
  • 📢🌥️ 测试统一异常处理:http://localhost:8080/exception

    {
        "name": "",
        "String": "1",
        "age":"18"
    }
    
  • 📢🌥️ 错误信息

    {
    	"code": 500,
    	"msg": "数据校验出现问题",
    	"data": {
    		"name": "姓名不能为空"
    	}
    }
    

三、分组校验

3.1【分组校验接口】

  • 创建分组校验接口

    /**
     * @author Silence Lamb
     * @apiNote 添加时校验
     */
    public interface AddGroup {
    }
    
    /**
     * @author Silence Lamb
     * @apiNote 修改时校验
     */
    public interface UpdateGroup {
    }
    

3.2【添加校验注解】

  • 添加校验注解

    @Data
    public class Student {
    
        @ApiModelProperty("姓名")
        @Null(message = "姓名不能为空", groups = {AddGroup.class, UpdateGroup.class})
        private String name;
    
        @ApiModelProperty("绰号")
        @NotEmpty(message = "绰号不能为空", groups = {AddGroup.class, UpdateGroup.class})
        private String nickName;
    
        @ApiModelProperty("学号")
        private String id;
    
        @ApiModelProperty("年龄")
        @Min(value = 18, message = "年龄下限不能低于18岁", groups = UpdateGroup.class)
        private String age;
    
        @ApiModelProperty("年龄上限")
        @Max(value = 60, message = "年龄上限不能超过60岁", groups = UpdateGroup.class)
        private String retireAge;
    }
    

3.3【开启分组校验】

  • @Validated(AddGroup.class)指定校验分组:添加时只校验 name nickName

        /**
         * 分组校验
         *
         * @param student 学生信息
         * @return
         */
        @PostMapping("/addGroup")
        public AjaxResult testAddGroup(@Valid @Validated(AddGroup.class) @RequestBody Student student) {
            String name = student.getName();
            String nickName = student.getNickName();
            String age = student.getAge();
            String retireAge = student.getRetireAge();
            Map<String, String> map = new HashMap<>();
            map.put("name", name);
            map.put("nickname", nickName);
            map.put("age", age);
            map.put("retireAge", retireAge);
            return AjaxResult.success(map);
        }
    
  • 📢🌥️ 测试分组校验:http://localhost:8080/addGroup

    {
        "name":"",
        "nickName":"",
        "age":"",
        "retireAge":""
    }		
    
  • 📢🌥️ 错误信息

    {
        "msg": "数据校验出现问题",
        "code": 500,
        "data": {
            "nickName": "绰号不能为空",
            "name": "姓名不能为空"
        }
    }
    
  • @Validated(UpdateGroup.class)指定校验分组 更新时校验

    	@PostMapping("/updateGroup")
        public Result testUpdateGroup(@Validated(UpdateGroup.class) @RequestBody Student student) {
            String name = student.getName();
            String nickName = student.getNickName();
            String age = student.getAge();
            String retireAge = student.getRetireAge();
            Map<String, String> map = new HashMap<>();
            map.put("name", name);
            map.put("nickname", nickName);
            map.put("age", age);
            map.put("retireAge", retireAge);
            return Result.ok(map);
        }
    
  • 📢🌥️ 测试分组校验:http://localhost:8080/testUpdateGroup

    {
        "name":"",
        "nickName":"",
        "age":"",
        "retireAge":""
    }
    
  • 📢🌥️ 错误信息

    {
        "msg": "数据校验出现问题",
        "code": 500,
        "data": {
            "retireAge": "年龄上限不能超过60岁",
            "nickName": "绰号不能为空",
            "name": "姓名不能为空",
            "age": "年龄下限不能低于18岁"
        }
    }
    

四、自定义校验

4.1【编写校验注解】

  • 比如要创建一个:@ListValue 注解,被标注的字段值只能是:0或1

    /**
     * @author Silence Lamb
     * @apiNote 被标注的字段值只能是:0或1
     */
    @Documented
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    public @interface ListValue {
        String message() default "{com.silencelamb.annotation.valid.ListValue.message}";
    
        Class<?>[] groups() default { };
    
        Class<? extends Payload>[] payload() default { };
    
        int[] value() default {};
    }
    
  • 设置错误信息:创建文件ValidationMessages.properties

    com.silencelamb.annotation.valid.ListValue.message=必须提交指定的值 [0,1]
    

4.2【编写注解约束器】

  • 编写注解约束器

    /**
     * @author Silence Lamb
     * @apiNote 注解约束器
     */
    public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {
    
        private Set<Integer> set = new HashSet<>();
        @Override
        public void initialize(ListValue constraintAnnotation) {
            //获取注解允许的值
            int[] value = constraintAnnotation.value();
            for (int i : value) {
                set.add(i);
            }
        }
    
        @Override
        public boolean isValid(Integer value, ConstraintValidatorContext context) {
            //判断传入的值是否在满足允许的值
            boolean b = set.contains(value);
            return b;
        }
    }
    

2.3【约束器和校验注解关联】

  • 在@ListValue注解关联校验器

    @Constraint(validatedBy = { ListValueConstraintValidator.class })
    一个校验注解可以匹配多个校验器
    

2.4【测试自定义校验注解】

  • 实体类属性上添加注解

      @ListValue(value = {0, 1})
      private Integer gender;
    
  • 测试自定义注解

        /**
         * 自定义注解
         * @param student 学生信息
         */
        @RequestMapping("/testCustomAnnotations")
        public AjaxResult testCustomAnnotations(@Valid @RequestBody Student student) {
            Map<String, Integer> map = new HashMap<>();
            map.put("gender", student.getGender());
            return AjaxResult.success(map);
        }
    

    📢🌥️ 测试自定义校验器http://localhost:8080/testCustomAnnotations

    {
        "gender":11
    }
    
    {
    	"code": 500,
    	"msg": "数据校验出现问题",
    	"data": {
    		"gender": "必须提交指定的值 [0,1]"
    	}
    }
    

🏅 项目地址:📢💨自定义注解实现数据校验
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Silence Lamb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值