ChatGPT解决这个技术问题 Extra ChatGPT

ASP.NET MVC 条件验证

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

例如,假设我们有以下模型(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)%>

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

我最近问过类似的问题:stackoverflow.com/questions/2280539/…
我很困惑。 Senior 对象始终是高级对象,那么在这种情况下,为什么 IsSenior 可以为假。当 Person.IsSenior 为假时,您是否只需要将“Person.Senior”属性设为空。或者为什么不按如下方式实现 IsSenior 属性:bool IsSenior { get { return this.Senior != null; } }
Steven:“IsSenior”翻译为视图中的复选框字段。当用户选中“IsSenior”复选框时,“Senior.Description”字段成为必填项。
达林·季米特洛夫:好吧,但不完全是。您看,您将如何实现将错误消息附加到特定字段?如果在对象级别进行验证,则会在对象级别出现错误。我需要属性级别的错误。

J
James Skemp

在 MVC3 中添加条件验证规则有更好的方法;让您的模型继承 IValidatableObject 并实现 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.");
    }
}

Introducing ASP.NET MVC 3 (Preview 1) 阅读更多信息。


不幸的是,微软把它放在了错误的层——验证是业务逻辑,而这个接口在 System.Web DLL 中。为了使用它,您必须让您的业务层依赖于表示技术。
如果你实现它,你就会这样做 - 请参阅 falconwebtech.com/post/… 中的完整示例
falconwebtech.com/post/… - @viperguynaz 这不起作用
@RayLoveless 您应该调用 ModelState.IsValid - 不直接调用 Validate
我知道这是一个较旧的线程,但要完成 - @viperguynaz - 将错误绑定到特定属性(例如,用于客户端模型绑定验证错误显示),请改用重载方法。 "yield return new ValidationResult("ErrorMessage.", new[] {"PutNameOfPropertyHere"}); 没有这个,验证错误是通用的,并且需要在视图中显示一个全面的警告标签。
J
Jakov

我通过处理控制器包含的 "ModelState" 字典解决了这个问题。 ModelState 字典包括所有必须验证的成员。

这是解决方案:

如果您需要基于某些字段实现条件验证(例如,如果 A=true,则需要 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)。

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


缺点是您的验证逻辑一部分位于模型中,另一部分位于控制器中。
当然,这不是必需的。我只展示最基本的例子。我已经使用模型上的接口和验证实现上述接口的模型的动作属性实现了这一点。接口出汗 Validate(ModelStateDictionary modelState) 方法。所以最后你在模型中做所有的验证。无论如何,好点。
同时,我喜欢这种方法的简单性,直到 MVC 团队开箱即用地构建出更好的东西。但是您的解决方案是否在启用客户端验证的情况下工作?
@Aaron:我很高兴您喜欢解决方案,但不幸的是,此解决方案不适用于客户端验证(因为每个验证属性都需要其 JavaScript 实现)。您可以使用“Remote”属性来帮助自己,因此只会发出 Ajax 调用来验证它。
你能扩展这个答案吗?这是有道理的,但我想确保我对此很清楚。我正面临这种确切的情况,我想解决它。
C
Ciarán Bruen

我昨天遇到了同样的问题,但我以一种非常干净的方式完成了它,它适用于客户端和服务器端验证。

条件:基于模型中其他属性的值,您要使其他属性成为必需的。这是代码

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 是必须验证您的其他属性是否需要的 PropertyName(属性)的特定值

说你有以下

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_Start 中)

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

asp.net mvc2中是否有任何等效的解决方案? ValidationResult、ValidationContext 类在 asp.net mvc2 (.net framework 3.5) 中不可用
这仅适用于链接博客状态的服务器端
我设法用 MVC5 在客户端让它工作,但在客户端,无论 DesiredValue 是什么,它都会启动验证。
@Dan Hunex:在 MVC4 中,我无法在客户端正常工作,无论 DesiredValue 是什么,它都会启动验证。有什么帮助吗?
K
Korayem

我一直在使用这个惊人的 nuget,它可以做动态注释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; }

ExpressiveAnnotation 库是这里所有答案中最灵活、最通用的解决方案。感谢分享!
我一直在努力寻找解决方案来度过充实的一天。 ExpressiveAnnotations 看起来是我的解决方案!
ExpressiveAnnotation 库太棒了!
它也有客户端支持!
但不支持 .NET Core,而且看起来不会发生。
P
Pavel Chuchuva

您可以通过从 ModelState 中删除错误来有条件地禁用验证器:

ModelState["DependentProperty"].Errors.Clear();

J
James Skemp

谢谢Merritt :)

我刚刚将它更新到 MVC 3,以防有人发现它有用:Conditional Validation in ASP.NET MVC 3


b
bojingo

现在有一个框架可以开箱即用地执行此条件验证(以及其他方便的数据注释验证):http://foolproof.codeplex.com/

具体来说,看看 [RequiredIfTrue("IsSenior")] 验证器。您将其直接放在要验证的属性上,因此您可以获得与“高级”属性相关联的验证错误的所需行为。

它以 NuGet 包的形式提供。


S
Steven

您需要在 Person 级别进行验证,而不是在 Senior 级别进行验证,或者 Senior 必须具有对其父 Person 的引用。在我看来,您需要一种自我验证机制来定义对 Person 的验证,而不是在它的某个属性上。我不确定,但我认为 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));
        }
    }
}

“您需要在 Person 级别验证,而不是在 Senior 级别验证” 是的,这是一个选项,但是您失去了将错误附加到特定字段的能力,这是 Senior 对象所必需的。
D
Den

我遇到了同样的问题,需要修改 [Required] 属性 - 使字段依赖于 http 请求。解决方案类似于 Dan Hunex 的答案,但他的解决方案无法正常工作(见评论)。我不使用不显眼的验证,只使用 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; }

对我来说主要的优势是我不必像在不显眼的验证中那样编写自定义客户端验证器。它与 [必需] 一样工作,但仅在您想要的情况下。


关于扩展 DataAnnotationsModelValidator 的部分正是我需要看到的。谢谢你。
J
James Skemp

查看 Simon Ince 的Conditional Validation in MVC

我现在正在研究他的示例项目。


J
Jeremy Ray Brown

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

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

例子:

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

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


佚名

我正在使用 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”。然后,您只需要检查您的发布操作的验证。