使用 Spring 验证器验证嵌套对象列表?

Posted

技术标签:

【中文标题】使用 Spring 验证器验证嵌套对象列表?【英文标题】:Validate a list of nested objects with Spring validator? 【发布时间】:2012-09-22 17:48:51 【问题描述】:

我想知道如何在 Spring MVC 应用程序中使用 Spring Validator(不是注释)来验证我的表单中的嵌套对象列表。

class MyForm() 
    String myName;
    List<TypeA> listObjects;

class TypeA() 
    String number;
    String value;

如何创建 MyFormValidator 来验证 listObjects 并为 TypeA 的数量和值添加错误消息。

【问题讨论】:

【参考方案1】:

对于嵌套验证,您可以执行以下操作:

public class MyFormValidator implements Validator 

    private TypeAValidator typeAValidator;

    @Override
    public boolean supports(Class clazz) 
        return MyForm.class.equals(clazz);
    

    @Override
    public void validate(Object target, Errors errors) 
        MyForm myForm = (MyForm) target;
        typeAValidator = new TypeAValidator();

        int idx = 0;
        for (TypeA item : myForm.getListObjects()) 

            errors.pushNestedPath("listObjects[" + idx + "]");
            ValidationUtils.invokeValidator(this.typeAValidator, item, errors);
            errors.popNestedPath();
            idx++;

            ...
        

        ...
    


public class TypeAValidator implements Validator

    @Override
    public boolean supports(Class<?> clazz) 
        return TypeA.class.isAssignableFrom(clazz);
    

    @Override
    public void validate(Object target, Errors errors) 
        TypeA objTypeA = (TypeA)target;

        ValidationUtils.rejectIfEmpty(errors, "number", "number.notEmpty");
    

希望这会有所帮助。

【讨论】:

我会推荐这个解决方案。刚刚在我正在进行的项目中遇到了类似的问题。此解决方案允许您保持代码模块化,如果需要,将您的验证拆分为多个不同的验证器。 很高兴它有帮助。我发布此内容的目的是因为它是模块化且易于维护的。 知道如何报告哪一行有错误吗? TypeA 有一个属性List&lt;TypeA&gt;(嵌套)时,这会起作用吗?如果我有一个类是 RuleCondition... 我可能有一个嵌套的 List&lt;RuleCondition&gt; 字段。我想知道这是否也适用于这种情况 ^ 请忽略我的问题,我试过了,它适用于我给定的场景。谢谢!【参考方案2】:
public class MyFormValidator implements Validator 

    @Override
    public boolean supports(Class clazz) 
        return MyForm.class.equals(clazz);
    

    @Override
    public void validate(Object target, Errors errors) 
        MyForm myForm = (MyForm) target;

        for (int i = 0; i < myForm.getListObjects().size(); i++) 
            TypeA typeA = myForm.getListObjects().get(i);

            if(typeAHasAnErrorOnNumber) 
                errors.rejectValue("listObjects[" + i + "].number", "your_error_code");
            

            ...
        

        ...
    


有趣的链接:

Spring MVC: Multiple Row Form Submit using List of Beans

【讨论】:

谢谢,杰罗姆。如果 TypeA 有自己的验证器,如何在 MyFormValidator 中使用验证器?如果有多个错误,如何在表单页面上只显示一条错误消息?【参考方案3】:

我使用的一个方便的辅助类 -

public final class ValidationHelper 

    public static <TEntity> void invokeNestedValidator(Validator validator, TEntity entity, Errors errors, String subPath) 
        try 
            errors.pushNestedPath(subPath);
            ValidationUtils.invokeValidator(validator, entity, errors);
         finally 
            errors.popNestedPath();
        
    

    public static <TEntity> void invokeNestedValidatorForList(Validator validator, List<TEntity> entities, Errors errors, String subPathRoot) 
        for (int index = 0; index < entities.size(); index++) 
            invokeNestedValidator(validator, entities.get(index), errors, subPathRoot + "[" + index + "]");
        
    

    private ValidationHelper() 

【讨论】:

如何使用ValidationHelper类?【参考方案4】:

你可以在项目的任何地方使用它

import org.springframework.validation.ValidationUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.CollectionUtils;

    public static void invokeValidatorForNestedCollection(Validator validator,
                                                      Object obj,
                                                      String collectionPath,
                                                      Errors errors) 

    Collection collection;
    try 
        collection = (Collection) PropertyUtils.getProperty(obj, collectionPath);
     catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) 
        throw new RuntimeException(e);
    

    if (CollectionUtils.isEmpty(collection)) return;
    int counter = 0;
    for (Object elem : collection) 
        errors.pushNestedPath(String.format(collectionPath + "[%d]", counter));
        ValidationUtils.invokeValidator(validator, elem, errors);
        errors.popNestedPath();
        counter++;
    

【讨论】:

以上是关于使用 Spring 验证器验证嵌套对象列表?的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC 表单验证不适用于嵌套的复杂类型

grails验证嵌套命令对象不起作用

如何在 ReactJS 中验证嵌套对象的 PropTypes?

使用休眠验证器的 Spring mvc 表单验证

使用嵌套数据更新 CoreData 对象时,RestKit 验证失败(Cocoa 错误 1550)

如何使用 express-validator 对嵌套对象进行验证