# spring-validation-demo
**Repository Path**: Ocean_Tang/spring-validation-demo
## Basic Information
- **Project Name**: spring-validation-demo
- **Description**: SpringBootValidation Demo
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 4
- **Forks**: 1
- **Created**: 2022-11-23
- **Last Updated**: 2024-08-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 使用 SpringBoot Validation 对参数进行校验
在开发接口时,如果要对参数进行校验,你会怎么写?编写 `if-else` 吗?虽然也能达到效果,但是不够优雅。
今天,推荐一种更简洁的写法,使用 SpringBoot Validation 对方法参数进行校验,特别是在编写 Controller 层的方法时,直接使用一个注解即可完成参数校验。
示例代码:
## 🚀引入依赖
想要完成上述所说的参数校验,我们需要一个核心依赖:`spring-boot-starter-validation`,此外,为了方便演示,还需要其他依赖。
依赖如下:
```xml
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-validation
org.projectlombok
lombok
true
```
**💡 以下部分不是核心内容:**
你在编写下面的示例代码中,会发现主要使用到了`javax.validation.constraints` 包下的注解,而这个包主要来自于 `jakarta.validation-api` 这个依赖。
如果引入依赖的时候直接引入 `jakarta.validation-api` 是无法实现参数校验功能的,因为它只定义了规范,而没有具体实现。但是 `hibernate-validator` 实现了这个规范,直接引入 `hibernate-validator` 也是可以实现参数校验功能的。
```xml
jakarta.validation
jakarta.validation-api
org.hibernate.validator
hibernate-validator
```
## 🚀 相关注解说明
这里罗列出一些主要的注解,这些注解主要来自于包 `javax.validation.constraints`,有兴趣查看源码的可以去这个包下查看。
可以先跳过这部分内容,下面的代码如果遇到不清楚作用的注解再回来查阅。
### ✈ 空值检查
| 注解 | 说明 |
| :---------: | :----------------------------------------------------------: |
| `@NotBlank` | 用于字符串,字符串不能为`null` 也不能为空字符串 |
| `@NotEmpty` | 字符串同上,对于集合(`Map`,`List`,`Set`)不能为空,必须有元素 |
| `@NotNull` | 不能为 `null` |
| `@Null` | 必须为 `null` |
### ✈ 数值检查
| 注解 | 说明 |
| ---------------------------- | ------------------------------------------------------------ |
| `@DecimalMax(value)` | 被注释的元素必须为数字,其值必须小于等于指定的值 |
| `@DecimalMin(value)` | 被注释的元素必须为数字,其值必须大于等于指定的值 |
| `@Digits(integer, fraction)` | 被注释的元素必须为数字,其值的整数部分精度为 `integer`,小数部分精度为 `fraction` |
| `@Positive` | 被注释的元素必须为正数 |
| `@PositiveOrZero` | 被注释的元素必须为正数或 0 |
| `@Max(value)` | 被注释的元素必须小于等于指定的值 |
| `@Min(value)` | 被注释的元素必须大于等于指定的值 |
| `@Negative` | 被注释的元素必须为负数 |
| `@NegativeOrZero` | 被注释的元素必须为负数或 0 |
### ✈ Boolean 检查
| 注解 | 说明 |
| -------------- | ---------------------------- |
| `@AssertFalse` | 被注释的元素必须值为 `false` |
| `@AssertTrue` | 被注释的元素必须值为 `true` |
### ✈ 长度检查
| 注解 | 说明 |
| ---------------- | ------------------------------------------------------------ |
| `@Size(min,max)` | 被注释的元素长度必须在 `min` 和 `max` 之间,可以是 String、Collection、Map、数组 |
### ✈ 日期检查
| 注解 | 说明 |
| ------------------ | ------------------------------------ |
| `@Future` | 被注释的元素必须是一个将来的日期 |
| `@FutureOrPresent` | 被注释的元素必须是现在或者将来的日期 |
| `@Past` | 被注释的元素必须是一个过去的日期 |
| `@PastOrPresent` | 被注释的元素必须是现在或者过去的日期 |
### ✈ 其他检查
| 注解 | 说明 |
| ------------------ | ------------------------------ |
| `@Email` | 被注释的元素必须是电子邮箱地址 |
| `@Pattern(regexp)` | 被注释的元素必须符合正则表达式 |
> 除此之外,`org.hibernate.validator.constraints` 包下还有其他校验注解,例如 `@ISBN` 检查一个字符串是否是一个有效地 ISBN 序列号。
## 🚀 参数校验
接下来开始体验 Spring Boot Validation。
首先,编写一个需要校验的实体类:
```java
@Data
public class Student {
@NotBlank(message = "主键不能为空")
private String id;
@NotBlank(message = "名字不能为空")
@Size(min=2, max = 4, message = "名字字符长度必须为 2~4个")
private String name;
@Pattern(regexp = "^1(3\\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\\d|9[0-35-9])\\d{8}$", message = "手机号格式错误")
private String phone;
@Email(message = "邮箱格式错误")
private String email;
@Past(message = "生日必须早于当前时间")
private Date birth;
@Min(value = 0, message = "年龄必须为 0~100")
@Max(value = 100, message = "年龄必须为 0~100")
private Integer age;
@PositiveOrZero
private Double score;
}
```
随后编写一个控制层代码,进行测试:
```java
@RestController
public class TestController {
@GetMapping("/test")
public Student test(@RequestBody @Validated Student student) {
return student;
}
}
```
使用 `postman` 进行测试,发送一个不带参数的请求,查看结果:

💡后端控制台日志打印是这样的(显示极度不友好),可以看到**校验规则生效**了:
```bash
2022-11-23 22:10:13.249 WARN 19840 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.example.springvalidationdemo.domain.Student com.example.springvalidationdemo.controller.TestController.test(com.example.springvalidationdemo.domain.Student) with 2 errors: [Field error in object 'student' on field 'name': rejected value [null]; codes [NotBlank.student.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.name,name]; arguments []; default message [name]]; default message [名字不能为空]] [Field error in object 'student' on field 'id': rejected value [null]; codes [NotBlank.student.id,NotBlank.id,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.id,id]; arguments []; default message [id]]; default message [主键不能为空]] ]
```
## 🚀 全局异常处理
查看上面的日志打印,可以看到当参数校验不通过时,会抛出异常 `MethodArgumentNotValidException`,同时也会打印那些**参数没有通过校验,以及该参数校验规则**。
为了方便查看,我们可以编写一个全局异常处理,处理这个**参数校验异常**,并使用统一返回实体返回给前端。
```java
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ResponseEntity