• 循序渐进了解如何使用JSR303进接口数据校验


    循序渐进了解如何使用JSR303进接口数据校验

    1. 引言

    在一个完整的前后端项目中,为了确保用户输入的数据是想要的格式的数据,或者说避免他人通过某种手段获取到接口然后进行非法的数据请求。所以无论是前端还是后端都需要进行数据校验。

    这时候有人可能就会说了,前端进行数据校验不就行了吗,他输入的数据不对,就不会发送请求给后端,这样不久安全了吗?

    乍一听很有道理,但是万一那个用户不是小白,是一个程序猿呢,故意输入正确的数据,然后利用浏览器的调试工具然后获得到我们的接口,再利用Postman这样的测试工具对我们的接口进行数据交互,那不相当于接口完全暴露给他了吗

    所以啊,后端进行数据的检验,是很有必要滴


    2. 数据准备

    下面模仿一个业务场景,我们需要对一个学生类进行“增删改查”的操作

    由于这里是对接口数据的检验,所以就不对数据库进行操作了,只是简单模仿一下场景

    学生类

    public class Student {
        /**
         * 编号id
         */
        private Integer id;
        /**
         * 学生姓名
         */
        private String name;
        /**
         * 学生年龄
         */
        private Integer age;
        /**
         * 学生电话
         */
        private String phone;
        /**
         * 学生状态:1表示正常,0表示已经退学
         */
        private Integer status;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    统一返回结果

    @Data
    public class R extends HashMap<String, Object>  {
    
        /**
         * 成功
         */
        public static R ok(String msg){
            R r = new R();
            r.put("code", 0);
            r.put("msg", msg);
            return r;
        }
    
        /**
         * 失败
         */
        public static R error(String msg){
            R r = new R();
            r.put("code", 1);
            r.put("msg", msg);
            return r;
        }
        
          /**
         * 重写put方法,使得返回值为R
         */
        @Override
        public R put(String key, Object value) {
            super.put(key, value);
            return this;
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    StudentController,进行数据校验的较多的是更新和保存

    @RestController
    @RequestMapping("/student")
    public class StudentController {
        @PostMapping("update")
        public R update(@RequestBody Student student){
            /**
             * 对应的对数据库进行操作
             */
            return R.ok("更新成功").put("student", student);
        }
    
        @PostMapping ("save")
        public R save(@RequestBody Student student){
            /**
             * 对应的对数据库进行操作
             */
            return R.ok("新增成功").put("student", student);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3. 比较“笨”的数据校验

    如果不适用任何工具类,手搓验证的话,就会像下面一样复杂

    @RestController
    @RequestMapping("/student")
    public class StudentController {
        @PostMapping("update")
        public R update(@RequestBody Student student) {
            if (student == null) {
                return R.error("参数错误");
            } else if (student.getId() == null){
                return R.error("学生编号不能为空");
            }else if (student.getName() == null || student.getName() == "" || student.getName().contains(" ")) {
                return R.error("请输入正确的姓名");
            } else if (student.getAge() < 0 || student.getAge() == null) {
                return R.error("请输入正确的年龄");
            } else if (student.getPhone() == null || student.getPhone() == "" || student.getPhone().contains(" ")) {
                return R.error("请输入正确的电话号码");
            }
            /**
             * 对应的对数据库进行操作
             */
            return R.ok("更新成功").put("student", student);
        }
    
        @PostMapping("save")
        public R save(@RequestBody Student student) {
            if (student == null) {
                return R.error("参数错误");
            } else if (student.getId() != null){
                return R.error("新增不能指定学生编号");
            }else if (student.getName() == null || student.getName() == "" || student.getName().contains(" ")) {
                return R.error("请输入正确的姓名");
            } else if (student.getAge() < 0 || student.getAge() == null) {
                return R.error("请输入正确的年龄");
            } else if (student.getPhone() == null || student.getPhone() == "" || student.getPhone().contains(" ")) {
                return R.error("请输入正确的电话号码");
            }
            /**
             * 对应的对数据库进行操作
             */
            return R.ok("新增成功").put("student", student);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    就算使用MP带的ObjectUtils工具类替换上面的手搓,也会出现很多重复的代码,这样是很不友好的


    4. 使用JSR303进行数据校验

    JSR303是一套JavaBean参数校验标准,其定义了很多校验注解,我们可以使用在实体类使用注解来对对象的成员变量进行参数校验,大大减少了如上所示的繁琐的数据校验

    以上面的更新学生信息为例,先导入依赖(下面那个是springboot集成的,用哪一个都行)

    
    <dependency>
        <groupId>javax.validationgroupId>
        <artifactId>validation-apiartifactId>
        <version>2.0.1.Finalversion>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-validationartifactId>
        <version>2.3.7.RELEASEversion>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    对学生实体类的代码修改如下

    @Data
    public class Student {
        /**
         * 编号id
         * 使用JSR-303校验id不能为null
         */
        @NotNull()
        private Integer id;
        /**
         * 学生姓名
         * 使用JSR-303校验name不能为null并且不能是空字符串和空格
         */
        @NotBlank()
        private String name;
        /**
         * 学生年龄
         * 使用JSR-303校验name不能为null并且大于等于0
         */
        @NotNull()
        @Min(value = 0)
        private Integer age;
        /**
         * 学生电话
         * 使用JSR-303校验phone必须是0-9组成的字符串
         */
        @NotBlank
        @Pattern(regexp = "^[0-9]*$")
        private String phone;
        /**
         * 学生状态:1表示正常,0表示已经退学
         */
        private Integer status;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    然后这里就添加了数据校验规则,添加完校验规则之后,还需要在controller添加@Valid注解

    @PostMapping("update")
    public R update(@Valid @RequestBody Student student) {
        /**
         * 对应的对数据库进行操作
         */
        return R.ok("更新成功").put("student", student);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    用postman测试接口,出现400

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VeNfJikF-1659854849337)(SpringBoot 接口数据校验.assets/image-20220806221053184.png)]

    而控制台打印告诉我们不能为null

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-30V8mliE-1659854849338)(SpringBoot 接口数据校验.assets/image-20220806221130436.png)]

    这样数据校验就成功了,有人获取就疑惑这里的提示信息是在哪来的呢?

    其实吧,它是在ValidationMessages_zh.properties中已经写好的了

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kS0HEdCB-1659854849339)(SpringBoot 接口数据校验.assets/image-20220806222358876.png)]

    我们也可以自定义提示信息,如下所示

    @Data
    public class Student {
        /**
         * 编号id
         * 使用JSR-303校验id不能为null
         */
        @NotNull(message = "学生编号不能为null")
        private Integer id;
        /**
         * 学生姓名
         * 使用JSR-303校验name不能为null并且不能是空字符串和空格
         */
        @NotBlank(message = "请输入正确的姓名")
        private String name;
        /**
         * 学生年龄
         * 使用JSR-303校验name不能为null并且大于等于0
         */
        @NotNull(message = "年龄不能为空")
        @Min(value = 0, message = "年龄必须大于等于0")
        private Integer age;
        /**
         * 学生电话
         * 使用JSR-303校验phone必须是0-9组成的字符串
         */
        @NotBlank
        @Pattern(regexp = "^[0-9]*$", message = "联系电话必须由0-9数字组成")
        private String phone;
        /**
         * 学生状态:1表示正常,0表示已经退学
         */
        private Integer status;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5lKFrxA2-1659854849340)(SpringBoot 接口数据校验.assets/image-20220806222528879.png)]


    5. 将数据检验不通过的信息返回给前端

    上面虽然完成了对数据的校验,但是它的提示只会在控制台输出,这样前端不能在后端检验完成之后返回相应的响应

    @PostMapping("update")
    public R update(@Valid @RequestBody Student student, BindingResult result) {
        if (result.hasErrors()){
            Map<String, String> map = new HashMap<>();
            List<FieldError> fieldErrors = result.getFieldErrors();
            fieldErrors.forEach((fieldError -> {
                String field = fieldError.getField();
                String message = fieldError.getDefaultMessage();
                map.put(field, message);
            }));
            return R.error("参数错误").put("data", map);
        }
        /**
         * 对应的对数据库进行操作
         */
        return R.ok("更新成功").put("student", student);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TwIdBGW3-1659854849340)(SpringBoot 接口数据校验.assets/image-20220806223733953.png)]

    OK!!!!!这样就能让前端知道后端的检验结果了

    但是这样还没结束,这样每个接口都要写一遍这样的代码,代码重复度太多。


    6. 集中处理异常

    由上面就知道了,当检验数据不通过的时候,就会报异常,前端得到的状态码是400

    这样,我们集中处理异常,然后将数据检验不通过的提示提取出来,再返回给前端,这样就能用一个类解决上面出现的问题了

    package com.example.jsr303.exception;
    
    import com.example.jsr303.vo.R;
    import org.springframework.validation.BindingResult;
    import org.springframework.validation.FieldError;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @ClassName ExceptionControllerAdvice
     * @Description TODO
     * @Author kang
     * @Date 2022/8/6 下午 10:43
     * @Version 1.0
     */
    @RestControllerAdvice
    public class ExceptionControllerAdvice {
        /**
         * 处理特定异常
         *
         * @param e
         * @return
         */
        @ExceptionHandler(value = MethodArgumentNotValidException.class)
        public R handleVaildException(MethodArgumentNotValidException e) {
            BindingResult result = e.getBindingResult();
            Map<String, String> map = new HashMap<>();
            if (result.hasErrors()) {
                List<FieldError> fieldErrors = result.getFieldErrors();
                fieldErrors.forEach((fieldError -> {
                    String field = fieldError.getField();
                    String message = fieldError.getDefaultMessage();
                    map.put(field, message);
                }));
            }
            return R.error("数据检验不通过").put("data", map);
        }
    
    
        /**
         * 处理其他异常
         *
         * @return
         */
        @ExceptionHandler(value = Exception.class)
        public R handleException() {
    
            return R.error("服务器内部异常");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    这里使用了RestControllerAdvice来处理,RestControllerAdvice是全局接口处理异常的类,其是由ControllerAdvice和@ResponseBody组合成的,规定了返回数据类型是json

    个人赶紧这种处理全局异常的方法,非常像Controller层, @ExceptionHandler类似@PostMapping,用来区别异常

    如上,如果是MethodArgumentNotValidException异常就会执行handleVaildException方法,然后给前端返回错误信息

    这样Controller就能改回原来的样子

    @PostMapping("update")
    public R update(@Valid @RequestBody Student student) {
        /**
         * 对应的对数据库进行操作
         */
        return R.ok("更新成功").put("student", student);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lrYujzJR-1659854849341)(SpringBoot 接口数据校验.assets/image-20220806225149417.png)]

    大功告成!!!!!


    7. 使用JSR303进行分组校验

    当我们进行新增操作的时候会发现学生ID并不能自定义,所以上面的数据校验只适用于修改操作

    那有人会说,在id加一个@Null注解不就行了吗,但是既有@NotNull注解又有@Null注解,程序也不知道什么时候要为null,什么时候为Null呀

    所以,就出现了分组校验,可以规定哪一个接口对应哪一种数据校验

    比如现在有修改和新增,那么就分成两组,修改组为UpdateGroup,新增组为AddGroup

    要实现分组校验,首先先创建两个接口——UpdateGroup和AddGroup,接口里面不需要写任何东西

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QpsjT1xJ-1659854849341)(SpringBoot 接口数据校验.assets/image-20220807140604436.png)]

    然后修改学生实力类代码,给它们分组

    @Data
    public class Student {
        /**
         * 编号id
         * 使用JSR-303校验id不能为null
         */
        @Null(message = "新增不能指定学生编号", groups = {AddGroup.class})
        @NotNull(message = "学生编号不能为null", groups = {UpdateGroup.class})
        private Integer id;
        /**
         * 学生姓名
         * 使用JSR-303校验name不能为null并且不能是空字符串和空格
         */
        @NotBlank(message = "请输入正确的姓名", groups = {AddGroup.class, UpdateGroup.class})
        private String name;
        /**
         * 学生年龄
         * 使用JSR-303校验name不能为null并且大于等于0
         */
        @NotNull(message = "年龄不能为空", groups = {AddGroup.class, UpdateGroup.class})
        @Min(value = 0, message = "年龄必须大于等于0", groups = {AddGroup.class, UpdateGroup.class})
        private Integer age;
        /**
         * 学生电话
         * 使用JSR-303校验phone必须是0-9组成的字符串
         */
        @NotBlank(message = "学生电话不能为空", groups = {AddGroup.class, UpdateGroup.class})
        @Pattern(regexp = "^[0-9]*$", message = "联系电话必须由0-9数字组成", groups = {AddGroup.class, UpdateGroup.class})
        private String phone;
        /**
         * 学生状态:1表示正常,0表示已经退学
         */
        private Integer status;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    修改完实例类之后还需要修改controller层,给每个接口分配,让他们匹配每个类对应的数据校验规则

    使用@Validated注解规定数据校验规则

    @RestController
    @RequestMapping("/student")
    public class StudentController {
        @PostMapping("update")
        public R update(@Validated(UpdateGroup.class) @RequestBody Student student) {
            /**
             * 对应的对数据库进行操作
             */
            return R.ok("更新成功").put("student", student);
        }
    
        @PostMapping("save")
        public R save(@Validated(AddGroup.class) @RequestBody Student student) {
            /**
             * 对应的对数据库进行操作
             */
            return R.ok("新增成功").put("student", student);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    测试接口,大功告成!!!!

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cd8YK7O1-1659854849342)(SpringBoot 接口数据校验.assets/image-20220807141834743.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hKScudJc-1659854849342)(SpringBoot 接口数据校验.assets/image-20220807141939084.png)]


    8. 使用JSR303自定义校验注解

    JSR303为我们提供了大量常用的校验注解,但是吧,总有些校验规则比较特殊

    比如Student中的status成员变量,我们规定它只能是0或者1,但是没有这样现成的注解,所以我们可以自定义一个校验注解

    我们可以模仿着现成的校验注解比如Null来自己写一个自定义的校验注解

    下面这个是@Null的注解

    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Repeatable(Null.List.class)
    @Documented
    @Constraint(
        validatedBy = {}
    )
    public @interface Null {
        String message() default "{javax.validation.constraints.Null.message}";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
        @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        public @interface List {
            Null[] value();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    然后我们自己新建一个注解StatusValue

    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Constraint(
            validatedBy = {}
    )
    public @interface StatusValue {
        String message() default "{com.example.jsr303.validation.StatusValue.message}";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
        int[] values() default {};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    其中values是我们需要规定的数据,比如我们规定是0和1,那么到时候就给注解的values赋值给0和1

    这样还不行,还需要加一个校验器,也就是validatedBy里面的内容

    下面是validatedBy点进去的发现我们需要一个ConstraintValidator类

    @Documented
    @Target({ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Constraint {
        Class<? extends ConstraintValidator<?, ?>>[] validatedBy();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    说干就干,我们就新建一个实现ConstraintValidator接口的类

    public class StatusValueConstraintValidator implements ConstraintValidator<StatusValue, Integer> {
        
        private Set<Integer> set = new HashSet<>();
        
        /**
         * 初始化
         * @param constraintAnnotation
         */
        @Override
        public void initialize(StatusValue constraintAnnotation) {
            int[] values = constraintAnnotation.values();
            for (int value : values) {
                set.add(value);
            }
        }
    
        /**
         * 判断是否校验成功
         * @param integer 需要检验的数据
         * @param constraintValidatorContext
         * @return
         */
        @Override
        public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
            return set.contains(integer);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    首先,在初始化的时候,将我们将数据初始化,也就是比如说我们需要规定数据为0和1,然后就将数据0和1从constraintAnnotation拿那个values(这个values就是自己定义的,你定义了什么,这个名字就应该是什么)出来,然后放到集合中

    然后在isValid方法进行校验**,第一个变量是需要校验的数据**,这里就校验一下这个数据是否在集合中就行了

    接着在StatusValue注解中给它添上校验规则

    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Constraint(
            validatedBy = {StatusValueConstraintValidator.class}
    )
    public @interface StatusValue {
        String message() default "{com.example.jsr303.validation.StatusValue.message}";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
        int[] values() default {};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这样自定义的校验注解的完成了,给Student的成员变量status测试一下试一试

    @Data
    public class Student {
        /**
         * 编号id
         * 使用JSR-303校验id不能为null
         */
        @Null(message = "新增不能指定学生编号", groups = {AddGroup.class})
        @NotNull(message = "学生编号不能为null", groups = {UpdateGroup.class})
        private Integer id;
        /**
         * 学生姓名
         * 使用JSR-303校验name不能为null并且不能是空字符串和空格
         */
        @NotBlank(message = "请输入正确的姓名", groups = {AddGroup.class, UpdateGroup.class})
        private String name;
        /**
         * 学生年龄
         * 使用JSR-303校验name不能为null并且大于等于0
         */
        @NotNull(message = "年龄不能为空", groups = {AddGroup.class, UpdateGroup.class})
        @Min(value = 0, message = "年龄必须大于等于0", groups = {AddGroup.class, UpdateGroup.class})
        private Integer age;
        /**
         * 学生电话
         * 使用JSR-303校验phone必须是0-9组成的字符串
         */
        @NotBlank(message = "学生电话不能为空", groups = {AddGroup.class, UpdateGroup.class})
        @Pattern(regexp = "^[0-9]*$", message = "联系电话必须由0-9数字组成", groups = {AddGroup.class, UpdateGroup.class})
        private String phone;
        /**
         * 学生状态:1表示正常,0表示已经退学
         */
        @StatusValue(values = {0, 1}, groups = {AddGroup.class, UpdateGroup.class}, message = "学生状态只能是0和1")
        private Integer status;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nyV3a6R2-1659854849343)(SpringBoot 接口数据校验.assets/image-20220807144104362.png)]

    当然这个message我们也可以写在配置文件中,让它自己来读

    首先新建一个ValidationMessages.properties文件,一定要是这个名字

    com.example.jsr303.validation.StatusValue.message=学生状态必须提交指定值
    
    • 1

    然后执行代码,把实体类的message删除掉,执行代码,也是可以的

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oP2TkqhO-1659854849343)(SpringBoot 接口数据校验.assets/image-20220807144405459.png)]


    9. 常用的校验注解

    这里面列出一下常用的校验注解

    这里常用的校验注解内容是参考https://blog.csdn.net/junR_980218/article/details/124590311

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KxpgZVr0-1659854849344)(SpringBoot 接口数据校验.assets/1881324b017c4ddead9a9999b6ca484b.png)]


  • 相关阅读:
    数据库索引面试的相关问题
    字符串的模式匹配
    Redis 为何使用近似 LRU 算法淘汰数据,而不是真实 LRU?
    MongoDB 未授权访问漏洞
    mac支持fat32格式吗 mac支持什么格式的移动硬盘
    DevOps与CI/CD常见面试问题汇总
    LM339模块电路故障查询
    @RequestBody、 @RequestParam 、 @PathVariable 和 @Vaild 注解
    【Python学习笔记】第二章循环:while循环,for循环,break和continue语句,死循环,循环的嵌套
    【前端指南】session和token
  • 原文地址:https://blog.csdn.net/weixin_51146329/article/details/126211143