1. 概述

Spring的Validator接口提供了一种灵活且可定制的方式来验证对象。本文将深入探讨如何在Spring应用中使用Validator接口实现对象校验。

2. Spring Validator接口详解

Validator接口是Spring框架的一部分,专门用于对象校验。这个接口定义了两个核心方法:supports()validate()这两个方法分别用于判断校验器是否支持特定对象类型,以及执行具体的校验逻辑

2.1. supports(Class<?> clazz)

supports()方法用于确定校验器是否能验证特定类的实例。该方法接收一个Class<?> clazz参数,表示待验证对象的类类型。使用泛型Class<?>是为了支持不同类型的对象。

Spring内部使用isAssignableFrom()方法检查对象是否可以合法转换为校验器支持的类类型。如果校验器能处理提供的clazz类型,则返回true,否则返回false表示需要使用其他校验器:

@Override
public boolean supports(Class<?> clazz) {
    return User.class.isAssignableFrom(clazz);
}

此示例中,校验器仅配置为支持User类及其子类的验证。**isAssignableFrom()方法通过继承关系验证兼容性——对User及其子类返回true,对其他类类型返回false**。

2.2. validate(Object target, Errors errors)

validate()方法在Spring校验框架中扮演关键角色。这是定义校验器支持对象的自定义校验逻辑的地方

该方法接收两个核心参数:

  • Object target:待验证的实际对象。Spring MVC会自动将待验证对象传递给此方法
  • ErrorsErrors接口的实例,提供多种方法向对象添加校验错误。

以下是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:未找到其他消息时显示的默认错误消息,如"Name cannot be empty"

3. 实现自定义Validator

创建校验器需要实现Validator接口。以下是一个验证User对象的简单校验器示例:

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;

    // Getters and Setters
}

3.2. 配置Spring Bean

要将自定义校验器集成到Spring应用中,可通过Spring配置类将其注册为应用上下文中的bean。此注册确保校验器在整个应用生命周期中可用于依赖注入

@Configuration
public class AppConfig implements WebMvcConfigurer{
    @Bean
    public UserValidator userValidator() {
        return new UserValidator();
    }
}

通过在userValidator()方法上添加@Bean注解,确保Spring将其返回对象注册为应用上下文中的bean。

3.3. 在Spring MVC控制器中集成Validator

注册校验器后,可在Spring MVC控制器中使用它验证User对象。

创建UserController处理用户相关请求:

@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());
        }
        // Save the user object to the database
        return ResponseEntity.ok("User created successfully!");
    }
}

此示例中,使用@RestController注解表示控制器返回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":"john.doe@example.com"}'

请求返回成功消息:

"User created successfully!"

5. 校验上下文

某些场景下,可能需要向校验器传递额外上下文。Spring的Validator接口通过validate(Object target, Errors errors, Object… validationHints)方法支持校验上下文。使用校验上下文时,可在调用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以使用带validationHintsUserValidator

@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());
    }
    // 更新数据库中的用户对象
    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接口提供灵活的对象校验机制
  • supports()方法确定校验器支持的类类型
  • validate()方法实现具体校验逻辑
  • 可通过校验上下文实现场景化校验
  • 集成到Spring MVC控制器中实现请求校验

示例源码可在GitHub获取。


原始标题:Validation Using the Spring Validator Interface | Baeldung