级联 bean 验证 2.0 不适用于 Map 中的嵌套对象

Cascaded bean validation 2.0 not working with nested object inside Map

不过,这个问题已经得到解答我很感兴趣为什么 @Validated 需要 Map<String, @Valid Employee>. [=22= 的工作级联验证]

更新 2:为了更深入的理解,我找到了那些帖子 (,Two and Three),这解释了需要 @Validated 来激活方法水平验证。借助于此,可以验证集合,因为它们不是经过验证的 JavaBeans (JSR 303)。


解决方案:我已经用工作代码示例更新了我的代码片段和我的存储库。我所要做的就是用 @Validated 注释我的控制器并在 Employee 中添加一些 getter。 MethodValidationPostProcessor 完全没有必要。

更新:我更新了我的问题并分叉了 Spring Boot Rest 示例以添加最小 Rest API 来演示:

Github Repo。 示例值在 README.md!


我有一个 Spring Boot 2 API 来存放一些员工。我可以传递一个 Employee 或一个 Map<String, Employee>.

@Validated //this is the solution to activate map validation
@RestController
class EmployeeController {

  @PostMapping("/employees")
  List<Employee> newEmployee(@RequestBody @Valid Employee newEmployee) {
     ...
  }

  @PostMapping("/employees/bulk")
  List<Employee> newEmployee(@RequestBody Map<String, @Valid Employee> 
  newEmployees) {
     ...
  }
}

员工存在一些内部静态 类 也需要验证:

public class Employee {

    @NotBlank
    public final String name;
    @Valid
    public final EmployeeRole role;

    @JsonCreator
    public Employee(@JsonProperty("name") String name,
        @JsonProperty("role") EmployeeRole role) {

        this.name = name;
        this.role = role;
    }

    // getters

    public static class EmployeeRole {

        @NotBlank
        public String rolename;

        @Min(0)
        public int rating;

        @JsonCreator
        public EmployeeRole(@JsonProperty("rolename") String rolename,
            @JsonProperty("rating") int rating) {

            this.rolename = rolename;
            this.rating = rating;
        }

        // getters
    }
}


目前,单个请求的验证有效,但我的批量请求无效。据我所知,这应该可以通过 Bean 验证 2.0 实现。

你知道我做错了什么吗?我需要编写自定义验证程序吗?

要使其正常工作,您必须执行以下操作:

Add MethodValidationPostProcessor bean to configuration

@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
    return new MethodValidationPostProcessor();
}

Add @Validated to your EmployeeController

@Validated
@RestController
public class EmployeeController {}'

Add @Valid to Map or to Employee

public List<Employee> newEmployee(@RequestBody @Valid Map<String, Employee> newEmployees) {}   
public List<Employee> newEmployee(@RequestBody Map<String, @Valid Employee> newEmployees) {}

就是这样。这是整个 EmployeeController:

@Validated
@RestController
public class EmployeeController {

    @PostMapping("/employees")
    public List<Employee> newEmployee(@RequestBody @Valid Employee newEmployee) {
        return Collections.singletonList(newEmployee);
    }

    @PostMapping("/employees/bulk")
    public List<Employee> newEmployee(@RequestBody @Valid Map<String, Employee> newEmployees) {
        return new ArrayList<>(newEmployees.values());
    }
}

和SpringBoot配置文件

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }

}

希望对你有帮助。

spring系统中有两种验证。

  • A: spring boot controller methods parameter validation, only works for the http post request body data in controller with @Valid or @Validated aside
  • B:方法级别验证,适用于任何方法参数和 return 值 @Validated on class 和 @Valid 除了要验证的值

我们可以看到A更窄,B更常见。我想从两个方面回答这个问题。

1 答案在代码中

this post中所述,more detail部分,A和B通过调用不同的方法通过aop触发方法增强 org.hibernate.validator.internal.engine.ValidatorImpl,这就导致了差异。

  • 通过RequestResponseBodyMethodProcessor
  • 调用ValidatorImpl中的validate方法
  • B call 通过 MethodValidationInterceptor
  • 调用 ValidatorImpl 中的 validateParameters 方法

它们是不同的方法,具有不同的功能,所以会导致不同的结果。看完这两种方法你就可以找到答案了。

2 个答案在规范中

JSR-303 定义了我们上面讨论的方法的功能。

validate方法在validation method part, and the implementation must obey the logic defined in validation routine中有解释,其中声明将对对象的所有可达字段执行所有约束验证,这就是为什么[=24=的元素] 对象(或其他集合实例)无法通过此方法验证 - 集合的元素不是集合实例的字段。

但是validateParameters,JSR-303实际上并没有把它作为主要话题放在Appendix C. Proposal for method-level validation中。它提供了一些描述:

The constraints declarations evaluated are the constraints hosted on the parameters of the method or constructor. If @Valid is placed on a parameter, constraints declared on the object itself are considered.

validateReturnedValue evaluates the constraints hosted on the method itself. If @Valid is placed on the method, the constraints declared on the object itself are considered.

public @NotNull String saveItem(@Valid @NotNull Item item, @Max(23) BigDecimal price)

In the previous example,

- item is validated against @NotNull and all the constraints it hosts
- price is validated against @Max(23)
- the result of saveItem is validated against @NotNull

并惊呼Bean Validation providers are free to implement this proposal as a specific extension。据我所知,Hibernate Validation项目实现了这个方法,使约束作用于对象本身,以及集合对象的元素。

3 一些抱怨

我不知道为什么 spring 框架的家伙在 RequestResponseBodyMethodProcessor 中调用 validate,导致很多相关问题出现在 Whosebug 中。也许只是因为http post body 数据通常是表单数据,自然可以用java bean 来表示。如果是我,我会调用RequestResponseBodyMethodProcessor里的validateParametes方便使用