SpringBoot 中使用@Validated注解进行数据校验

概述

在写业务代码时经常会遇到各种数据校验的问题,针对不同的业务需要不同的校验规则,这就会导致代码中出现很多的if else语句,所以用@Validated注解搭配@Valid注解进行代码简化和数据校验是很有必要的。

基本用法介绍

场景:新增学生

//如果你有一个学生类,其结构如下
//在新增时需要验证其名字和年龄是否为空,那么只需要如下操作即可

public class StudentDTO{
    @NotBlank(message = "学生名称不能为空")
    private String name;
    @NotNull(message = "年龄不能为空")
    private Integer age;
    ...
    getter
    setter省略
}
//在web接口处

@RestController
@RequestMapping("/api/v1/student")
public class StudentController{
    
    /**
     *
     * BindingResult result 一定要跟在 @Validated 注解对象的后面,且当有多个@Validated
     * 注解时,每个注解对象后面都需要添加一个
     */
    @PostMapping("add")
    public ResponseEntity addStudent(@RequestBody @Validated StudentDTO student , BindingResult result){
    
        if(result.hasErrors()){
            return new ResponseEntity(result.getFieldError().getDefaultMessage());
            //result.getFieldError().getDefaultMessage() 这个方法的内容就是刚刚在DTO处定义的message内容
        }
    }
}

嵌入校验

场景:新增学生。

很多时候,我们需要根据接口文档来传递参数,有可能新增学生的接口文档不仅仅会传递学生的数据,还会传入reqId之类的公共参数,这个时候就需要用到嵌入校验
举例:

//首先是公共参数类
public RequestStudentDTO{
    @NotBlank(message = "reqId不能为空")
    private String reqId;
    
    //这里直接使用@Valid注解
    @Valid
    @NotNull(message = "Student不能为空")
    private StudentDTO;
}

在web接口处需要修改为 RequestStudentDTO类

@RestController
@RequestMapping("/api/v1/student")
public class StudentController{
    
    /**
     *
     * BindingResult result 一定要跟在 @Validated 注解对象的后面,且当有多个@Validated
     * 注解时,每个注解对象后面都需要添加一个
     */
    @PostMapping("add")
    public ResponseEntity addStudent(@RequestBody @Validated RequestStudentDTO student , BindingResult result){
    
        if(result.hasErrors()){
            return new ResponseEntity(result.getFieldError().getDefaultMessage());
            //result.getFieldError().getDefaultMessage() 这个方法的内容就是刚刚在DTO处定义的message内容
        }
    }
}

因为

Validated注解

Valid注解

用在类型、方法和方法参数上。但是不能用在成员属性(字段)上

用在方法、构造函数、方法参数和成员属性(字段)上

所以,我们可以使用Valid注解来实现嵌套校验

分组校验

分组校验适用于同样的参数,不同的校验规则。如修改学生信息接口和新增学生接口

修改学生接口的参数校验规则肯定和新增学生接口的参数校验规则不一样,如:修改学生要根据学生的id进行修改,而新增学生不需要id。总不能为了一个字段再去新写一个类吧,那么在同一个类中,如何实现不同的接口进行不同参数规则的校验呢。

Validated注解为我们提供了分组校验的规则,只需要在写规则的时候加入group参数,同时以不同的接口进行区分就行了
举例:

//将前文的学生类进行修改,其结构如下
//在新增时需要验证其名字和年龄是否为空,那么只需要如下操作即可

public class StudentDTO{

    @NotBlank(message = "id", groups = {StudentUpdateGroup.class})
    private String id;
    @NotBlank(message = "学生名称不能为空", groups = {StudentInsertGroup.class, StudentUpdateGroup.class})
    private String name;
    @NotNull(message = "年龄不能为空", groups = {StudentInsertGroup.class, StudentUpdateGroup.class})
    private Integer age;
    ...
    getter
    setter省略
    
    
    //接口可以写在该类中,也可以分开单独写
    public interface StudentInsertGroup{}
    public interface StudentUpdateGroup{}
}

在web接口处,需要加上相关分组信息

@RestController
@RequestMapping("/api/v1/student")
public class StudentController{
    
    /**
     *
     * BindingResult result 一定要跟在 @Validated 注解对象的后面,且当有多个@Validated
     * 注解时,每个注解对象后面都需要添加一个
     */
    @PostMapping("add")
    public ResponseEntity addStudent(@RequestBody @Validated(StudentInsertGroup.class) RequestStudentDTO student , BindingResult result){
    
        if(result.hasErrors()){
            return new ResponseEntity(result.getFieldError().getDefaultMessage());
            //result.getFieldError().getDefaultMessage() 这个方法的内容就是刚刚在DTO处定义的message内容
        }
    }
    
    
    @PostMapping("update")
    public ResponseEntity addStudent(@RequestBody @Validated(StudentUpdateGroup.class) RequestStudentDTO student , BindingResult result){
    
        if(result.hasErrors()){
            return new ResponseEntity(result.getFieldError().getDefaultMessage());
            //result.getFieldError().getDefaultMessage() 这个方法的内容就是刚刚在DTO处定义的message内容
        }
    }
}

这样,我们就可以在同一个DTO中实现不同的校验规则了

常用校验规则

注解

说明

@Null

限制只能为Null

@NotNull

限制必须不为null

@AssertFalse

限制必须为false

@AssertTrue

限制必须为true

@DecimalMax(value)

限制必须为一个不大于指定值的数字

@DecimalMin(value)

限制必须为一个不小于指定值的数字

@Digits(integer,fraction)

限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction

@Future

限制必须是一个将来的日期

@Max(value)

限制必须为一个不大于指定值的数字

@Min(value)

限制必须为一个不小于指定值的数字

@Past

限制必须是一个过去的日期

@Pattern(value)

限制必须符合指定的正则表达式

@Size(max,min)

限制字符长度必须在min到max之间

@NotEmpty

验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)

@NotBlank

验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格

@Email

验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式