ASP.Net MVC:如何动态地向任何模型属性添加验证

时间:2017-03-10 09:25:02

标签: c# asp.net-mvc validation

我很高兴知道如何在运行时向我的模型的任何属性添加验证而不触及代码。所以寻找指南如何在asp.net mvc中实现我的目标。

样本模型属性

[Required(ErrorMessage = "The field {0} is required")]
[Display(Name = "Company")]
public string Name { get; set; }

上面的代码是一个示例,其中在编码时添加了验证,但我不想在编码时附加验证,而我想在运行时以这样的方式编码,我将能够注入验证任何模型财产。

我搜索谷歌并找到了一个示例,但不明白如何使用它来完成我的任务。这是我找到的示例代码,但不清楚如何将其用于我的目的。

模型中的

[Domainabcd(20, "Bar", "Baz", ErrorMessage = "The combined minimum length of the Foo, Bar and Baz properties should be longer than 20")]
public string strAccessionId { get; set; }
DomainabcdAttribute.cs中的

public class DomainabcdAttribute : ValidationAttribute, IClientValidatable
 //Domain is the Attribute name 
//if you do not inherit IClientValidatable ,the server validation still will work ,but the Client validation will not work.

    {
        public DomainAttribute(int minLength, params string[] propertyNames) 
// this is where to get parameters from you pass 
//such as : 20, "Bar", "Baz",
        {
            this.PropertyNames = propertyNames;
            this.MinLength = minLength;
        }

        public string[] PropertyNames { get; private set; }
        public int MinLength { get; private set; }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
           ///this is where to decide if to display error ,or not.
            //var properties = this.PropertyNames.Select(validationContext.ObjectType.GetProperty);
            //var values = properties.Select(p => p.GetValue(validationContext.ObjectInstance, null)).OfType<string>();
            //var totalLength = values.Sum(x => x.Length) + Convert.ToString(value).Length;
            //if (totalLength < this.MinLength)
            //{
               //this is the place to return the validation result
             //so you may just use this line code:   return new ValidationResult(validationContext.DisplayName)
                return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
            //}
            //return null; //no error massage

       }
        //this is for Client to display error message 
        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var modelClientValidationRule = new ModelClientValidationRule
            {
                ValidationType = "requiredif",
                ErrorMessage = ErrorMessage //Added ,pass the error message 

            };

            modelClientValidationRule.ValidationParameters.Add("param", this.PropertyNames); //Added
            return new List<ModelClientValidationRule> { modelClientValidationRule };

        }
        // public override string FormatErrorMessage(string name) //to custom format for error message
        //{
        //    string[] values = this.Values.Select(value => string.Format("'{0}'", value)).ToArray();
        //     //return string.Format("{0}{1}", name, string.Join(",", values));
        //     return string.Format(base.ErrorMessageString, name, string.Join(",", values));
        // }
    }

我的目标如下

1)假设第一天我会在运行时向name属性添加验证,例如名称

2)几天之后,我将动态添加另一个名称属性验证,假设名称不应该有@ , : # etc这样的特殊字符。

3)几天后我会再次向名称属性添加另一个验证,假设名称的长度不应小于10个字符。

希望我清楚我想要实现的目标。所以请那些做过它的人只是帮我提供示例代码。

感谢

2 个答案:

答案 0 :(得分:1)

当然可以。例如。您可以使用正则表达式验证对象并将它们存储在web.config中。

示例(此代码只是一个快速示例,不应在生产中使用):

修改web.config以存储验证程序的规则。将其添加到<configuration></configuration>部分:

<configSections>
    <sectionGroup name="CustomConfig">
    <section name="UniversalValidatorConfig" type="System.Configuration.NameValueSectionHandler" />
    </sectionGroup>
</configSections>

<CustomConfig>
    <UniversalValidatorConfig>
        <add key="EMail" value="^[a-zA-Z0-9_.-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$"/>
        <add key="CurrencyIsoCode" value="^([a-zA-Z]{3})$" />
    </UniversalValidatorConfig>
</CustomConfig>

此处我们添加了电子邮件 CurrencyIsoCode 属性的验证。 要应用验证,您应该创建如下属性:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

using System.Configuration;
using System.Collections.Specialized;
using System.Text.RegularExpressions;

namespace WebApplication1.Attributes
{
    [AttributeUsage(AttributeTargets.Class)]
    public class UniversalValidatorAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(Object value, ValidationContext validationContext)
        {
            if (value == null)
                return ValidationResult.Success;

            var validatorConfig = (NameValueCollection)ConfigurationManager.GetSection("CustomConfig/UniversalValidatorConfig");
            var validationRules = validatorConfig.AllKeys
                .Select(key => new { PropertyName = key, ValidationRule = validatorConfig.GetValues(key).FirstOrDefault() })
                .ToList();

            var errorMessages = new List<string>(validationRules.Count);

            foreach (var validationRule in validationRules)
            {
                var property = value.GetType().GetProperty(validationRule.PropertyName);
                if (property == null) continue;

                var propValue = property.GetValue(value)?.ToString();
                var regEx = new Regex(validationRule.ValidationRule, (RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline) | RegexOptions.IgnoreCase);
                var isValid = regEx.IsMatch(propValue);

                if (!isValid)
                    errorMessages.Add(FormatErrorMessage(validationRule.PropertyName));
            }


            if (errorMessages.Any())
                return new ValidationResult(string.Join("\r\n", errorMessages));

            return ValidationResult.Success;
        }
    }
}

创建一个将用作操作的输入参数的类,并使用 UniversalValidatorAttribute 标记该类:

using WebApplication1.Attributes;

namespace WebApplication1.ActionParameters
{
    [UniversalValidator]
    public class IndexParameters
    {
        public string EMail { get; set; }
        public string CurrencyIsoCode { get; set; }
    }
}

现在我们可以在某些动作中使用参数。例如。修改主页/索引操作以使用 IndexParameters

public ActionResult Index(IndexParameters parameters)
{
    if (!ModelState.IsValid)
    {
        foreach(var error in ModelState.Where(ms => ms.Value.Errors.Any()).SelectMany(ms => ms.Value.Errors))
            Debug.WriteLine(error.ErrorMessage);
    }

    return View();
}

要测试Validator,我们可以使用这样的URL(第一个包含两个验证错误,第二个有效):

  

http://localhost:9316/Home/Index?email=admini.net&currencyIsoCode=U   http://localhost:9316/Home/Index?email=admin@i.net&currencyIsoCode=USD

这将允许您在web.config

中即时更改验证规则

自定义规则示例:

的web.config:

<CustomValidatorConfig>
  <add key="FirstName" value="LENGTHGREATER(10)" />
  <add key="BirthDate" value="DATEBETWEEN(01.01.2017, 01.02.2017)" />
</CustomValidatorConfig>

验证器属性:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using System.ComponentModel.DataAnnotations;
using System.Configuration;
using System.Collections.Specialized;
using System.Text.RegularExpressions;

namespace WebApplication1.Attributes
{
    public class CustomValidatorAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(Object value, ValidationContext validationContext)
        {
            if (value == null)
                return ValidationResult.Success;

            var validatorConfig = (NameValueCollection)ConfigurationManager.GetSection("CustomConfig/CustomValidatorConfig");
            var validationRules = validatorConfig.AllKeys
                .Select(key => new { PropertyName = key, ValidationRule = validatorConfig.GetValues(key).FirstOrDefault() })
                .ToList();

            var errorMessages = new List<string>(validationRules.Count);

            foreach (var validationRule in validationRules)
            {
                var property = value.GetType().GetProperty(validationRule.PropertyName);
                if (property == null) continue;

                var propValue = property.GetValue(value);
                var isValid = CustomValidator.IsValid(propValue, validationRule.ValidationRule);

                if (!isValid)
                    errorMessages.Add(FormatErrorMessage(validationRule.PropertyName));
            }


            if (errorMessages.Any())
                return new ValidationResult(string.Join("\r\n", errorMessages));

            return ValidationResult.Success;
        }
    }
}

应用于模型的属性

[CustomValidator]
public class TestValidatorParameters
{
    public string FirstName { get; set; }
    public DateTime BirthDate { get; set; }
}

使用属性的操作:

public ActionResult TestValidator(TestValidatorParameters parameters)
{
    if (!ModelState.IsValid)
    {
        foreach (var error in ModelState.Where(ms => ms.Value.Errors.Any()).SelectMany(ms => ms.Value.Errors))
            Debug.WriteLine(error.ErrorMessage);

        return Json("Validation failed", JsonRequestBehavior.AllowGet);
    }

    return Json("Validation passed", JsonRequestBehavior.AllowGet);
}

要测试的网址(首先 - 验证正常,第二次 - 验证失败):

  

http://localhost:9316/Home/TestValidator?FirstName=KowalskiPinguin&BirthDate=01.01.2017   http://localhost:9316/Home/TestValidator?FirstName=Kowalski&BirthDate=01.01.2016

答案 1 :(得分:0)

一种可能的解决方案是在您的模型上实现IValidatableObject接口(https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.ivalidatableobject(v=vs.110).aspx),并在Validate方法中使用Fluent验证(https://github.com/JeremySkinner/FluentValidation)或类似的机制/框架创建验证消息。这仍然需要编辑/分发变化才能看到效果。为了避免这种情况,您可以实现解释器模式,以在验证中创建在运行时执行的验证规则。