【问题标题】:How to validate one field related to another's value in ASP .NET MVC 3如何在 ASP .NET MVC 3 中验证与另一个值相关的一个字段
【发布时间】:2012-01-18 06:45:18
【问题描述】:

我有两个字段,例如电话号码和手机号码。有点像..

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

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

但用户可以在其中任何一个中输入数据。一个是强制性的。如何处理它们,即当用户在另一个字段中输入数据时如何禁用一个字段的必填字段验证器,反之亦然。在哪种情况下,我必须在 javascript 中处理它,我需要为此添加哪些脚本。任何人都可以帮助找到解决方案...

【问题讨论】:

    标签: asp.net-mvc-3


    【解决方案1】:

    一种可能性是编写自定义验证属性:

    public class RequiredIfOtherFieldIsNullAttribute : ValidationAttribute, IClientValidatable
    {
        private readonly string _otherProperty;
        public RequiredIfOtherFieldIsNullAttribute(string otherProperty)
        {
            _otherProperty = otherProperty;
        }
    
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var property = validationContext.ObjectType.GetProperty(_otherProperty);
            if (property == null)
            {
                return new ValidationResult(string.Format(
                    CultureInfo.CurrentCulture, 
                    "Unknown property {0}", 
                    new[] { _otherProperty }
                ));
            }
            var otherPropertyValue = property.GetValue(validationContext.ObjectInstance, null);
    
            if (otherPropertyValue == null || otherPropertyValue as string == string.Empty)
            {
                if (value == null || value as string == string.Empty)
                {
                    return new ValidationResult(string.Format(
                        CultureInfo.CurrentCulture,
                        FormatErrorMessage(validationContext.DisplayName),
                        new[] { _otherProperty }
                    ));
                }
            }
    
            return null;
        }
    
        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
                ValidationType = "requiredif",
            };
            rule.ValidationParameters.Add("other", _otherProperty);
            yield return rule;
        }
    }
    

    您将应用于视图模型的属性之一:

    public class MyViewModel
    {
        [RequiredIfOtherFieldIsNull("Mobile")]
        public string Phone { get; set; }
    
        public string Mobile { get; set; }
    }
    

    那么你可以有一个控制器:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View(new MyViewModel());
        }
    
        [HttpPost]
        public ActionResult Index(MyViewModel model)
        {
            return View(model);
        }
    }
    

    最后是一个视图,您将在该视图中注册一个适配器,以便为此自定义规则连接客户端验证:

    @model MyViewModel
    
    <script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
    <script type="text/javascript">
        jQuery.validator.unobtrusive.adapters.add(
            'requiredif', ['other'], function (options) {
    
                var getModelPrefix = function (fieldName) {
                    return fieldName.substr(0, fieldName.lastIndexOf('.') + 1);
                }
    
                var appendModelPrefix = function (value, prefix) {
                    if (value.indexOf('*.') === 0) {
                        value = value.replace('*.', prefix);
                    }
                    return value;
                }
    
                var prefix = getModelPrefix(options.element.name),
                    other = options.params.other,
                    fullOtherName = appendModelPrefix(other, prefix),
                    element = $(options.form).find(':input[name="' + fullOtherName + '"]')[0];
    
                options.rules['requiredif'] = element;
                if (options.message) {
                    options.messages['requiredif'] = options.message;
                }
            }
        );
    
        jQuery.validator.addMethod('requiredif', function (value, element, params) {
            var otherValue = $(params).val();
            if (otherValue != null && otherValue != '') {
                return true;
            }
            return value != null && value != '';
        }, '');
    </script>
    
    @using (Html.BeginForm())
    {
        <div>
            @Html.LabelFor(x => x.Phone)
            @Html.EditorFor(x => x.Phone)
            @Html.ValidationMessageFor(x => x.Phone)
        </div>
    
        <div>
            @Html.LabelFor(x => x.Mobile)
            @Html.EditorFor(x => x.Mobile)
            @Html.ValidationMessageFor(x => x.Mobile)
        </div>
    
        <button type="submit">OK</button>
    }
    

    对于我们在日常生活中遇到的非常简单的验证规则来说,这真是太恶心了。我不知道 ASP.NET MVC 的设计者在决定选择声明式验证方法而不是命令式验证时的想法。

    无论如何,这就是为什么我使用FluentValidation.NET 而不是数据注释来对我的模型执行验证。实现这样简单的验证场景的方式应该很简单。

    【讨论】:

    • 根据您的建议,我设置了 FluentValidation。我试图解决的问题与这个问题相同,FV 中的 When() 验证器解决了它。我遇到的问题是“When()”没有客户端验证。我对 jquery 验证 atm 不太了解,但是为它创建客户端验证是否涉及很多?我假设它与您的答案有点相似,是吗?
    • 可以从 RequiredAttribute 继承以利用内置错误消息。
    【解决方案2】:

    我知道这个问题不是很热,因为它是很久以前提出的,但是我将分享一个稍微不同的解决这个问题的想法。我决定实现一种机制,该机制提供条件属性来根据其他属性值和它们之间的关系计算验证结果,这些属性值和它们之间的关系在逻辑表达式中定义。

    您的问题可以通过使用以下注释来定义和自动解决:

    [RequiredIf("Mobile == null",
        ErrorMessage = "At least email or phone should be provided.")]
    public string Phone{ get; set; }
    
    [RequiredIf("Phone == null",
        ErrorMessage = "At least email or phone should be provided.")]
    public string Mobile { get; set; }
    

    如果您觉得它对您的目的有用,请查看有关 ExpressiveAnnotations 库 can be found here 的更多信息。开箱即用也支持客户端验证。

    【讨论】:

    • 这个库很棒!
    • 我不能使用这个包。如何使用??请。帮助我。
    • @lazycoder:您介意建议您需要什么帮助吗?
    【解决方案3】:

    由于没有其他人建议,我将告诉您我们使用的另一种方法。

    如果您创建自定义数据类型的未映射字段(在我的示例中,一对 gps 点),您可以将验证器放在上面,甚至不需要使用反射来获取所有值。

        [NotMapped]
        [DCGps]
        public GPS EntryPoint
        {
            get
            {
                return new GPS(EntryPointLat, EntryPointLon);
            }
        }
    

    类,一个标准的getter/setter

       public class GPS
    {
        public decimal? lat { get; set; }
        public decimal? lon { get; set; }
        public GPS(decimal? lat, decimal? lon)
        {
            this.lat = lat;
            this.lon = lon;
        }
    }
    

    现在是验证器:

        public class DCGps : DCValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (!(value is GPS)) {
                return new ValidationResult("DCGps:  This annotation only works with fields with the data type GPS.");
            }
            //value stored in the field.
            //these come through as zero or emptry string.  Normalize to ""
            string lonValue = ((GPS)value).lonstring == "0" ? "" : ((GPS)value).lonstring;
            string latValue = ((GPS)value).latstring == "0" ? "" : ((GPS)value).latstring;
    
            //place validation code here.  You have access to both values.  
            //If you have a ton of values to validate, you can do them all at once this way.
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-20
      • 1970-01-01
      • 2014-12-26
      相关资源
      最近更新 更多