代码之家  ›  专栏  ›  技术社区  ›  Peter Stegnar

ASP.NET MVC条件验证

  •  119
  • Peter Stegnar  · 技术社区  · 16 年前

    如何使用数据注释对模型进行条件验证?

    例如,假设我们有以下模型(Person和Senior):

    public class Person
    {
        [Required(ErrorMessage = "*")]
        public string Name
        {
            get;
            set;
        }
    
        public bool IsSenior
        {
            get;
            set;
        }
    
        public Senior Senior
        {
            get;
            set;
        }
    }
    
    public class Senior
    {
        [Required(ErrorMessage = "*")]//this should be conditional validation, based on the "IsSenior" value
        public string Description
        {
            get;
            set;
        }
    }
    

    以及以下视图:

    <%= Html.EditorFor(m => m.Name)%>
    <%= Html.ValidationMessageFor(m => m.Name)%>
    
    <%= Html.CheckBoxFor(m => m.IsSenior)%>
    <%= Html.ValidationMessageFor(m => m.IsSenior)%>
    
    <%= Html.CheckBoxFor(m => m.Senior.Description)%>
    <%= Html.ValidationMessageFor(m => m.Senior.Description)%>
    

    我想成为“Senior.Description”属性条件必需字段,该字段基于“IsSenior”属性的选择(true->必需)。如何在带有数据注释的ASP.NET MVC 2中实现条件验证?

    12 回复  |  直到 9 年前
        1
  •  142
  •   Peter Rasmussen    11 年前

    在MVC3中添加条件验证规则有更好的方法。让您的模型继承ivalidableObject并实现validate方法:

    public class Person : IValidatableObject
    {
        public string Name { get; set; }
        public bool IsSenior { get; set; }
        public Senior Senior { get; set; }
    
        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
        { 
            if (IsSenior && string.IsNullOrEmpty(Senior.Description)) 
                yield return new ValidationResult("Description must be supplied.");
        }
    }
    

    更多描述请参见 http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx

        2
  •  61
  •   Jakov    9 年前

    我通过处理 "ModelState" 控制器包含的字典。ModelState字典包含所有必须验证的成员。

    解决方案如下:

    如果需要实现 条件验证 基于某些字段(例如,如果a=真,则需要b), 同时维护属性级错误消息传递 (对于对象级别的自定义验证器,这是不正确的)您可以通过处理“modelstate”来实现这一点,只需从中删除不需要的验证器即可。

    …在某些班级…

    public bool PropertyThatRequiredAnotherFieldToBeFilled
    {
      get;
      set;
    }
    
    [Required(ErrorMessage = "*")] 
    public string DepentedProperty
    {
      get;
      set;
    }
    

    …课程继续……

    …在某些控制器操作中…

    if (!PropertyThatRequiredAnotherFieldToBeFilled)
    {
       this.ModelState.Remove("DepentedProperty");
    }
    

    通过这个,我们实现了条件验证,而其他的一切都保持不变。


    更新:

    这是我的最后一个实现:我在模型上使用了一个接口,并使用了action属性来验证实现上述接口的模型。接口规定了validate(modelstatedictionary modelstate)方法。action属性只调用ivalidatorsomething上的validate(modelstate)。

    我不想让这个答案复杂化,所以我没有提到最终的实现细节(最终,这在生产代码中很重要)。

        3
  •  33
  •   Ciarán Bruen    13 年前

    昨天我也遇到了同样的问题,但我以一种非常干净的方式完成了这项工作,这对客户端和服务器端的验证都有效。

    条件:根据模型中其他属性的值,您需要另一个属性。这是密码

    public class RequiredIfAttribute : RequiredAttribute
    {
        private String PropertyName { get; set; }
        private Object DesiredValue { get; set; }
    
        public RequiredIfAttribute(String propertyName, Object desiredvalue)
        {
            PropertyName = propertyName;
            DesiredValue = desiredvalue;
        }
    
        protected override ValidationResult IsValid(object value, ValidationContext context)
        {
            Object instance = context.ObjectInstance;
            Type type = instance.GetType();
            Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
            if (proprtyvalue.ToString() == DesiredValue.ToString())
            {
                ValidationResult result = base.IsValid(value, context);
                return result;
            }
            return ValidationResult.Success;
        }
    }
    

    这里,propertyname是您要在其上创建条件的属性 DesiredValue是属性名(属性)的特定值,您的其他属性必须根据需要进行验证。

    假设你有以下内容

    public class User
    {
        public UserType UserType { get; set; }
    
        [RequiredIf("UserType", UserType.Admin, ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(ResourceString))]
        public string Password
        {
            get;
            set;
        }
    }
    

    最后,但不是最不重要的,为您的属性注册适配器,以便它可以进行客户端验证(我把它放在global.asax中,application\u start)

     DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter));
    
        4
  •  27
  •   Korayem Praphul Katlana    9 年前

    我一直在用这个神奇的装置做动态注释。 ExpressiveAnnotations

    你可以验证你梦寐以求的任何逻辑:

    public string Email { get; set; }
    public string Phone { get; set; }
    [RequiredIf("Email != null")]
    [RequiredIf("Phone != null")]
    [AssertThat("AgreeToContact == true")]
    public bool? AgreeToContact { get; set; }
    
        5
  •  17
  •   Pavel Chuchuva grapeot    15 年前

    通过从ModelState中删除错误,可以有条件地禁用验证程序:

    ModelState["DependentProperty"].Errors.Clear();
    
        6
  •  8
  •   Simon Ince    15 年前

    谢谢,梅里特:)

    我刚把它更新到MVC 3,以防有人发现它有用; http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx

    西蒙

        7
  •  6
  •   bojingo    12 年前

    现在有了一个框架,可以立即进行条件验证(以及其他方便的数据注释验证): http://foolproof.codeplex.com/

    具体来说,请查看[RequireDifTrue(“IsSenior”)]验证程序。您将其直接放在要验证的属性上,这样就可以获得与“Senior”属性关联的验证错误的所需行为。

    它可以作为Nuget包提供。

        8
  •  3
  •   Steven    16 年前

    您需要在个人级别验证,而不是在高级级别验证,或者高级级别必须具有对其父级人员的引用。在我看来,您需要一个自我验证机制来定义对人员的验证,而不是对其属性之一的验证。我不确定,但我认为DataAnnotations不支持这种开箱即用的方式。你能做的就是创造你自己的 Attribute 源于 ValidationAttribute 它可以在类级别上进行修饰,然后创建一个自定义验证器,该验证器还允许这些类级别的验证器运行。

    我知道验证应用程序块支持开箱即用的自我验证,但是VAB有一个相当陡峭的学习曲线。不过,这里有一个使用vab的例子:

    [HasSelfValidation]
    public class Person
    {
        public string Name { get; set; }
        public bool IsSenior { get; set; }
        public Senior Senior { get; set; }
    
        [SelfValidation]
        public void ValidateRange(ValidationResults results)
        {
            if (this.IsSenior && this.Senior != null && 
                string.IsNullOrEmpty(this.Senior.Description))
            {
                results.AddResult(new ValidationResult(
                    "A senior description is required", 
                    this, "", "", null));
            }
        }
    }
    
        9
  •  3
  •   Den    12 年前

    我有同样的问题,需要修改HTTP请求依赖性所需的[Required]属性make字段。解决方案类似于dan hunex answer,但他的解决方案没有正确工作(见注释)。我不使用不引人注目的验证,只使用microsoftmvcvalidation.js。 在这里。实现自定义属性:

    public class RequiredIfAttribute : RequiredAttribute
    {
    
        public RequiredIfAttribute(/*You can put here pararmeters if You need, as seen in other answers of this topic*/)
        {
    
        }
    
        protected override ValidationResult IsValid(object value, ValidationContext context)
        {
    
        //You can put your logic here   
    
            return ValidationResult.Success;//I don't need its server-side so it always valid on server but you can do what you need
        }
    
    
    }
    

    然后,您需要实现您的自定义提供程序,以将其用作global.asax中的适配器。

    public class RequreIfValidator : DataAnnotationsModelValidator <RequiredIfAttribute>
    {
    
        ControllerContext ccontext;
        public RequreIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
           : base(metadata, context, attribute)
        {
            ccontext = context;// I need only http request
        }
    
    //override it for custom client-side validation 
         public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
         {       
                   //here you can customize it as you want
             ModelClientValidationRule rule = new ModelClientValidationRule()
             {
                 ErrorMessage = ErrorMessage,
        //and here is what i need on client side - if you want to make field required on client side just make ValidationType "required"    
                 ValidationType =(ccontext.HttpContext.Request["extOperation"] == "2") ? "required" : "none";
             };
             return new ModelClientValidationRule[] { rule };
          }
    }
    

    并用一行代码修改global.asax

    DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequreIfValidator));
    

    这里是

    [RequiredIf]
    public string NomenclatureId { get; set; }
    

    对我来说,主要的优点是,我不必像在不引人注目的验证中那样编写自定义客户端验证程序的代码。它按[要求]工作,但只在您需要的情况下工作。

        10
  •  2
  •   Merritt    15 年前
        11
  •  0
  •   Jeremy Ray Brown    11 年前

    从模型状态中有条件地删除错误的典型用法:

    1. 使控制器动作的条件第一部分
    2. 执行逻辑以从ModelState中删除错误
    3. 执行现有逻辑的其余部分(通常是模型状态验证,然后是其他所有操作)

    例子:

    public ActionResult MyAction(MyViewModel vm)
    {
        // perform conditional test
        // if true, then remove from ModelState (e.g. ModelState.Remove("MyKey")
    
        // Do typical model state validation, inside following if:
        //     if (!ModelState.IsValid)
    
        // Do rest of logic (e.g. fetching, saving
    

    在您的示例中,保持一切不变,并将建议的逻辑添加到控制器的操作中。我假设传递给Controller操作的ViewModel具有Person和Senior Person对象,其中包含从UI填充的数据。

        12
  •  0
  •   fosterImposter    9 年前

    我正在使用MVC 5,但您可以尝试如下操作:

    public DateTime JobStart { get; set; }
    
    [AssertThat("StartDate >= JobStart", ErrorMessage = "Time Manager may not begin before job start date")]
    [DisplayName("Start Date")]
    [Required]
    public DateTime? StartDate { get; set; }
    

    在你的例子中,你会说“Issenior==true”。 然后,您只需要检查您的post操作的验证。