使用 Spring Validator 接口实现验证

Spring Validator接口提供了一种灵活且可自定义的方法来验证对象。在本文中,我们学习如何使用Validator接口在基于 Spring 的应用程序中验证对象。我们探索了Validator接口的两种方法supports()和verify(),以及如何实现自定义验证器来验证对象。

什么是Spring Validator接口
Validator接口是Spring 框架的一部分,它提供了一种验证对象的方法。

它是一个简单的接口,定义了两个方法,supports()和verify()。这两个方法用于确定验证器是否可以验证对象并执行验证逻辑。 

支持(Class clazz)
Validator接口中的 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。

验证(对象目标,错误错误)
另一方面,validate()方法在 Spring 的验证框架中起着至关重要的作用。我们在这里为验证器支持的对象定义自定义验证逻辑。

该方法接收两个关键参数:

  • 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对象执行各种验证,并使用rejectionValue()将特定错误消息添加到Errors对象以识别目标字段错误。值得注意的是,rejectValue()有三个主要参数:
  • field:出现错误的字段名称,例如“ name ”
  • errorCode:标识错误的唯一代码,例如“ name.required ”
  • defaultMessage:如果未找到其他消息,则显示默认错误消息,例如“名称不能为空”
实现验证器
要创建验证器,我们需要实现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");
        }
    }
}

创建用户类
在应用验证之前,必须定义我们要验证的对象的结构。以下是User类的一个示例:

public class User {
    private String name;
    private String email;
    // Getters and Setters
}

配置 Spring Bean
接下来,为了将自定义验证器集成到基于 Spring 的应用程序中,我们可以使用 Spring 配置类将其注册为应用程序上下文中的 bean。此注册可确保验证器在整个应用程序生命周期内可用于依赖项注入:

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

通过用@Bean注释userValidator()方法,我们确保它返回 Spring 在应用程序上下文中注册为 bean 的对象。

在 Spring MVC 控制器中集成验证器
一旦我们注册了验证器,我们就可以使用它来验证Spring MVC 控制器中的用户对象。

接下来我们创建一个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!");
    }
}

在此示例中,我们使用 Spring 的@RestController注释来指示此控制器返回JSON响应。此外,我们使用@RequestBody将传入的 JSON 请求正文绑定到User对象。如果验证失败,我们将返回 400 Bad Request 响应,其 JSON 正文包含错误消息。否则,我们将返回 200 OK 响应,其中包含成功消息。

使用 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"
  }
]

如果我们发送一个带有姓名和电子邮件的有效用户对象,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!"


验证上下文
此外,在某些情况下,我们可能希望将其他上下文传递给验证器。Spring的Validator接口通过validate(Object target, Errors errors, Object…validationHints)方法支持验证上下文。因此,要使用验证上下文,我们可以在调用validate()方法时传递其他对象作为验证提示。

例如,我们想根据特定场景验证用户对象:

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") {
            // Perform update-specific validation
            if (StringUtils.isEmpty(user.getName()) && StringUtils.isEmpty(user.getEmail())) {
                errors.rejectValue("name", "name.or.email.required", "Name or email cannot be empty");
            }
        }
    } else {
        // Perform default validation
    }
}

在此示例中,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());
    }
    // Update the user object in the database
    return ResponseEntity.ok("User updated successfully!");
}

现在,让我们发送以下curl命令,其中名称和电子邮件字段均为空:

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"
  }
]

如果我们只传递其中一个字段,例如名称字段,那么UserValidator允许更新继续:

curl -X PUT \
  http://localhost:8080/api/users/1 \
  -H 'Content-Type: application/json' \
  -d '{"name":"John Doe"}'

响应是200 OK响应,表示更新成功:

"User updated successfully!"