目录
- 前言
- `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) {
// ......
}
接口测试
参数合法结果
name
字段为空的结果
年龄不合法的结果
@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
更新时不传入参数 id
@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);
}
}
嵌套校验测试
Teacher
和 Student
都校验不通过
Student
都校验不通过
控制分组校验顺序
@GroupSequence
它是 JSR
标准提供的注解,可以按指定的分组先后顺序进行验证;前面的分组校验不通过,后面的分组校验就不执行
- 如:
@GroupSequence({One.class, Two.class, Three.class})
先执行One
分组校验,然后执行Two
分组校验。如果One
分组校验失败了,则不会进行Two
分组的校验。即必须第一个组校验正确了,才执行第二组校验
控制分组校验顺序示例:
手动校验
在某些场景下需要我们手动校验 bean
,用校验器对需要被校验的 bean
发起 validate
获得校验结果
- 既可以使用
Hibernate Validation
提供Validator
,也可以使用Spring Validation
的Validator
使用 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 Validation
的 Validator
@Component
@Configuration
public class GlobalWebConfig {
@Bean
public Validator validator() {
return new LocalValidatorFactoryBean();
}
}
如果使用 springBoot
,LocalValidatorFactoryBean
已经成为了 Validator
的默认实现,使用时只需要自动注入即可
@Autowired
Validator globalValidator;
参数校验常用注解
注解 | 说明 |
| 限制只能为null |
| 限制必须不为null |
| 验证注解的元素值 |
| 只支持字符串类型字段,验证注解的元素值 |
| 限制必须符合指定的正则表达式 |
| 限制字符长度必须在min到max之间,(主要用于: String, Collection, Map and array) |
| 被注释的元素必须在合适的范围内 (主要用于 : BigDecimal, BigInteger, String, byte, short, int, long ,原始类型的包装类 ) |
| 被注解的对象必须是字符串,大小必须在制定的范围内 |
@DecimalMax(value) | 限制必须为一个不大于指定值的数字 |
@DecimalMin(value) | 限制必须为一个不小于指定值的数字 |
| 限制必须为一个不大于指定值的数字 |
| 限制必须为一个不小于指定值的数字 |
@Digits(integer,fraction) | 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction |
@AssertFalse | 限制必须为false |
@AssertTrue | 限制必须为true |
@Future | 限制必须是一个将来的日期 |
@Past | 验证注解的元素值(日期类型)比当前时间早 |
| 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式 |