【问题标题】:ASP.NET MVC: Custom Validation by DataAnnotationASP.NET MVC:通过 DataAnnotation 进行自定义验证
【发布时间】:2013-04-12 14:12:18
【问题描述】:

我有一个具有 4 个字符串类型属性的模型。我知道您可以使用 StringLength 注释来验证单个属性的长度。但是我想验证 4 个属性组合的长度。

使用数据注释执行此操作的 MVC 方法是什么?

我之所以问这个问题是因为我是 MVC 的新手,并且希望在制定自己的解决方案之前以正确的方式进行操作。

【问题讨论】:

  • 你看过 Fluent Validation 吗?它比数据注释更好地处理复杂场景
  • 看看强烈推荐的解决方案......dotnetcurry.com/ShowArticle.aspx?ID=776
  • 感谢您的回答。我将检查 Fluent Validation,从未听说过。 Niks,Darin 基本上写出了您发布的链接中的文章所解释的内容。所以,谢谢你......很棒的东西!

标签: c# .net asp.net-mvc asp.net-mvc-3 data-annotations


【解决方案1】:

您可以编写自定义验证属性:

public class CombinedMinLengthAttribute: ValidationAttribute
{
    public CombinedMinLengthAttribute(int minLength, params string[] propertyNames)
    {
        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)
    {
        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)
        {
            return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
        }
        return null;
    }
}

然后你可能有一个视图模型并用它装饰它的一个属性:

public class MyViewModel
{
    [CombinedMinLength(20, "Bar", "Baz", ErrorMessage = "The combined minimum length of the Foo, Bar and Baz properties should be longer than 20")]
    public string Foo { get; set; }
    public string Bar { get; set; }
    public string Baz { get; set; }
}

【讨论】:

  • 感谢您的回答,我接受了您的回答。其实感觉有点尴尬。你写出了整个解决方案!呵呵。只需更改 IsValid 函数即可检查最大长度。那么对于这些​​类型的问题,这是公认的 MVC 解决方案吗?
  • @DannyvanderKraan,是的,这是公认的方式。当然,这太糟糕了,以至于我从不使用它,而是使用 FluentValidation.NET 来执行验证。
  • 这里:fluentvalidation.codeplex.com。您可以为可能看起来像这样的视图模型编写一个简单的验证器(一行代码):this.RuleFor(x =&gt; x.Foo).Must((x, foo) =&gt; x.Foo.Length + x.Bar.Length + x.Baz.Length &lt; 20).WithMessage("The combined minimum length of the Foo, Bar and Baz properties should be longer than 20");。现在查看我的答案中您需要使用数据注释编写的代码,并告诉我您更喜欢哪一个。与命令式模型相比,声明式验证模型非常差。
  • 这有点晚了,但是有谁知道您是否必须“打开”其他设置才能允许自定义数据注释?我知道在 web.config 文件中为不显眼的 js 添加命名空间,但在其他任何地方?
  • 我整个上午都在寻找这个!我已经实现了它,不幸的是当IsValid 被称为validationContext 时为空。知道我做错了什么吗? :-(
【解决方案2】:

自我验证模型

你的模型应该实现一个接口IValidatableObject。将您的验证码放入Validate 方法中:

public class MyModel : IValidatableObject
{
    public string Title { get; set; }
    public string Description { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Title == null)
            yield return new ValidationResult("*", new [] { nameof(Title) });

        if (Description == null)
            yield return new ValidationResult("*", new [] { nameof(Description) });
    }
}

请注意:这是一个服务器端验证。它在客户端不起作用。只有在表单提交后才会执行您的验证。

【讨论】:

  • 感谢您回答安德烈。虽然您的解决方案也可以,但我选择了 Darin's,因为它更易于重复使用。
  • yield return new ValidationResult("标题为必填项。", "标题");将添加属性名称,在必要时将验证错误分组以进行显示。
  • 注意这个验证方法只有在所有验证属性都通过验证后才会调用。
  • 这对我很有效,因为我的验证非常具体。添加自定义属性对我来说太过分了,因为验证不会被重复使用。
  • 这就是我要找的。谢谢!
【解决方案3】:

ExpressiveAnnotations给了你这样的可能:

[Required]
[AssertThat("Length(FieldA) + Length(FieldB) + Length(FieldC) + Length(FieldD) > 50")]
public string FieldA { get; set; }

【讨论】:

  • 这太棒了!我的祈祷得到了回应:)
  • 刚刚找到这个答案,它只是节省了大量时间。 ExpressiveAnnotations 很棒!
【解决方案4】:

为了改进达林的答案,可以短一点:

public class UniqueFileName : ValidationAttribute
{
    private readonly NewsService _newsService = new NewsService();

    public override bool IsValid(object value)
    {
        if (value == null) { return false; }

        var file = (HttpPostedFile) value;

        return _newsService.IsFileNameUnique(file.FileName);
    }
}

型号:

[UniqueFileName(ErrorMessage = "This file name is not unique.")]

请注意,错误信息是必需的,否则错误将为空。

【讨论】:

    【解决方案5】:

    背景:

    需要模型验证以确保我们收到的接收数据有效且正确,以便我们可以对这些数据进行进一步处理。我们可以在动作方法中验证模型。内置验证属性是 Compare、Range、RegularExpression、Required、StringLength。但是,我们可能会遇到需要验证属性而不是内置属性的情况。

    自定义验证属性

    public class EmployeeModel 
    {
        [Required]
        [UniqueEmailAddress]
        public string EmailAddress {get;set;}
        public string FirstName {get;set;}
        public string LastName {get;set;}
        public int OrganizationId {get;set;}
    }
    

    要创建自定义验证属性,您必须从 ValidationAttribute 派生此类。

    public class UniqueEmailAddress : ValidationAttribute
    {
        private IEmployeeRepository _employeeRepository;
        [Inject]
        public IEmployeeRepository EmployeeRepository
        {
            get { return _employeeRepository; }
            set
            {
                _employeeRepository = value;
            }
        }
        protected override ValidationResult IsValid(object value,
                            ValidationContext validationContext)
        {
            var model = (EmployeeModel)validationContext.ObjectInstance;
            if(model.Field1 == null){
                return new ValidationResult("Field1 is null");
            }
            if(model.Field2 == null){
                return new ValidationResult("Field2 is null");
            }
            if(model.Field3 == null){
                return new ValidationResult("Field3 is null");
            }
            return ValidationResult.Success;
        }
    }
    

    希望这会有所帮助。干杯!

    参考文献

    【讨论】:

      【解决方案6】:

      回答有点晚,但对于谁在搜索。 您可以通过使用带有数据注释的额外属性轻松做到这一点:

      public string foo { get; set; }
      public string bar { get; set; }
      
      [MinLength(20, ErrorMessage = "too short")]
      public string foobar 
      { 
          get
          {
              return foo + bar;
          }
      }
      

      仅此而已。如果您真的想在特定位置显示验证错误,您可以在视图中添加:

      @Html.ValidationMessage("foobar", "your combined text is too short")
      

      如果您想进行本地化,在视图中执行此操作会派上用场。

      希望这会有所帮助!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-15
        • 2012-03-30
        • 1970-01-01
        相关资源
        最近更新 更多