使用 Spring Validator 接口进行数据校验
1、概览
Spring Validator
接口为验证对象提供了一种灵活且可定制的方式。本文将带你了解如何在 Spring 应用中使用 Validator
接口验证对象。
2、Spring Validator 接口
Validator
接口是 Spring 框架的一部分,它提供了一种验证对象的方法。
这是一个简单的接口,定义了两个方法:supports()
和 validate()
,用于确定 Validator
是否能验证对象并执行验证逻辑。
2.1、supports(Class clazz) 方法
Validator
接口中的 supports()
方法决定 Validator
能否验证特定类的实例。该方法的参数 Class<?> clazz
代表被验证对象的类。它是一个泛型类(Class<?>
),可以灵活用于不同的对象类型。
具体来说,Spring 利用 isAssignableFrom()
方法来检查对象是否可以合法地转换为 Validator
支持类的对象。因此,如果 Validator
能处理所提供的 clazz
对象,它就会返回 true
,否则就会返回 false
,以表示应使用另一个 Validator
。
@Override
public boolean supports(Class<?> clazz) {
return User.class.isAssignableFrom(clazz);
}
在上例中,Validator
配置为仅支持验证类型为 User
或其子类的对象。isAssignableFrom()
方法通过继来承验证兼容性 - 对于 User
及其子类,它返回 true
,对于任何其他类类型,它返回 false
。
2.2、validate(Object target, Errors errors) 方法
validate()
方法则用于为 Validator
支持的对象定义自定义验证逻辑。
该方法接收两个关键参数:
- Object target:该参数代表要验证的实际对象。Spring MVC 会自动将要验证的对象传递给此方法。
- Errors:该参数是
Errors
接口的一个实例。它提供了各种方法,用于添加对象验证错误的信息。
下面是一个 validate()
方法的示例:
@Override
public void validate(Object target, Errors errors) {
User user = (User) target;
if (StringUtils.isEmpty(user.getName())) {
errors.rejectValue("name", "name.required", "Name cannot be empty");
}
}
在这个示例中,validate()
方法对 User
对象执行各种验证,并使用 rejectValue()
将特定的错误信息添加到 Errors
对象中,以处理目标字段的错误。值得注意的是,rejectValue()
方法需要三个主要参数:
- field:出错字段的名称,如
name
(姓名)。 - errorCode:可识别错误的唯一代码,如
name.required
(名称必填)。 - defaultMessage:默认错误信息,在找不到其他信息时显示,例如 “名称不能为空”。
3、实现 Validator
要创建一个 Validator
,我们需要实现 Validator
接口。下面是一个验证 User
对象的简单 Validator
示例:
public class UserValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return User.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
User user = (User) target;
if (StringUtils.isEmpty(user.getName())) {
errors.rejectValue("name", "name.required", "Name cannot be empty");
}
if (StringUtils.isEmpty(user.getEmail())) {
errors.rejectValue("email", "email.required", "Invalid email format");
}
}
}
3.1、创建 User 类
定义要验证的对象,以 User
类为例:
public class User {
private String name;
private String email;
// Get/Set 方法省略
}
3.2、配置 Spring Bean
接下来,为了将自定义 Validator
集成到基于 Spring 的应用中,我们可以使用 Spring 配置(Configuration
)类将其注册为 Application Context 中的一个 Bean。这种注册可确保 Validator
在整个应用生命周期中都可用于依赖注入(DI):
@Configuration
public class AppConfig implements WebMvcConfigurer{
@Bean
public UserValidator userValidator() {
return new UserValidator();
}
}
通过在 userValidator()
方法上使用 @Bean
注解,可以确保它返回的对象会被 Spring 注册为 Application Context 中的一个 Bean。
3.3、在 Spring MVC Controller 中整合 Validator
Validator
注册后,就可以用它来验证 Spring MVC Controller 中的 User
对象。
接下来,创建一个 UserController
来处理与用户(User
)相关的请求:
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserValidator userValidator;
@PostMapping
public ResponseEntity<?> createUser(@RequestBody User user) {
Errors errors = new BeanPropertyBindingResult(user, "user");
userValidator.validate(user, errors);
if (errors.hasErrors()) {
return ResponseEntity.badRequest().body(errors.getAllErrors());
}
// TODO 保存对象到数据库
return ResponseEntity.ok("User created successfully!");
}
}
在上例中,使用 Spring 的 @RestController
注解来表示该 Controller 返回 JSON 响应。此外,还使用 @RequestBody
将传入的 JSON 请求体与 User
对象绑定。如果验证失败,就返回一个 400 Bad Request 响应,其中的 JSON 响应体包含错误信息。否则,返回一个 200 OK 的成功响应。
4、使用 cURL 进行测试
使用 curl
来测试此 API。
发送包含 User
对象数据的 JSON 请求体:
curl -X POST \
http://localhost:8080/api/users \
-H 'Content-Type: application/json' \
-d '{"name":"","email":""}'
这会返回一个 400 Bad Request 响应,其 JSON 响应体包含错误信息:
[
{
"codes": [
"name.required.user.name",
"name.required.name",
"name.required.java.lang.String",
"name.required"
],
"arguments": null,
"defaultMessage": "Name cannot be empty",
"objectName": "user",
"field": "name",
"rejectedValue": "",
"bindingFailure": false,
"code": "name.required"
},
{
"codes": [
"email.required.user.email",
"email.required.email",
"email.required.java.lang.String",
"email.required"
],
"arguments": null,
"defaultMessage": "Invalid email format",
"objectName": "user",
"field": "email",
"rejectedValue": "",
"bindingFailure": false,
"code": "email.required"
}
]
如果发送一个包含 name
和 email
的有效 User
对象,API 就会返回 200 OK 响应和成功信息:
curl -X POST \
http://localhost:8080/api/users \
-H 'Content-Type: application/json' \
-d '{"name":"John Doe","email":"johndoe@example.com"}'
成功的响应信息如下:
"User created successfully!"
5、Validation Context
此外,在某些情况下,我们可能希望向 Validator
传递额外的上下文。
Spring Validator
接口通过 validate(Object target, Errors errors, Object... validationHints)
方法支 Validation Context。因此,可以在调用 validate()
方法时传递其他对象作为验证提示。
例如,根据特定场景验证 User
对象:
public void validate(Object target, Errors errors, Object... validationHints) {
User user = (User) target;
if (validationHints.length > 0) {
if (validationHints[0] == "create") {
if (StringUtils.isEmpty(user.getName())) {
errors.rejectValue("name", "name.required", "Name cannot be empty");
}
if (StringUtils.isEmpty(user.getEmail())) {
errors.rejectValue("email", "email.required", "Invalid email format");
}
} else if (validationHints[0] == "update") {
// 执行特定的更新验证
if (StringUtils.isEmpty(user.getName()) && StringUtils.isEmpty(user.getEmail())) {
errors.rejectValue("name", "name.or.email.required", "Name or email cannot be empty");
}
}
} else {
// 执行默认验证
}
}
在上例中,UserValidator
会检查 validationHints
数组,以确定使用哪种验证方案。
更新 UserController
以使用带有 validationHints
的 UserValidator
:
@PutMapping("/{id}")
public ResponseEntity<?> updateUser(@PathVariable Long id, @RequestBody User user) {
Errors errors = new BeanPropertyBindingResult(user, "user");
userValidator.validate(user, errors, "update");
if (errors.hasErrors()) {
return ResponseEntity.badRequest().body(errors.getAllErrors());
}
// TODO 更新数据库中的用户对象
return ResponseEntity.ok("User updated successfully!");
}
现在,执行下面的 curl
命令,name
和 email
字段均为空:
curl -X PUT \
http://localhost:8080/api/users/1 \
-H 'Content-Type: application/json' \
-d '{"name":"","email":""}'
UserValidator
会返回带有错误信息的 400 Bad Request 响应:
[
{
"codes": [
"name.or.email.required.user.name",
"name.or.email.required.name",
"name.or.email.required.java.lang.String",
"name.or.email.required"
],
"arguments": null,
"defaultMessage": "Name or email cannot be empty",
"objectName": "user",
"field": "name",
"rejectedValue": "",
"bindingFailure": false,
"code": "name.or.email.required"
}
]
如果只传递其中一个字段,例如 name
字段,那么 UserValidator
就会允许继续进行更新:
curl -X PUT \
http://localhost:8080/api/users/1 \
-H 'Content-Type: application/json' \
-d '{"name":"John Doe"}'
响应是 200 OK,表明更新成功:
"User updated successfully!"
6、总结
本文介绍了如何在 Spring 应用中使用 Validator
接口来验证对象,主要介绍了 Validator
接口的两个方法:supports()
和 validate()
,以及如何实现自定义 Validator
来验证对象。
Ref:https://www.baeldung.com/spring-validator-interface