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

时间:2020-02-11 11:35:49

标签: java spring-boot validation bean-validation

尽管已回答了这个问题,但我很感兴趣,为什么@Validated的工作级联验证需要Map<String, @Valid Employee>

更新2 :为了更深入地了解,我发现了这些帖子(OneTwoThree),它们说明了{{1 }}需要激活方法级别验证。借助此方法,可以验证集合,因为它们不是经过验证的JavaBean(JSR 303)。


解决方案:我已经使用有效的代码示例更新了代码段和存储库。我要做的就是用@Validated注释我的控制器,并在@Validated中添加一些吸气剂。完全不需要Employee

更新:我已经更新了问题,并添加了Spring Boot Rest示例以添加一个最小的Rest API来演示:

Github Repo。 示例值在README.md中!


我有一个Spring Boot 2 API来存储一些员工。我可以传递一个MethodValidationPostProcessorEmployee

Map<String, Employee>

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) {
     ...
  }
}

目前,可以验证单个请求,但不能验证我的批量请求。据我所知,使用Bean验证2.0应该可以做到这一点。

您知道我做错了吗?我需要编写自定义验证器吗?

2 个答案:

答案 0 :(得分:2)

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

MethodValidationPostProcessor bean添加到配置

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

@Validated添加到您的EmployeeController

@Validated
@RestController
public class EmployeeController {}'

@Valid添加到MapEmployee

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();
    }

}

希望它对您有帮助。

答案 1 :(得分:1)

spring系统中有两种验证方式。

  • A:spring boot控制器方法参数验证仅适用于控制器中的http发帖请求正文数据,并且不带@Valid@Validated
  • B:方法级别验证可用于任何方法参数,并且返回值带有类上的@Validated@Valid并保留待验证的值

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

1个答案在代码中

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

  • 通过validateValidatorImpl中调用RequestResponseBodyMethodProcessor方法
  • 通过validateParameters调用ValidatorImpl中的MethodValidationInterceptor方法

它们是具有不同功能的不同方法,因此导致不同的结果。您可以通过阅读两种方法找到答案。

2个答案在规范中

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

validate方法在validation method部分中进行了说明,实现必须遵守validation routine中定义的逻辑,在该逻辑中,声明将对所有可到达的对象执行所有约束验证。 List对象(或其他集合实例)的元素无法通过此方法验证的原因-集合的元素不是集合实例的字段。

但是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框架人员在validate中调用RequestResponseBodyMethodProcessor,使很多相关问题出现在stackoverflow中。也许只是因为http post正文数据通常是表单数据,并且可以自然地由java bean表示。如果是我,我会在validateParametes中呼叫RequestResponseBodyMethodProcessor,以方便使用。