【问题标题】:Selective validation in MVC3MVC3 中的选择性验证
【发布时间】:2011-10-26 11:55:59
【问题描述】:

我有一个这样的地址类:

public class CustomerAddress
{
    [Required]
    public string Line1 { get; set; }

    public string Line2 { get; set; }

    [Required]
    public string Town { get; set; }

    [Required]
    public string Postcode { get; set; }
}

我有一个像这样的视图模型:

public class CheckoutViewModel
{
    [Required]
    public string Name { get; set; }

    //... etc

    public bool DeliverySameAsBilling { get; set; }

    public CustomerAddress BillingAddress { get; set; }

    public CustomerAddress DeliveryAddress { get; set; }
 }

我只想在DeliverySameAsBilling 为假时验证收货地址,我可以从this 看到 IValidatableObject 可能是要走的路。

该示例对模型施加了比属性更严格的标准;就我而言,我想选择性地忽略 CustomerAddress 类中的 [Required] 属性。我该怎么做呢?我将如何连接适当的客户端验证?

或者,我可以在BillingAddressDeliveryAddress 上使用this 之类的自定义属性,然后可能更容易处理客户端验证;但是,如果DeliverySameAsBilling 为真,我仍然不知道如何有效地“取消”对属性的验证。

哪个最好?

【问题讨论】:

    标签: c# asp.net-mvc asp.net-mvc-3 validation


    【解决方案1】:

    看看这个。

    http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx

    创建一个RequiredIfAttribute

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.ComponentModel.DataAnnotations;
    
    namespace ConditionalValidation.Validation
    {
        public class RequiredIfAttribute : ValidationAttribute
        {
            // Note: we don't inherit from RequiredAttribute as some elements of the MVC
            // framework specifically look for it and choose not to add a RequiredValidator
            // for non-nullable fields if one is found. This would be invalid if we inherited
            // from it as obviously our RequiredIf only applies if a condition is satisfied.
            // Therefore we're using a private instance of one just so we can reuse the IsValid
            // logic, and don't need to rewrite it.
            private RequiredAttribute innerAttribute = new RequiredAttribute();
            public string DependentProperty { get; set; }
            public object TargetValue { get; set; }
    
            public RequiredIfAttribute(string dependentProperty, object targetValue)
            {
                this.DependentProperty = dependentProperty;
                this.TargetValue = targetValue;
            }
    
            public override bool IsValid(object value)
            {
                return innerAttribute.IsValid(value);
            }
        }
    }
    

    然后创建一个RequiredIfValidator

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    
    namespace ConditionalValidation.Validation
    {
        public class RequiredIfValidator : DataAnnotationsModelValidator<RequiredIfAttribute>
        {
            public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
                : base(metadata, context, attribute)
            {
            }
    
            public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
            {
                // no client validation - I might well blog about this soon!
                return base.GetClientValidationRules();
            }
    
            public override IEnumerable<ModelValidationResult> Validate(object container)
            {
                // get a reference to the property this validation depends upon
                var field = Metadata.ContainerType.GetProperty(Attribute.DependentProperty);
    
                if (field != null)
                {
                    // get the value of the dependent property
                    var value = field.GetValue(container, null);
    
                    // compare the value against the target value
                    if ((value == null && Attribute.TargetValue == null) ||
                        (value.Equals(Attribute.TargetValue)))
                    {
                        // match => means we should try validating this field
                        if (!Attribute.IsValid(Metadata.Model))
                            // validation failed - return an error
                            yield return new ModelValidationResult { Message = ErrorMessage };
                    }
                }
            }
        }
    }
    

    并在模型中使用

    namespace ConditionalValidation.Models
    {
        public class Person
        {
            [HiddenInput(DisplayValue = false)]
            public int Id { get; set; }
    
            [StringLength(10)]
            [RequiredIf("City", null)]
            public string Name { get; set; }
    
            [RequiredIf("IsUKResident", true, ErrorMessage = "You must specify the City if UK resident")]
            public string City { get; set; }
    
            [RequiredIf("IsUKResident", false, ErrorMessage = "You must specify the country if not UK resident")]
            [RegularExpression("^(\\w)+$", ErrorMessage = "Only letters are permitted in the Country field")]
            public string Country { get; set; }
    
            // this field is last in the class - therefore any RequiredAttribute validation that occurs
            // on fields before it don't guarantee this field's value is correctly set - see my blog post 
            // if that doesn't make sense!
            [DisplayName("UK Resident")]
            public bool IsUKResident { get; set; }
        }
    }
    

    【讨论】:

    • 感谢您提供如此详细的答案。您会注意到 BillingAddress 和 DeliveryAddress 实际上没有必需的属性,但是模型验证无论如何都会失败,因为子属性具有必需的属性。如果不将 RequiredIf 属性放入 CustomerAddress 类,我如何使用您的代码选择性地忽略视图模型级别的地址字段(而不是选择性地要求)?谢谢!
    • 对于 Billing+Delivery 地址,我所做的是禁用 JS 中的字段 - 这样它们就没有经过客户端验证。然后我使用这个stackoverflow.com/questions/14008561/… 删除模型状态服务器端
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-18
    相关资源
    最近更新 更多