Validation - Spring Boot项目中参数检验的利器
Validation - Spring Boot项目中参数检验的利器
什么是Validation
Sping Boot官方原文:
When it comes to validating user input, Spring Boot provides strong support for this common, yet critical, task straight out of the box.
Although Spring Boot supports seamless integration with custom validators, the de-facto standard for performing validation is Hibernate Validator, the Bean Validation framework’s reference implementation.
Validation是Spring Boot官方提供的一个参数检验的工具,通常负责验证用户输入,也就是在从前端获取请求时对请求中包含的信息参数进行检验,避免了进入后端检验这个流程的耗时与逻辑编写
为什么要使用Validation
Validation在Spring Boot架构的分布式项目当中比较常见,其优点我总结为:
- 与后端逻辑分离,做到了解耦
- 使用简单,只需要在需要进行参数检验的参数(DTO层)上增加一个注释即可,不需要编写大量接口和实现代码
- 官方支持,使用稳定
接下来我们将通过前两个角度来看看Validation是怎样简单上手使用的,我将使用我的学习项目作为示例
如何使用Validation
在一种情况下,比如我们设计数据库表的时候,往往会有一些字段包含实际意义,比如邮箱或电话号码,这些字段与分配的随机id不同,在现实中有一定的规范;另一种情况下,比如我们的用户设置初始密码时,我们也通常会要求用户至少输入6位,有时还会要求至少有一位大写字母等,这时我们就需要对用户输入的数据进行检验,检查这些数据是否符合实际规范或者我们的要求,此时就可以用上Validation
比如在我的项目中,作为一个电商平台,有用户表如下:
public class User {private Long id;private String username;private String password;private String phone;private String email;private Date registerTime;private Integer status;private String realName;private String address;
}
电商平台会出现用户注册的场景,于是我们就要定义用户注册时返回的数据的DTO类
public class UserRegisterDTO {@NotBlank(message = "用户名不能为空")private String username;@NotBlank(message = "密码不能为空")@Size(message = "密码长度至少为6", min = 6)private String password;@NotBlank(message = "电话号码不能为空")@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")private String phone;@NotBlank(message = "邮箱不能为空")@Email(message = "邮箱格式不正确")private String email;
}
在注册时,我们就需要规范用户输入的这些信息,所有信息都是必要的不能为空,同时密码有长度限制,电话号码有其固定格式,邮箱同理,我们在这里就可以通过Validation的注释来告诉系统,在把用户返回的这些数据包装成对应的DTO类时,需要进行检查
再具体一点,我们怎么通知系统呢
首先我们需要添加依赖,比如我的项目中:
<dependency><groupId>jakarta.validation</groupId><artifactId>jakarta.validation-api</artifactId><version>3.1.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
我做的是一个微服务项目,目前上述的代码都位于用户模块,因此这个依赖我也是添加在我用户模块的子pom中,有了这个依赖,我们就可以开始使用Validation
接着我们需要在对应的API中声明,告诉系统我们在处理这个请求之前需要对其参数进行检验,比如我注册的Restful API:
// 注册用户@PostMapping("/register")public Result<Boolean> register(@Valid @RequestBody UserRegisterDTO dto) {return userService.register(dto) ?Result.ok(true) :Result.fail("Register Failed");}
只有在这里指明了@Valid这个注释,系统才会对其进行Validation相关处理
接着我们就要进一步对参数进行注释,正如我之前给出的代码,在DTO层对应的参数前添加相应的注释即可,一般来说,validation注释有以下几种:
- @AssertFalse:被注解的元素必须为false
- @AssertTrue:被注解的元素必须为true
- @DecimalMax(value):被注解的元素必须是一个数字,其值必须小于等于指定的最大值
- @DecimalMin(value):被注解的元素必须是一个数字,其值必须大于等于指定的最小值
- @Digits(integer, fraction):被注解的元素必须是一个数字,其值必须在可接受的范围内
- @Future:被注解的元素必须是一个将来的日期
- @Max(value):被注解的元素必须是一个数字,其值必须小于等于指定的最大值
- @Min(value):被注解的元素必须是一个数字,其值必须大于等于指定的最小值
- @NotNull:被注解的元素必须不为null
- @Null:被注解的元素必须为null
- @Past:被注解的元素必须是一个过去的日期
- @Pattern(regex, flag):被注解的元素必须符合指定的正则表达式
- @Size(min, max):被注解的元素的大小必须在指定的范围内
- @Email:被注解的元素必须是电子邮件地址
- @NotEmpty:被注解的元素不能为null,并且长度必须大于0
- @NotBlank:被注解的元素为字符串,并且被trim()以后length要大于0
- @Range(min, max):被注解的元素必须在合适的范围内
- @URL:被注解的元素必须是一个有效的URL
在元素之前添加相应的注释,项目启动后,把用户返回的参数包装成DTO类对象时就会进行检查
但是只检查出来了,最多也就会返回400的错误代码,前端只是了解到有错误,但是无法对错误进行定位,于是此时我们就需要进一步处理
自定义Result类,用来统一管理前端请求的处理结果
public class Result<T> {private Integer code;private String msg;private T data;public static <T> Result<T> ok(T data) {Result<T> r = new Result<>();r.setCode(200);r.setMsg("success");r.setData(data);return r;}public static <T> Result<T> fail(String msg) {Result<T> r = new Result<>();r.setCode(500);r.setMsg(msg);return r;}
}
自定义异常处理类,用于对运行时错误进行处理
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(MethodArgumentNotValidException.class)public Result<?> handleValidationException(MethodArgumentNotValidException ex) {String errorMsg = ex.getBindingResult().getFieldError().getDefaultMessage();return Result.fail(errorMsg);}
}
有了这两段代码,项目启动后就有了自定义的对运行时错误进行定位处理的能力,比如用户注册时输入的初始密码小于6位,通过Validation注释检查出了这一错误,然后汇报给GlobalExceptionHandler类,它就将错误信息包装,并最终包装成Result类,返回给前端,这样前端就能看见具体的错误信息,比如我测试如下:
我在Postman中进行用户注册测试,这是注册的body json部分:
{"username": "testValid6","password": "five","email": "12345678","phone": "12345678"
}
最后返回了结果:
{"timestamp": "2025-07-19T04:59:28.091+00:00","status": 400,"error": "Bad Request","trace": 略"message": "Validation failed for object='userRegisterDTO'. Error count: 3","errors": [{"codes": ["Email.userRegisterDTO.email","Email.email","Email.java.lang.String","Email"],"arguments": [{"codes": ["userRegisterDTO.email","email"],"arguments": null,"defaultMessage": "email","code": "email"},[],{"arguments": null,"defaultMessage": ".*","codes": [".*"]}],"defaultMessage": "邮箱格式不正确","objectName": "userRegisterDTO","field": "email","rejectedValue": "12345678","bindingFailure": false,"code": "Email"},{"codes": ["Pattern.userRegisterDTO.phone","Pattern.phone","Pattern.java.lang.String","Pattern"],"arguments": [{"codes": ["userRegisterDTO.phone","phone"],"arguments": null,"defaultMessage": "phone","code": "phone"},[],{"arguments": null,"defaultMessage": "^1[3-9]\\d{9}$","codes": ["^1[3-9]\\d{9}$"]}],"defaultMessage": "手机号格式不正确","objectName": "userRegisterDTO","field": "phone","rejectedValue": "12345678","bindingFailure": false,"code": "Pattern"},{"codes": ["Size.userRegisterDTO.password","Size.password","Size.java.lang.String","Size"],"arguments": [{"codes": ["userRegisterDTO.password","password"],"arguments": null,"defaultMessage": "password","code": "password"},2147483647,6],"defaultMessage": "密码长度至少为6","objectName": "userRegisterDTO","field": "password","rejectedValue": "five","bindingFailure": false,"code": "Size"}],"path": "/user/register"
}
可以看出,这里按照DTO设计的参数的顺序,通通给出了错误的信息,这样就能够精准地定位错误,前端也就可以对其做相应的处理
总结
整体来说,Validation将原先需要一个API一个API编写的参数检验逻辑简化至如此方便,可以说是Spring Boot项目中官方提供的一个参数检验的利器了,对参数标注只需要注释,错误处理可以统一编写错误处理类,前端也可以清晰地看见具体的错误,能够极大地简化这部分逻辑的开发,省去繁琐的重复性工作