Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/opengl/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Spring MVC和@Validate:仅在特定条件下或用户更改属性时执行验证_Spring_Validation_Spring Mvc - Fatal编程技术网

Spring MVC和@Validate:仅在特定条件下或用户更改属性时执行验证

Spring MVC和@Validate:仅在特定条件下或用户更改属性时执行验证,spring,validation,spring-mvc,Spring,Validation,Spring Mvc,控制器方法需要一个@NotNull@Valid@modeldattribute Person。 个人具有@有效地址属性 在PersonController.create(@NotNull@Valid@modeldattribute Person Person,BindingResult BindingResult…)上,我需要验证Person.address,前提是用户设置了地址的任何字段或基于Person实例的字段值(例如Person.hasAddress=true) 问题在于,默认情况下,s

控制器方法需要一个
@NotNull@Valid@modeldattribute Person
个人
具有
@有效地址
属性

PersonController.create(@NotNull@Valid@modeldattribute Person Person,BindingResult BindingResult…)上,我需要验证Person.address,前提是用户设置了地址的任何字段或基于Person实例的字段值(例如Person.hasAddress=true)

问题在于,默认情况下,spring创建了一个新的Address实例,该实例在createForm提交时提交,在验证时失败

我亲自创建了一个crossproperty验证,它要求地址在hasAddress=true时不为null,但无法解决地址字段中验证的问题

我尝试使用
@InitBinder(“地址”)
/
@InitBinder(“person.address”)
来设置
binder.setAutologownestedPath(false)但我打不到这个电话。全局使用@InitBinder会导致其他属性出现其他问题

我在考虑组,但只有当您在开发时知道是否需要验证时,才可以使用组。在我的情况下,根据地址或hasAddress字段的任何更改,它将在提交时被知道

有什么想法吗?

中也有类似的问题()

我的解决方案的主要思想是动态绑定数据,即逐步有条件地绑定和验证输入数据:

  • 我创建了一个新的注释类
    @BindingGroup
    。它类似于验证约束注释的groups参数。在我的解决方案中,您可以使用它来指定一组没有验证约束的字段

  • 我创建了一个名为GroupAwareDataBinder的定制活页夹。调用此绑定器时,将传递一个组,并且绑定器仅绑定属于此组的字段。要为字段设置组,可以使用新的
    @BindingGroup
    注释。由于也可能存在正常组足够的情况,绑定器还会查找验证约束的“组”参数。为方便起见,活页夹提供了一种方法
    bindAndValidate()

  • 指定名为
    BasicCheck
    的绑定组和第二个绑定组
    AddressCheck
    ,并将它们分配给Person和Address类的相应字段

  • 现在,您可以在控制器方法中逐步执行数据绑定。下面是一些伪代码:

    //create a new binder for a new Person instance
    result = binder.getBindingResult();
    binder.bindAndValidate(data, BasicCheck.class);
    if (person.hasAddress)
       binder.bindAndValidate(data, AddressCheck.class);
    if (!result.hasErrors())
       // do something
    
  • 正如您所看到的,缺点是您必须自己执行绑定,而不是使用漂亮的注释

    以下是我的源代码:

    绑定组:

    import java.lang.annotation.*;
    
    @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface BindingGroup
    {
       Class<?>[] value() default {};
    }
    
    Person类字段的一些示例:

    @NotNull(groups = BasicCheck.class)
    public String getName() { return name; }
    
    @BindingGroup(BasicCheck.class)
    public String phoneNumber() { return phoneNumber; }
    
    @Valid
    public Address getAddress() { return address; }
    
    地址类别:

    @BindingGroup(BasicCheck.class)
    public Integer getZipCode() { return zipCode; }
    

    写这个答案需要很多工作,所以我希望它能帮助你。

    这个答案是针对
    @InitBinder
    部分的。Javadoc说注释的值是这个init binder方法应该应用的[model attribute]的名称

    我认为您应该明确地为model属性设置一个名称,并为
    @InitBinder

    @InitBinder("person_with_address")
    public void ...
    ...
    public String create(@NotNull @Valid @ModelAttribute("person_with_address") Person person, BindingResult bindingResult...)
    
    这样,您应该能够为该控制器方法使用专用绑定

  • 从地址字段中删除了
    @Valid
  • 手动验证:在控制器的
    create
    方法中:
    validateAddressIfNeeded(person,bindingResult)


  • 感谢@fishbone的回复,不幸的是,我无法接受,因为我发现它很复杂,并且由于工作的规模,可能会成为遗留问题或引入新的bug。请看下面我最后遵循的方法。不太理想,但它比较短,维护的代码也比较少。这看起来确实比实际情况更复杂。它只是一个尊重组的绑定器,而不是绑定整个数据。如果您想定义一个组,但没有验证约束,那么可以添加一个注释“BindingGroup”。我们经常以多步骤的形式使用它。谢谢@Serge,最后我采用了其他方法
    @BindingGroup(BasicCheck.class)
    public Integer getZipCode() { return zipCode; }
    
    @InitBinder("person_with_address")
    public void ...
    ...
    public String create(@NotNull @Valid @ModelAttribute("person_with_address") Person person, BindingResult bindingResult...)
    
    private void validateAddressIfNeeded(Person person, BindingResult bindingResult) {
        if (person.hasAddress()) {
            bindingResult.pushNestedPath("address");
            validator.validate(person.getAddress(), bindingResult);
            bindingResult.popNestedPath();
        }
    }