目录

  • 前言
  • `Controller` 中方法参数校验示例
  • 使用 `@Valid` 或 `@Validated` 注解
  • `Maven` 依赖
  • 实体类字段加上相关注解
  • `Controller` 方法参数加上 `@Valid` 或 `@Validated` 注解
  • 单个参数校验
  • 多个参数校验
  • 接口测试
  • 参数合法结果
  • `name` 字段为空的结果
  • 年龄不合法的结果
  • `@Validated` 注解特有的功能
  • 参数分组校验
  • 分组接口(空接口)
  • 实体类
  • `Controller` 控制类
  • 参数分组校验测试
  • 新增时传入参数 `id`
  • 更新时不传入参数 `id`
  • 嵌套校验
  • 实体类
  • `Controller` 控制类
  • 嵌套校验测试
  • `Teacher` 和 `Student` 都校验不通过
  • `Student` 都校验不通过
  • 控制分组校验顺序
  • 手动校验
  • 使用 `Hibernate Validation` 提供 `Validator`
  • 使用 `Spring Validation` 的 `Validator`
  • 参数校验常用注解


前言

数据的校验是网站一个不可或缺的功能,前端的 js 校验可以涵盖大部分的校验职责,如用户名唯一性,生日格式,邮箱格式校验等等常用的校验。但是为了避免用户绕过浏览器,使用 http 工具直接向后端请求一些违法数据,服务端的数据校验也是必要的

Controller 中方法参数校验示例

使用 @Valid@Validated 注解

Maven 依赖

  • @Valid:新版本的 springBoot 需要手动引入下面的依赖,老版本只需引入 spring-boot-starter-web 即可,里面集成了 Hibernate-Validator
<dependency>
	<groupId>org.hibernate.validator</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>6.0.13.Final</version>
</dependency>
  • @Validated:是对 hibernate-validator 的封装,是 spring 提供的校验机制,只要引入 spring-context 依赖即可

实体类字段加上相关注解

public class User implements Serializable {

    @NotBlank(message = "姓名不能为空")
    private String name;

    @NotNull(message = "年龄不能为空")
    @Range(min = 0, max = 200, message = "年龄不合法")
    private Integer age;

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
	
	// setter,getter 方法省略......
}
  • @NotBlank:只用于字符串,字符串不能为 null,并且去除两端空白字符后的长度大于 0,例:""," "
  • @NotEmpty:只用于字符串、集合、map、数组,且不能为 null,并且长度或者大小大于 1
  • @NotNull:适用于所有类型,且不能为 null
  • @AssertTrue:被注释的元素必须为 true
  • @AssertFalse:被注释的元素必须为 false
  • @Min(value):被注释的元素必须是一个数字,其值必须大于等于指定的最小值
  • @Max(value):被注释的元素必须是一个数字,其值必须小于等于指定的最大值
  • @Size(max,min):被注释的元素的大小必须在指定的范围内
  • @Email:被注释的元素必须是电子邮件地址
  • @Length:被注释的字符串的大小必须在指定的范围内
  • @Range:被注释的元素必须在合适的范围内

Controller 方法参数加上 @Valid@Validated 注解

单个参数校验
@Controller
public class UserController {

    @RequestMapping(path = "validatorUser", method = RequestMethod.POST)
    @ResponseBody
    public ResponseResult validatorUser(@Valid User user, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
        	// 用于获取相应字段上添加的 message 中的内容
            String message = bindingResult.getFieldError().getDefaultMessage();
            return new ResponseResult(500, message, null);
        }
        return new ResponseResult(200, "成功", user);
    }
}
多个参数校验
public Objet test(@Validated Object param1, BindingResult result1 ,@Validated Object param2, BindingResult Result2) {
	// ......
}

接口测试

参数合法结果

spring controller 布尔参数 springboot controller参数校验_spring

name 字段为空的结果

spring controller 布尔参数 springboot controller参数校验_spring_02

年龄不合法的结果

spring controller 布尔参数 springboot controller参数校验_spring_03

@Validated 注解特有的功能

@Valid 不具备的功能,而注解 @Validated 注解特有的功能。源码分别如下

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {

	Class<?>[] value() default {};
}


@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
public @interface Valid {
}

参数分组校验

当一个实体类需要多种验证方式时,例如:对于一个实体类的 id 来说,新增的时候是不需要的,对于更新时是必须的。可以通过 groups 对验证进行分组

分组接口(空接口)

通过向 groups 分配不同类的 class 对象,达到分组目的

public interface UserServiceInsert {

}

public interface UserServiceUpdate {

}
实体类
public class UserInfo implements Serializable {

    @Null(message = "新增时id必须为空", groups = {UserServiceInsert.class})
    @NotNull(message = "更新时id不能为空", groups = {UserServiceUpdate.class})
    private Integer id;

    @NotBlank(message = "姓名不能为空")
    private String name;

    @NotNull(message = "年龄不能为空")
    @Range(min = 0, max = 200, message = "年龄不合法")
    private Integer age;

    public UserInfo(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
	
	// setter,getter 方法省略......
}
Controller 控制类
@Controller
public class UserController {

    @RequestMapping(path = "insertUser", method = RequestMethod.POST)
    @ResponseBody
    public ResponseResult insertUser(@Validated(value = UserServiceInsert.class) UserInfo userInfo, @NotNull BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            String message = bindingResult.getFieldError().getDefaultMessage();
            return new ResponseResult(500, message, null);
        }
        return new ResponseResult(200, "成功", userInfo);
    }

    @RequestMapping(path = "updateUser", method = RequestMethod.POST)
    @ResponseBody
    public ResponseResult updateUser(@Validated(value = UserServiceUpdate.class) UserInfo userInfo, @NotNull BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            String message = bindingResult.getFieldError().getDefaultMessage();
            return new ResponseResult(500, message, null);
        }
        return new ResponseResult(200, "成功", userInfo);
    }
}
参数分组校验测试
新增时传入参数 id

spring controller 布尔参数 springboot controller参数校验_实体类_04

更新时不传入参数 id

spring controller 布尔参数 springboot controller参数校验_实体类_05

  • @Validated 添加了 groups 属性时,其只会校验实体分组的属性。如只会校验 UserInfo 中的 id 属性,而不会校验 name,age 属性

嵌套校验

@Valid 加在方法参数时,不会自动进行嵌套验证,而是用在需要嵌套验证类内的相应字段上,来配合方法参数上 @Validated@Valid 来进行嵌套验证

实体类
public class Teacher implements Serializable {

    @NotEmpty(message = "Teacher 姓名不能为空")
    private String teacher_name;

    @NotNull(message = "Teacher 年龄不能为空")
    @Range(min = 0, max = 200, message = "Teacher 年龄不合法")
    private Integer teacher_age;

    @Valid
    @Size(min = 1, max = 10, message = "列表中的元素数量为1~10")
    private List<Student> students;

    public Teacher(String teacher_name, Integer teacher_age, List<Student> students) {
        this.teacher_name = teacher_name;
        this.teacher_age = teacher_age;
        this.students = students;
    }
	
	// setter,getter 方法省略......
}


public class Student implements Serializable {

    @Valid
    @NotEmpty(message = "Student 姓名不能为空")
    private String name;

    @Valid
    @NotNull(message = "Student 年龄不能为空")
    @Range(min = 0, max = 200, message = "Student 年龄不合法")
    private Integer age;

    public Student(String name, Integer age) {
        this.age = age;
        this.name = name;
    }

	// setter,getter 方法省略......
}
Controller 控制类
@Controller
public class UserController {

    @RequestMapping(path = "nestValid", method = RequestMethod.POST)
    @ResponseBody
    public ResponseResult nestValid(@Validated @RequestBody Teacher teacher, @NotNull BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            String message = bindingResult.getFieldError().getDefaultMessage();
            return new ResponseResult(500, message, null);
        }
        return new ResponseResult(200, "成功", teacher);
    }
}
嵌套校验测试
TeacherStudent 都校验不通过

spring controller 布尔参数 springboot controller参数校验_嵌套_06

Student 都校验不通过

spring controller 布尔参数 springboot controller参数校验_嵌套_07


spring controller 布尔参数 springboot controller参数校验_实体类_08

控制分组校验顺序

@GroupSequence 它是 JSR 标准提供的注解,可以按指定的分组先后顺序进行验证;前面的分组校验不通过,后面的分组校验就不执行

  • 如:@GroupSequence({One.class, Two.class, Three.class}) 先执行 One 分组校验,然后执行 Two 分组校验。如果 One 分组校验失败了,则不会进行 Two 分组的校验。即必须第一个组校验正确了,才执行第二组校验

控制分组校验顺序示例:

手动校验

在某些场景下需要我们手动校验 bean,用校验器对需要被校验的 bean 发起 validate 获得校验结果

  • 既可以使用 Hibernate Validation 提供 Validator,也可以使用 Spring ValidationValidator

使用 Hibernate Validation 提供 Validator

public class ValidationTest {
    public static void main(String[] args) {
        Foo foo = new Foo();
        foo.setUsername(null);
        foo.setPassword(null);
        foo.setUserType("");

		// 构建Validator
        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
        Validator validator = validatorFactory.getValidator();
		// 使用Validator校验bean
        Set<ConstraintViolation<Foo>> set = validator.validate(foo);
        for (ConstraintViolation<Foo> constraintViolation : set) {
            System.out.println(constraintViolation.getMessage());
        }
    }

    @Data
    public static class Foo {
    
        @NotNull(message = "username不能为空")
        private String username;

        @NotNull(message = "password不能为空")
        private String password;

        @NotBlank(message = "userType不能为空")
        private String userType;
    }
}

使用 Spring ValidationValidator

@Component
@Configuration
public class GlobalWebConfig {

 	@Bean
	public Validator validator() {
   		return new LocalValidatorFactoryBean();
	}
}

如果使用 springBootLocalValidatorFactoryBean 已经成为了 Validator 的默认实现,使用时只需要自动注入即可

@Autowired 
Validator globalValidator;

参数校验常用注解

注解

说明

@Null

限制只能为null

@NotNull

限制必须不为null

@NotEmpty

验证注解的元素值不为null且不为空字符串长度不为0、集合大小不为0)(主要用于:String,Collection,Map,array)

@NotBlank

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

@Pattern(value)

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

@Size(max,min)

限制字符长度必须在min到max之间,(主要用于: String, Collection, Map and array)

@Range(min, max)

被注释的元素必须在合适的范围内 (主要用于 : BigDecimal, BigInteger, String, byte, short, int, long ,原始类型的包装类 )

@Length(min, max)

被注解的对象必须是字符串,大小必须在制定的范围内

@DecimalMax(value)

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

@DecimalMin(value)

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

@Max(value)

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

@Min(value)

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

@Digits(integer,fraction)

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

@AssertFalse

限制必须为false

@AssertTrue

限制必须为true

@Future

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

@Past

验证注解的元素值(日期类型)比当前时间早

@Email

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