简体   繁体   English

级联 bean 验证 2.0 不适用于 object 内部的嵌套 Map

[英]Cascaded bean validation 2.0 not working with nested object inside Map

Although, this question has been answered I'm interested why @Validated is needed for a working cascaded validation of Map<String, @Valid Employee> .虽然,这个问题已经得到回答,但我很感兴趣为什么需要@Validated来进行Map<String, @Valid Employee>的工作级联验证。

Update 2 : For some deeper understanding I've found those posts ( One , Two and Three ), which explains, that @Validated is neeeded to activate method level validation.更新 2 :为了更深入地了解,我找到了这些帖子(),这解释了@Validated来激活方法级别验证。 With the help of this, collections can be validated, due they are no JavaBeans which are validated instead (JSR 303).借助于此,可以验证 collections,因为它们不是经过验证的 JavaBeans (JSR 303)。


Solution : I've updated my code snippets and my repository with working code examples.解决方案:我已经用工作代码示例更新了我的代码片段和我的存储库。 All I have to do is to annotate my controller with @Validated and add some getters in Employee .我所要做的就是用 @Validated 注释我的@Validated并在Employee中添加一些吸气剂。 MethodValidationPostProcessor is not necessary at all. MethodValidationPostProcessor根本不是必需的。

Update : I've updated my question and forked Spring Boot Rest example to add a minimal Rest API to demonstrate:更新:我更新了我的问题并分叉 Spring Boot Rest 示例以添加最小的 Rest API 来演示:

Github Repo . Github 回购 The example values are inside README.md!示例值在 README.md 中!


I've got an Spring Boot 2 API to store some employees.我有一个 Spring Boot 2 API 来存放一些员工。 I can pass either one Employee or either a Map<String, Employee> .我可以传递一个EmployeeMap<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) {
     ...
  }
}

Employee exists of some inner static classes which also needs to be validated: Employee 存在一些内部 static 类也需要验证:

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


For now, validation for single requests are working but not for my bulk requests.目前,对单个请求的验证有效,但对我的批量请求无效。 As far as i know this should be possible with Bean validation 2.0.据我所知,这应该可以通过 Bean 验证 2.0 实现。

Do you know what I've did wrong?你知道我做错了什么吗? Do i need to write a custom validator?我需要编写自定义验证程序吗?

To make it working you have to do following:要使其正常工作,您必须执行以下操作:

Add MethodValidationPostProcessor bean to configurationMethodValidationPostProcessor bean 添加到配置中

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

Add @Validated to your EmployeeController@Validated添加到您的EmployeeController

@Validated
@RestController
public class EmployeeController {}'

Add @Valid to Map or to Employee@Valid添加到MapEmployee

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

That's all.就这样。 This is entire EmployeeController :这是整个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());
    }
}

And SpringBoot configuration file和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();
    }

}

Hope it help you.希望对你有帮助。

There are two kinds of validation in spring system. spring系统中有两种验证方式。

  • A: The spring boot controller methods parameter validation, only works for the http post request body data in controller with @Valid or @Validated aside A: spring boot controller methods parameter validation, 只对controller中的@Valid post request body data有效,@Valid or @Validated aside
  • B: The method level validation, works for any method parameters and return values with @Validated on class and @Valid aside values to be validated B:方法级别验证,适用于任何方法参数和返回值@Validated on class 和@Valid aside values to be validated

We can see that A is more narrow while B is a more common one.我们可以看到 A 更窄,而 B 更常见。 I'd like to answer the question on two aspects.我想从两个方面回答这个问题。

1 Answers are in the code 1 答案在代码中

As describe in this post , the more detail part, A and B triggers method enhancement via aop by calling different method in org.hibernate.validator.internal.engine.ValidatorImpl , which leads to the difference.本文所述, more detail的部分,A 和 B 通过调用org.hibernate.validator.internal.engine.ValidatorImpl中的不同方法通过 aop 触发方法增强,这导致了差异。

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

They are different methods with different functions, so lead to different results.它们是具有不同功能的不同方法,因此导致不同的结果。 You can find the answer by reading the two methods.通过阅读这两种方法,您可以找到答案。

2 Answers are in the specification 2 答案在规范中

The JSR-303 defines functions of the methods we discussed above. JSR-303定义了我们上面讨论的方法的功能。

validate method is explained in the validation method part, and the implementation must obey the logic defined in validation routine , in which it states that it will execute all the constraint validation for all reachable fields of the object, this is why element of List object (or other collection instance) cannot be validated via this method - the elements of the collection are not fields of the collection instance. validate 方法部分解释了validate方法,其实现必须遵循验证例程中定义的逻辑,其中声明将对object的所有可达字段执行所有约束验证,这就是为什么List object的元素(或其他集合实例)无法通过此方法验证 - 集合的元素不是集合实例的字段。

But validateParameters , JSR-303 actually doesn't treat it as main topic and put it in Appendix C. Proposal for method-level validation .但是validateParameters ,JSR-303 其实并没有把它作为主要话题,而是放在了Appendix C. Proposal for method-level validation中。 It provides some description:它提供了一些描述:

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

and exclaim that Bean Validation providers are free to implement this proposal as a specific extension .并声明Bean Validation providers are free to implement this proposal as a specific extension As far as I know, the Hibernate Validation project implements this method, makes constraints works on the object itself, and element of collection object.据我所知, Hibernate Validation项目实现了此方法,使约束适用于 object 本身和集合 object 的元素。

3 Some complain 3 有人抱怨

I don't know why the spring framework guys call validate in RequestResponseBodyMethodProcessor , makes lots of related questions appeare in stackoverflow.我不知道为什么 spring 框架人员在RequestResponseBodyMethodProcessor中调用validate ,使得很多相关问题出现在 stackoverflow 中。 Maybe it's just because http post body data usually is a form data, and can be represented by a java bean naturally.也许只是因为 http post body 数据通常是表单数据,自然可以用 java bean 表示。 If it's me, I'll call the validateParametes in RequestResponseBodyMethodProcessor for easy use.如果是我,我会调用RequestResponseBodyMethodProcessor中的validateParametes ,方便使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM