有条件地使用DataAnnotations验证ASP.NET MVC模型的部分?

时间:2009-08-30 03:52:59

标签: asp.net-mvc data-annotations

我的页面上有某些面板在某些情况下是隐藏的。

例如我可能有“帐单邮寄地址”和“送货地址”,如果选中“ShippingSameAsBilling”复选框,我不想验证“送货地址”。

我正在尝试使用ASP.NET MVC 2的新DataAnnotations capabilities(预览1)来实现这一目标。

我需要在未显示“发货地址”时阻止验证,并且需要找到实现此目的的方法。我主要讨论的是服务器端而不是using jquery

我怎样才能做到这一点?我有几个想法,与自定义模型绑定有关,但我目前最好的解决方案如下。有关此方法的任何反馈意见吗?

7 个答案:

答案 0 :(得分:6)

对于CheckoutModel我正在使用这种方法(大多数字段隐藏):

[ModelBinder(typeof(CheckoutModelBinder))]
public class CheckoutModel : ShoppingCartModel
{        
    public Address BillingAddress { get; set; }
    public Address ShippingAddress { get; set; }
    public bool ShipToBillingAddress { get; set; }
}

public class Address
{
    [Required(ErrorMessage = "Email is required")]
    public string Email { get; set; }

    [Required(ErrorMessage = "First name is required")]
    public string FirstName { get; set; }

    [Required()]
    public string LastName { get; set; }

    [Required()]
    public string Address1 { get; set; }
}

自定义模型绑定器会删除以“ShippingAddress”开头的字段的所有ModelState错误(如果找到)。然后'TryUpdateModel()'将返回true。

    public class CheckoutModelBinder : DefaultModelBinder
    {
        protected override void OnModelUpdated(ControllerContext controllerContext,
                                               ModelBindingContext bindingContext) {

            base.OnModelUpdated(controllerContext, bindingContext);

            var model = (CheckoutModel)bindingContext.Model;

            // if user specified Shipping and Billing are the same then 
            // remove all ModelState errors for ShippingAddress
            if (model.ShipToBillingAddress)
            {
                var keys = bindingContext.ModelState.Where(x => x.Key.StartsWith("ShippingAddress")).Select(x => x.Key).ToList();
                foreach (var key in keys)
                {
                    bindingContext.ModelState.Remove(key);
                }
            }
        }    
    }

有更好的解决方案吗?

答案 1 :(得分:3)

答案 2 :(得分:2)

我可以看到你的困境。我正在寻找其他验证解决方案,这些解决方案也适用于可能适用于给定模型对象上的多个属性的复杂验证规则,或者甚至是对象图中不同模型对象的许多属性(如果你不幸的是要验证链接对象)像这样)。

IDataErrorInfo接口的限制是模型对象仅在没有任何属性出错时满足有效状态。这就是说有效对象是其中所有属性也有效的对象。但是,我可能会遇到这样的情况:如果属性A,B和C有效 - 则整个对象有效.. 如果属性A无效但B C,然后该对象满足有效性。我无法使用IDataErrorInfo接口/ DataAnnotations属性来描述此条件/规则。

所以我找到了delegate approach。现在,在撰写本文时,MVC中许多有用的进步都不存在,但核心概念应该对您有所帮助。我们不是使用属性来定义对象的验证条件,而是创建委托函数来验证更复杂的需求,因为它们被委托我们可以重用它们。当然这是更多的工作,但代表的使用意味着我们应该能够编写验证规则代码一次将所有验证规则存储在一个地方(可能是服务层)(kool位)甚至使用MVC 2 DefaultModelBinder自动调用验证(没有大量检查我们的控制器操作 - 就像Scott的博客说我们可以使用DataAnnotations。请参阅{在“强类型UI助手”标题之前{3}}!

我确信您可以使用Func<T>Predicate<T>这样的匿名代表稍微提高上述文章中建议的方法,并为验证规则编写自定义代码块将启用跨属性条件(例如,您提到的条件,如果您的ShippingSameAsBilling属性为真,那么您可以忽略更多的送货地址规则等。)

DataAnnotations用于使用非常少的代码对对象非常简单进行简单的验证规则。但随着您的需求的发展,您需要验证更复杂的规则。 MVC2模型绑定器中的新虚拟方法应该继续为我们提供将未来验证发明集成到MVC框架中的方法。

答案 3 :(得分:2)

确保您不需要验证的字段不会发布到操作中。我们只验证实际发布的字段。

编辑:(通过提问者)

此行为在MVC2 RC2中已更改:

  

默认验证系统验证   整个模型默认验证   ASP.NET MVC 1.0及其中的系统   在RC之前预览ASP.NET MVC 2   2仅验证了模型属性   被发布到服务器。在ASP.NET中   MVC 2,新的行为就是全部   模型属性在验证时验证   无论如何,模型都经过验证   是否发布了新值。   依赖于的应用程序   ASP.NET MVC 1.0行为可能需要   变化。有关的更多信息   此更改,请参阅条目Input Validation vs. Model Validation   Brad Wilson博客上的ASP.NET MVC。

答案 4 :(得分:1)

对于更复杂的案例,我从简单的DataAnnotations转移到以下内容:Validation with visitors and extension methods

如果您想使用DataAnnotations,您可以替换以下内容:

public IEnumerable<ErrorInfo> BrokenRules (Payment payment)
{   
    // snip... 
    if (string.IsNullOrEmpty (payment.CCName))
    {
      yield return new ErrorInfo ("CCName", "Credit card name is required");
    }
}

使用一种方法通过DataAnnotations(我没有atm)按名称验证属性。

答案 5 :(得分:1)

我创建了一个仅部分模型绑定器,它只验证提交的键。出于安全原因(如果我要更进一步),我将创建一个数据注释属性,用于标记允许从模型中排除哪些字段。然后,OnModelUpdated检查字段属性,以确保不会发生意外的欠调配。

public class PartialModelBinder : DefaultModelBinder
{
    protected override void OnModelUpdated(ControllerContext controllerContext, 
        ModelBindingContext bindingContext)
    {
        // default model binding to get errors
        base.OnModelUpdated(controllerContext, bindingContext);

        // remove errors from filds not posted
        // TODO: include request files
        var postedKeys = controllerContext.HttpContext.Request.Form.AllKeys;
        var unpostedKeysWithErrors = bindingContext.ModelState
            .Where(i => !postedKeys.Contains(i.Key))
            .Select(i=> i.Key).ToList();
        foreach (var key in unpostedKeysWithErrors)
        {
            bindingContext.ModelState.Remove(key);
        }
    }    
}

答案 6 :(得分:0)

这与 DataAnnotations 无关,但您是否查看了 Fluent Validation 项目?它为您提供了对您的验证的精细控制,如果您有对象到对象的验证,那么这两个对象的聚合对象将帮助您。

它似乎也是用MVC构建的,但它也有自己的“运行时”,这样你就可以在其他.NET应用程序中使用它,这是我书中的另一个好处。