ASP.NET MVC:通过DataAnnotation进行自定义验证


110

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

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

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


2
您是否看过流利验证?它处理复杂的场景要比数据注释要好得多
levelnis 2013年

看一看强烈推荐的解决方案...。 dotnetcurry.com/ShowArticle.aspx?ID=776
Niks

谢谢回答。我将检查Fluent Validation,从没听说过。而Niks和Darin基本上会写出您发布的链接上的文章解释了什么。所以,谢谢...很棒的东西!
Danny van der Kraan

Answers:


177

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

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; }
}

4
感谢您的回答,我接受了您的回答。实际上感到有些尴尬。您已写出整个解决方案!呵呵。只需更改IsValid函数即可检查最大长度。那么,对于这些类型的问题,这是公认的MVC解决方案吗?
Danny van der Kraan

7
@DannyvanderKraan,是的,这是公认的方式。当然,这是如此糟糕,以至于我从不使用它,而是使用FluentValidation.NET来执行验证。
Darin Dimitrov

11
此处:fluentvalidation.codeplex.com。你可以只写那些可能是这个样子的视图模型一个简单的验证器(一个单一的代码行)this.RuleFor(x => x.Foo).Must((x, foo) => x.Foo.Length + x.Bar.Length + x.Baz.Length < 20).WithMessage("The combined minimum length of the Foo, Bar and Baz properties should be longer than 20");。现在看一下我的答案中需要用数据注释编写的代码,并告诉我您喜欢哪一种。与命令式模型相比,声明式验证模型非常差。
Darin Dimitrov

1
这有点晚了,但是有人知道您是否必须“打开”其他设置才能允许自定义数据注释?我知道要在web.config文件中为不打扰的js添加一个名称空间,但是在其他地方呢?
何塞

1
我一直在寻找一切早上!我已经实现了它,不幸IsValidvalidationContext是何时被称为the 为null。知道我做错了什么吗?:-(
格林(Grimm The Opiner)

95

自我验证的模型

您的模型应该实现一个接口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是因为它更可重用。
Danny van der Kraan

6
yield return new ValidationResult(“标题为必填。”,“标题”); 将添加属性名称,在将验证错误分组以便在必要时显示时很有用。
Mike Kingscott 2013年

5
请注意,仅在所有验证属性都通过验证后才调用此验证方法。
Pedro

3
这对我来说效果很好,因为我的验证非常具体。对我而言,添加自定义属性对我来说是过大的,因为验证不会被重复使用。
史蒂夫·S

这就是我想要的。谢谢!
Amol Jadhav

26

ExpressiveAnnotations为您提供了这样的可能性:

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

这太棒了!我的祈祷得到了回答:)
Korayem

刚刚找到了这个答案,它只是节省了很多时间。富有表现力的注释很棒!
布拉德

10

为了改善达林的答案,它可以更短一些:

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.")]

请注意,必须输入错误消息,否则错误将为空。


8

背景:

需要进行模型验证,以确保我们收到的接收数据正确有效,以便我们可以对该数据进行进一步处理。我们可以使用动作方法来验证模型。内置的验证属性是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;
    }
}

希望这可以帮助。干杯!

参考资料


1

回答迟了一点,但要寻找谁。您可以通过在数据注释中使用额外的属性来轻松实现此目的:

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")

如果要进行本地化,可以在视图中进行操作。

希望这可以帮助!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.