在 Spring 中校验 List 参数

1、概览

在 Spring 中,可以通过内置工具和注解简化数据验证,使我们能够轻松实现强大的验证逻辑。

本文将带你了解如何在 Spring 中验证 List 类型的参数值。

2、在 Spring 中配置 Validation

要启用验证,需要在 pom.xml 中添加以下 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

该依赖会自动将 Hibernate Validator 整合到我们的 Spring 应用程序中。

3、验证 List 中的每个元素

假设我们需要验证员工角色列表。每个角色都应以 ROLE_ 开头。

首先在 Employee DTO 类中添加一个 roles 变量:

@NotEmpty(message = "Roles list cannot be empty")
@Valid
private List<@Pattern(regexp = "ROLE_[A-Z]+", 
  message = "Each role must start with 'ROLE_' and contain uppercase letters only") String> roles;

@NotEmpty 注解确保列表不是空的,而 @Pattern 注解则验证每个角色都符合指定的格式(正则)。

下一步是在 Controller 类中添加一个方法,以便触发验证:

@PostMapping("/validateListAtService")
public ResponseEntity<String> validateRoles(@RequestBody @Validated Employee request) {
    return ResponseEntity.ok("Roles are valid!");
}

如上,@Validated 注解告诉 Spring 要对 Employee 对象执行验证。

最后,可以定义有一个全局异常处理器来处理验证错误:

@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleValidationException(MethodArgumentNotValidException ex) {
    return ex.getBindingResult()
      .getFieldErrors()
      .stream()
      .map(e -> e.getDefaultMessage())
      .collect(Collectors.joining(","));
}

解释如下:

  • @ExceptionHandler 注解指定了方法处理的异常类型。在本例中,它处理的是 MethodArgumentNotValidException,该异常会在对注有 @Valid@Validated 的方法参数的验证失败时抛出
  • @ResponseStatus 设置响应的 HTTP 状态代码。在这里,它会发送 400 BAD REQUEST 状态,表明客户端提供了无效数据
  • MethodArgumentNotValidException 异常会提供有关验证错误的详细信息,包括验证失败的字段和相关错误信息

现在,来看看可能出现验证错误的一种情况。比方说,客户端发送的请求中包含无效数据:

{
    "roles": ["admin", "ROLE_ADMIN,", ""]
}

由于验证规则规定,角色必须以 ROLE_ 开头,且不能为空,因此响应如下:

Each role must start with 'ROLE_' and contain uppercase letters only

全局异常处理器的主要优点是,它能确保对所有验证错误进行一致的处理。它能向客户端提供用户友好的反馈,而且无需在单个 Controller 中使用错误处理逻辑,从而减少了模板。

4、枚举值验证

再来考虑另一种情况,即我们想验证特定员工的各个部门。

要实现这一功能,我们要创建一个自定义注解,验证输入的部门列表是否有效,即是否属于预定义列表的一部分。

首先,创建一个自定义 Validator 注解:

@Constraint(validatedBy = AllowedValuesValidator.class)
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface AllowedValues {
    String message() default "Invalid value";
    String[] values();
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

如上:

  • @Constraint:指定负责验证注解逻辑的类(AllowedValuesValidator
  • @Target:限制此注解的应用范围(如字段或方法参数)
  • @Retention:确保注解在运行时可用于验证
  • values:接受一组预定义的允许验证值
  • message:验证失败时的自定义错误信息
  • groupspayload:可选的分组限制和自定义元数据属性

下一步是实现 Validator:

public class AllowedValuesValidator implements ConstraintValidator<AllowedValues, List<String>> {
    private List<String> allowedValues;

    @Override
    public void initialize(AllowedValues constraintAnnotation) 
    {
        allowedValues = Arrays.asList(constraintAnnotation.values());
    }

    @Override
    public boolean isValid(List<String> values, ConstraintValidatorContext context) {
        return values != null && values.stream()
          .allMatch(allowedValues::contains);
    }
}

initialize() 方法会检索 @AllowedValues 注解中指定的允许值并将其存储在一个列表中,如果值列表中的所有元素都在 allowedValues 列表中,isValid() 方法就会返回 true。该方法还能优雅地处理 null 输入,返回 false

最后一步是将我们的自定义注解应用到代表各种预定义部门的列表中:

@NotEmpty(message = "Departments list cannot be empty")
@AllowedValues(values = {"Management", "Software Development", "DevOps", "Architect"}, message = "Invalid department provided")
private List<String> department;

@NotEmpty 注解确保列表不是空的。我们的自定义注解 @AllowedValues 指定了 ManagementSoftware DevelopmentDevOpsArchitect 等有效角色,如果验证失败,则会返回一条自定义错误消息,说明提供的部门无效。

5、总结

本文介绍了如何在 Spring 中验证 List,包括只允许使用特定值的情况。


Ref:https://www.baeldung.com/java-spring-validate-value-list