【问题标题】:Spring MVC and JSR-303 hibernate conditional validationSpring MVC 和 JSR-303 休眠条件验证
【发布时间】:2011-04-30 03:03:56
【问题描述】:

我有一个要验证的表单。它包含 2 个地址变量。 address1 必须始终被验证,address2 必须根据某些条件进行验证

public class MyForm {
    String name;
    @Valid Address address1;
    Address address2;
 }

public class Address {
    @NotEmpty   
    private String street;
}

我的控制器自动验证并绑定我的表单 obj

@RequestMapping(...)
public ModelAndView edit(
        @ModelAttribute("form")
        @Valid
        MyForm form,
        BindingResult bindingResult,
        ...)

        if(someCondition) {
            VALIDATE form.address2 USING JSR 303

问题是,如果我使用 LocalValidatorFactoryBean 验证器,我无法重用 Spring 提供的 BinidingResult 对象。绑定不起作用,因为“结果”的目标对象是“MyForm”而不是“地址”

validate(form.getAddress2(), bindingResult)   //won't work

我想知道进行条件验证的标准/干净方法是什么。

我正在考虑以编程方式在我的控制器中创建一个新的 BindingResult。

final BindingResult bindingResultAddress2 = new BeanPropertyBindingResult(address2, "form");
validate(form.getAddress2(), bindingResultAddress2);

但是我从 bindingResultAddress2 获得的错误列表无法添加到一般的“bindingResult”中,因为字段名称不正确(“street”而不是“address2.street”)并且绑定不起作用.

一些肮脏的方法是扩展 BeanPropertyBindingResult 以接受一些字符串附加到字段名称.. 你有更好的方法吗?

【问题讨论】:

    标签: java spring validation spring-mvc bean-validation


    【解决方案1】:

    首先我们来看看@javax.validation.ValidAPI

    将关联标记为级联。关联对象将通过级联进行验证。

    当 Spring 框架使用 @Valid 作为标记 来验证其命令对象时,它会破坏其用途。 Spring 应改为创建您自己的特定注释,以指定应验证的组。

    不幸的是,如果您需要验证某些组,您应该使用 Spring 原生 Validator API

    public void doSomething(Command command, Errors errors) {
        new BeanValidationValidator(SomeUserCase.class, OtherUserCase.class)
            .validate(command, errors);
    
        if(errors.hasErrors()) {
    
        } else {
    
        }
    }
    

    BeanValidationValidator 可以实现为

    public class BeanValidationValidator implements Validator {
    
        javax.validation.Validator validator = ValidatorUtil.getValidator();
    
        private Class [] groups;
    
        public BeanValidationValidator(Class... groups) {
            this.groups = groups;
        }
    
        public void validate(Object command, Errors errors) {
            Set<ConstraintViolation<Object>> constraintViolationSet = validator.validate(command, groups);
    
            for(ConstraintViolation<Object> constraintViolation: constraintViolationSet) {
                errors.rejectValue(constraintViolation.getPropertyPath().toString(), null, constraintViolation.getMessage()); 
            }
        }
    
    }
    

    【讨论】:

    • 我非常不同意将组添加到 @Valid 注释会破坏其目的。我认为它是一种增强。 @Valid 说“这个对象应该被验证”。添加组实际上意味着“应该以这种方式验证此对象”。没有理由为什么相同的语句不能应用于对象图的级联关联以及方法参数。此外,规范应该随着明显有价值的补充而发展;将组添加到 @Valid 将允许许多框架消除重复代码。规范应该更注重实用性而不是理想主义。
    【解决方案2】:

    我自己从未尝试过,但我认为正确的方法是使用validator groups

    【讨论】:

    • 不幸的是@Valid没有组
    • 很不幸 - 请参阅我上面的评论。我认为这将是对规范的一个很好的补充,并且这种程度的实用性应该比理想主义更受青睐。
    • 或者为了安抚理想主义者,我们应该停止将其称为“Bean Validation Framework”并开始将其称为“Java Validation Framework”,并在对象图之外添加对方法参数和返回值的支持。
    【解决方案3】:

    验证分层结构的标准方法是使用pushNestedPath()/popNestedPath(),尽管我不确定它如何与 JSR-303 配合使用:

    bindingResult.pushNestedPath("address2");
    validate(form.getAddress2(), bindingResult);
    bindingResult.popNestedPath();
    

    【讨论】:

    • 感谢 axtavt,它有效。我想知道这是否是实现它的最佳方法,为什么 @Valid 中没有 groups 属性以及 JSR-303 组概念是否是实现条件验证的唯一方法
    • @Valid中的群组有功能请求:jira.springframework.org/browse/SPR-6373
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-10
    • 2014-06-24
    • 2022-01-20
    • 1970-01-01
    • 2018-08-15
    • 2016-11-22
    • 2014-09-28
    相关资源
    最近更新 更多