Spring参数校验

如何使用

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

导入坐标

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

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.1.Final</version>
        </dependency>

常用注解如下(本人并没有都使用过,粘贴的)
@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;

}
2.创建实现分组校验的接口
public interface Group1 {
	//命名比较随意,看看就行
    public interface g11{};
    public interface g12{};
    public interface g13{};
    public interface g14{};
    
}
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());
    }

可以看到,我写了三个接口,v1是验证Student(加入了分组校验),v2是直接在接口参数中对基本类型String进行校验,v3是接收JSON参数进行校验
接下来我们一个一个测试:
v1
v1接口很明显,需要对Student进行校验,并且指定了分组Group1.g12.class,而通过观察Student对象可以看到,只有学号no携带了该分组,所以只要学号字段验证通过就没问题。但是当学号为空时则控制台打印Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errors<EOL>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 [学号不能为空]<EOL>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 errors<EOL>Field 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;
    }
	//拦截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;
    }

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值