• Spring参数校验


    如何使用

    Spring提供了简便的参数校验注解,不需要像以前一样if else去判断了,下面记录一下如何使用注解实现参数的校验

    导入坐标

    要使用各种注解完成参数的校验,需要导入hibernate-validator坐标以实现

            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-validator</artifactId>
                <version>6.0.1.Final</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    常用注解如下(本人并没有都使用过,粘贴的)
    @Null 检查该字段为空
    @NotNull 不能为null
    @NotBlank 不能为空,常用于检查空字符串
    @NotEmpty 不能为空,多用于检测list是否size是0
    @Max 该字段的值只能小于或等于该值
    @Min 该字段的值只能大于或等于该值
    @Past 检查该字段的日期是在过去
    @Future 检查该字段的日期是否是属于将来的日期
    @Email 检查是否是一个有效的email地址
    @Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
    @Range(min=,max=,message=) 被注释的元素必须在合适的范围内
    @Size(min=, max=) 检查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等
    @Length(min=,max=) 检查所属的字段的长度是否在min和max之间,只能用于字符串
    @AssertTrue 用于boolean字段,该字段只能为true
    @AssertFalse 该字段的值只能为false

    注解说明

    我们使用到的验证参数注解共有两个@Valid与@Validated
    @Validated:可以用在类型、方法和方法参数上,支持分组校验。但是不能用在成员属性上,不支持嵌套检测;
    @Valid:可以用在方法、构造方法、方法参数和成员属性上,支持嵌套检测。

    验证注解时可能抛出的异常说明

    在使用注解校验参数的过程中,可能由于参数不合法会抛出各种异常,但是大体就只有下面三种
    BindException:表单提交时,如果参数未通过注解校验抛出该异常,对于以json格式提交将不会抛出该异常
    MethodArgumentNotValidException:前端以JSON格式提交参数,后端使用requestBody接收时,如果参数未通过注解校验则抛出该异常。需要说明的是,该异常是BindException的子类
    ConstraintViolationException :1. 接口参数上加@RequestParam、@PathVariable或者没加任何注解(按形参名赋值)
    2. 接口参数加@NotBlank、@NotNull、@Length等校验注解
    当1和2都成立时,也就是说当直接在接口的参数中对基本类型进行校验时,若校验未通过则抛出该异常

    简单的使用
    1. 创建接收参数的对象
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public class Student {
        @NotNull(groups = Group1.g12.class,message = "学号不能为空")
        @Length(min = 2,max = 3, groups = Group1.g12.class,message = "学号长度错误,需要在2~3位之间")
        private String no;
    
        @NotNull(groups = Group1.g11.class,message = "姓名不存在!")
        private String name;
    
        @Max(12)
        private Integer age;
    
        private String address;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    2.创建实现分组校验的接口
    public interface Group1 {
    	//命名比较随意,看看就行
        public interface g11{};
        public interface g12{};
        public interface g13{};
        public interface g14{};
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    3.写几个接口测试测试
        @PostMapping("v1")
        public void v1(@Validated(Group1.g12.class) Student student){  //bindException
            System.out.println(student.toString());
        }
    
        @GetMapping("v2/{name}")
        public void v2(@PathVariable @Length(min = 2,max = 5,message = "name长度需要在2~5之间") String name){ //ConstraintViolationException
            System.out.println(name);
        }
    
        @PostMapping("v3")
        public void v3(@RequestBody @Validated Student student){ //MethodArgumentNotValidException
            System.out.println("业务执行了...");
            System.out.println(student.toString());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    可以看到,我写了三个接口,v1是验证Student(加入了分组校验),v2是直接在接口参数中对基本类型String进行校验,v3是接收JSON参数进行校验
    接下来我们一个一个测试:
    v1
    v1接口很明显,需要对Student进行校验,并且指定了分组Group1.g12.class,而通过观察Student对象可以看到,只有学号no携带了该分组,所以只要学号字段验证通过就没问题。但是当学号为空时则控制台打印Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errorsField error in object 'student' on field 'no': rejected value [null]; codes [NotNull.student.no,NotNull.no,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.no,no]; arguments []; default message [no]]; default message [学号不能为空]Field error in object 'student' on field 'no': rejected value [null]; codes [NotNull.student.no,NotNull.no,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.no,no]; arguments []; default message [no]]; default message [学号不能为空]]
    当学号超过3位长度时则控制台打印:Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errorsField error in object 'student' on field 'no': rejected value [1200]; codes [Length.student.no,Length.no,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.no,no]; arguments []; default message [no],3,2]; default message [学号长度错误,需要在2~3位之间]]

    v2(提示下,直接在接口中对类型进行注解校验需要在controller上打@Validated注解)
    v2就是在接口参数中直接对对基本类型进行校验,长度需要在2~5之间,所以我们直接发送一个不符合条件的请求,http://localhost:8080/v2/HelloWorld,控制台打印:
    在这里插入图片描述
    v3
    v3就是接收参数的形式变成了JSON,并且进行了参数校验(不带任何分组),所以只是对age进行了校验,age不能超过12,我们试着发送一个不合法的请求,JSON参数为{“name”:“zhangsan”,“age”:15},结果控制台打印:Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public void com.example.validation.controller.VController.v3(com.example.validation.entity.Student): [Field error in object 'student' on field 'age': rejected value [15]; codes [Max.student.age,Max.age,Max.java.lang.Integer,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.age,age]; arguments []; default message [age],12]; default message [最大不能超过12]] ]

    4.关于分组校验

    分组校验的情况下,对于没有添加分组校验的注解将不会生效
    没有分组校验情况下,只会校验不带分组校验的校验注解

    5.分组校验的全局异常处理

    在第三步中,我们测试完接口发现报出了不同的异常信息,那么全局异常处理是必不可少的,需要将不合法的提示正确的返回给用户。
    经过测试,对于报出的异常共有三类

    1. 对于直接在接口中进行校验的基本类型参数(包含String),抛出ConstraintViolationException
    2. 对于JSON请求映射的参数,抛出MethodArgumentNotValidException
    3. 对于接口参数为对象时,抛出BindException

    其中,MethodArgumentNotValidException是BindException的子类
    那么,我们只需要拦截BindException和ConstraintViolationException进行处理即可,如下:

    	//拦截BindException和MethodArgumentNotValidException
        @ExceptionHandler(BindException.class)
        public Map<String, String> MANVE(BindException e){
            Map<String, String> res = new LinkedHashMap();
            BindingResult bindingResult = e.getBindingResult();
            List<ObjectError> allErrors = bindingResult.getAllErrors();
            for (ObjectError item:allErrors) {
                if (item instanceof FieldError){
                    String field = ((FieldError) item).getField();
                    String defaultMessage = ((FieldError) item).getDefaultMessage();
                    res.put(field,defaultMessage);
                }
            }
            return res;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    	//拦截ConstraintViolationException
        @ExceptionHandler(ConstraintViolationException.class)
        public Map<String,String> CVE(ConstraintViolationException e){
            Map<String,String> res = new LinkedHashMap<>();
            Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
            for (ConstraintViolation item: constraintViolations) {
                String messageTemplate = item.getMessageTemplate();
                PathImpl propertyPath = (PathImpl) item.getPropertyPath();
                NodeImpl leafNode = propertyPath.getLeafNode();
                res.put(leafNode.asString(),messageTemplate);
            }
            return res;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以把代码中的返回报错信息改为你所需要的形式

  • 相关阅读:
    Typescript中类的使用
    Astyle Linux风格配置
    数据库系统学习 关系代数
    / 将 Month 转换为整数:int monthAsInt = static_cast<int>(month);“static_cast”是干啥的?
    【vue3】自定义hook函数
    Map中key和value值是否可以为null或空字符串?
    【Python零基础入门篇 · 5】:占位符和格式化输入输出、标识符和保留字
    JavaScrip学习
    什么叫硬编码?如何避免硬编码
    Mybatis使用注解实现复杂动态SQL
  • 原文地址:https://blog.csdn.net/weixin_44001317/article/details/127904264