NET MVC: 通过 DataAnnote 进行自定义验证

我有一个具有4个属性的 Model,它们的类型是 string。我知道您可以通过使用 StringLlength 注释来验证单个属性的长度。然而,我想验证4个属性组合的长度。

使用数据注释的 MVC 方法是什么?

我这样问是因为我对 MVC 还是个新手,想在制定自己的解决方案之前用正确的方法来做。

172345 次浏览

You could write a custom validation attribute:

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

and then you might have a view model and decorate one of its properties with it:

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

Self validated model

Your model should implement an interface IValidatableObject. Put your validation code in Validate method:

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

Please notice: this is a server-side validation. It doesn't work on client-side. You validation will be performed only after form submission.

Background:

Model validations are required for ensuring that the received data we receive is valid and correct so that we can do the further processing with this data. We can validate a model in an action method. The built-in validation attributes are Compare, Range, RegularExpression, Required, StringLength. However we may have scenarios wherein we required validation attributes other than the built-in ones.

Custom Validation Attributes

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

To create a custom validation attribute, you will have to derive this class from 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;
}
}

Hope this helps. Cheers !

References

ExpressiveAnnotations gives you such a possibility:

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

A bit late to answer, but for who is searching. You can easily do this by using an extra property with the data annotation:

public string foo { get; set; }
public string bar { get; set; }


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

That's all that is too it really. If you really want to display in a specific place the validation error as well, you can add this in your view:

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

doing this in the view can come in handy if you want to do localization.

Hope this helps!

To improve Darin's answer, it can be bit shorter:

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

Model:

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

Do note that an error message is required, otherwise the error will be empty.