引入依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

相关注解

注解 功能
@AssertFalse 可以为null,如果不为null的话必须为false
@AssertTrue 可以为null,如果不为null的话必须为true
@DecimalMax 设置不能超过最大值
@DecimalMin 设置不能超过最小值
@Digits 设置必须是数字且数字整数的位数和小数的位数必须在指定范围内
@Future 日期必须在当前日期的未来
@Past 日期必须在当前日期的过去
@Max 最大不得超过此最大值
@Min 最大不得小于此最小值
@NotNull 不能为null,可以是空
@Null 必须为null
@Pattern 必须满足指定的正则表达式
@Size 集合、数组、map等的size()值必须在指定范围内
@Email 必须是email格式
@Length 长度必须在指定范围内
@NotBlank 字符串不能为null,字符串trim()后不能等于””
@NotEmpty 不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于””
@Range 值必须在指定范围内
@URL 必须是一个URL

校验

实体类添加校验注解

在实体类中需要校验的字段加入响应的注解

1
2
3
4
5
6
7
8
9
10
11
public class Article {
@NotNull(message = "ID不能为空")
private Long id;

@NotBlank(message = "标题不能为空")
private String title;

@NotBlank(message = "内容不能为空")
private String content;
}

在controller层校验

controller层的方法添加@Valid或者@Validated注解

1
2
3
4
@PostMapping("create")
public R create(@RequestBody @Validated Article article){
...
}

捕获校验结果

写一个全局全局捕获异常的行数,捕获表单校验异常的结果,返回给前端

1
2
3
4
5
6
7
8
9

// 捕获表单验证异常
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public R handler(MethodArgumentNotValidException e){
BindingResult bindingResult = e.getBindingResult();
String defaultMessage = Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage();
return R.error().message(defaultMessage);
}

校验分组

在实际开发中经常会遇到这种情况:想要用一个实体类去接收多个controller的参数,但是不同controller所需要的参数又有些许不同,而你又不想为这点不同去建个新的类接收参数。比如有一个create接口不需要id参数,而eidt接口又需要该参数。校验插件提供的groups来实现

定义表示组别的interface

1
2
// 名字随便起
public interface ArticleUpdate {}

在实体类的注解中标记这个哪个组所使用的参数

1
2
3
4
5
6
7
8
9
10
public class Article {
@NotNull(groups = {ArticleUpdate.class}, message = "ID不能为空")
private Long id;

@NotBlank(message = "标题不能为空")
private String title;

@NotBlank(message = "内容不能为空")
private String content;
}

@Validated中指定使用哪个组

/edit接口只对标记了ArticleUpdate的参数校验生效,即上面的id属性

1
2
3
4
@PostMapping("edit")
public R edit(@RequestBody @Validated({ArticleUpdate.class) Dream dream){
...
}

也可以多个分组同时用

Defaultjavax.validation.groups中的类,表示没有分组的参数,本文中对应titlecontent

1
2
3
4
@PostMapping("edit")
public R edit(@RequestBody @Validated({ArticleUpdate.class, Default.class}) Dream dream){
...
}

手动校验

如果要在其他地方校验,例如在 service 层校验,就不能用 controller 层添加注解的方式了。要手动触发校验,您可以使用javax.validation.Validator接口的实现类,例如Hibernate Validator。

在Spring Boot中使用Hibernate Validator进行手动校验的步骤如下:

  1. 在您的项目中添加Hibernate Validator依赖:
1
2
3
4
5
6
phpCopy code
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.0.Final</version>
</dependency>
  1. 在需要校验的地方注入javax.validation.Validator实例:
1
2
3
4
5
6
7
8
9
10

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
private final Validator validator;

public UserServiceImpl(Validator validator) {
this.validator = validator;
}
...
}
  1. 在您的控制器方法中,使用validator.validate()方法手动触发校验。例如:
1
2
3
4
5
6
7
8
9

@Override
public void updateUser(UserUpdateDTO userData) {{
Set<ConstraintViolation<UserUpdateDTO>> violations = validator.validate(userData);
if (!violations.isEmpty()) {
// 校验失败,处理错误
}
...
}

在这个例子中,validator.validate(request)方法返回一个包含所有约束违反的集合,您可以对这个集合进行处理来处理校验错误。如果校验通过,集合将为空。

请注意,手动触发校验时,您还可以使用BindingResult对象来收集校验错误。BindingResult对象将包含校验结果,而不会抛出异常,因此您可以在代码中检查这些错误并做出响应。

改进代码

我们封装一个校验工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.zdong.dream.utils;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.Validator;
import java.util.Set;
/**
* @author : zdong
* @date : Created in 2023/2/22 16:50
* @description :hibernate validator的校验工具
*/


public class ValidateUtil {
private static final Validator validator =
Validation.buildDefaultValidatorFactory().getValidator();

/**
* 校验实体类
*/
public static <T> void validate(T t) {
Set<ConstraintViolation<T>> constraintViolations = validator.validate(t);
if (constraintViolations.size() > 0) {
StringBuilder validateError = new StringBuilder();
for (ConstraintViolation<T> constraintViolation : constraintViolations) {
validateError.append(constraintViolation.getMessage()).append(";");
}

throw new ValidationException(validateError.toString());
}
}

/**
* 通过组来校验实体类
*/
public static <T> void validate(T t, Class<?>... groups) {
Set<ConstraintViolation<T>> constraintViolations = validator.validate(t, groups);
if (constraintViolations.size() > 0) {
StringBuilder validateError = new StringBuilder();
for (ConstraintViolation<T> constraintViolation : constraintViolations) {
validateError.append(constraintViolation.getMessage()).append(";");
}

throw new ValidationException(validateError.toString());
}
}
}

全局捕获异常。

1
2
3
4
5
6
7
8
/**
* hibernate validator的手动校验工具抛出的异常
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ValidationException.class)
public R handler(ValidationException e){
return R.error().message(e.getMessage()).code(CodeEnum.VALIDATE.getCode());
}

在需要校验的地方引入工具类

1
2
3
4
5
6
@Override
public void updateUser(UserUpdateDTO userData) {
// 如果校验失败会自动捕获异常。
ValidateUtil.validate(userUpdateDTO);
...
}

手动校验同样支持分组

1
2
3
4
5
6
@Override
public void updateUser(UserUpdateDTO userData) {
// 如果校验失败会自动捕获异常。
ValidateUtil.validate(userUpdateDTO, ArticleUpdate.class);
...
}