I have a class that is called MyMethodQuery that contains a property of entry of my web method
[DataContract()]
public class MyMethodQuery
{
[DataMember(IsRequired = true)]
[StringLength(100, MinimumLength = 10)]
public string Id{ get; set; }
}
My Web Method :
public string MyMethod(MyMethodQuery MyMethodQuery)
{
return "it's ok !";
}
I want to throw an exception if I leave the empty property. But it doesn't work in spite of the attribute StringLength.
I test my method with SoapUI.
These attributes are not supposed to work in this context "out of the box". Validator class is the entry point to the world of validation, and you will have to invoke it manually.
See this answer for an example.
[DataContract()]
public class MyMethodQuery: IValidatableObject
{
[DataMember(IsRequired = true, EmitDefaultValue = false)]
[StringLength(500, MinimumLength = 5)]
public string Id { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Id.Length < 1)
{
yield return new ValidationResult("error");
}
}
}
But i have not error return if Id is empty
Related
I have following object
public class TestClass
{
[Required]
public int TestId { get; set; }
}
I validate using:
List<ValidationResult> results = new List<ValidationResult>();
var vc = new ValidationContext(data);
if (Validator.TryValidateObject(data, vc, results, true))
return;
This validates perfectly fine if data is of type TestClass but not when I pass list of TestClass items (List<TestClass>)
How can I validate the items withing a list without iterating?
TryValidateObject expects an object and not a list of. You have to write a helper class. Furthermore it even doesn't recursively check the Validation. See this SO question for more...
Usually I add the Validate method in the class that I need to validate, and I foreach the property that has a list of T.
For example:
public class ClassToValidate : IValidatableObject
{
// Property with a non-custom type
[Required(AllowEmptyStrings = false, ErrorMessage = "\"Language\" is required. It can not be empty or whitespace")]
public string Language { get; set; }
// Property with a custom type
public Source Source { get; set; }
// Property with a custom type list
public List<Streaming> StreamingList { get; set; } = new List<Streaming>();
// Validate method that you need to implement because of "IValidatableObject"
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// The validation result that will be returned
List<ValidationResult> result = new List<ValidationResult>();
// Language doesn't need to be added in the Validate method because it isn't a custom object
// Custom single T object to be validated
Validator.TryValidateObject(Source, new ValidationContext(Source), result, true);
// Custom list<T> object to be validated
foreach (var streaming in StreamingList)
{
Validator.TryValidateObject(streaming, new ValidationContext(streaming), result, true);
}
return result;
}
}
public class Source
{
[Range(16, 192, ErrorMessage = "\"Bitrate\" must be between 16 and 192")]
public int? Bitrate { get; set; }
public string Codec { get; set; }
}
public class Streaming
{
[Required(AllowEmptyStrings = false, ErrorMessage = "\"Url\" can not be empty")]
public string Url { get; set; }
[Range(0, 1000, ErrorMessage = "\"Offset\" must be between 0 and 1000")]
public int Offset { get; set; }
}
I'm using Fluent Validation v5.5 with ASP.NET v5.2.2 and I'm getting some inconsistent results with the validation.
My view model is:
public class QuoteViewModel
{
[Display(Name = #"Day")]
public int DateOfBirthDay { get; set; }
[Display(Name = #"Month")]
public int DateOfBirthMonth { get; set; }
[Display(Name = #"Year")]
public int DateOfBirthYear { get; set; }
[Display(Name = #"Gender")]
public Gender? Gender { get; set; }
[Display(Name = #"State")]
public int StateId { get; set; }
}
My controller method is:
public ActionResult Quote(QuoteViewModel viewModel)
{
var _validator = new QuoteValidator();
var results = _validator.Validate(viewModel);
if (!ModelState.IsValid)
{
return Json(false);
}
return Json(true);
}
My validator is:
public class QuoteValidator : AbstractValidator<QuoteViewModel>
{
public QuoteValidator()
{
RuleFor(x => x.Gender).NotEmpty();
RuleFor(x => x.StateId).NotEmpty();
RuleFor(x => x.DateOfBirthDay).NotEmpty().InclusiveBetween(1, 31);
RuleFor(x => x.DateOfBirthMonth).NotEmpty().InclusiveBetween(1, 12);
RuleFor(x => x.DateOfBirthYear).NotEmpty().LessThanOrEqualTo(DateTime.UtcNow.Year);
}
}
I'm running a test that posts all blank value form fields. Thus the view model fields retain default values after the view model object is created.
For comparison, in the controller I'm running the validation explicitly and the results aren't consistent with the validation result in ModelState.
ModelState is showing 4 errors, all triggered by NotEmpty rules. NotEmpty on the nullable enum Gender doesn't seem to trigger.
The explicit validation is returning 7 out of 8 errors, the LessThanOrEqualTo rule won't fire since the DateOfBirthYear defaults to zero.
My pain point is I can't figure out why ModelState is missing the NotEmpty error on the nullable enum Gender.
The only way I've been able to trigger that error is to post just the Gender value.
Please help.
EDIT:
After stepping through some code, it appears that the issue is related to the Fluent Validation RequiredFluentValidationPropertyValidator. The Gender field is a nullable value type which is bound to null. The following snippet from RequiredFluentValidationPropertyValidator prevents validation:
ShouldValidate = isNonNullableValueType && nullWasSpecified;
!ModelState.IsValid doesn't use your validation result it uses defaulf MVC validation (that can be added through DataAnnotations). You have to check !results.IsValid instead which contains the validation result of your QuoteValidator.
If you want to use default ModelState.IsValid you have to mark your model with validator attribute:
[Validator(typeof(QuoteValidator))]
public class QuoteViewModel
{
[Display(Name = #"Day")]
public int DateOfBirthDay { get; set; }
[Display(Name = #"Month")]
public int DateOfBirthMonth { get; set; }
[Display(Name = #"Year")]
public int DateOfBirthYear { get; set; }
[Display(Name = #"Gender")]
public Gender? Gender { get; set; }
[Display(Name = #"State")]
public int StateId { get; set; }
}
And add the following line to your Application_Start method:
protected void Application_Start() {
FluentValidationModelValidatorProvider.Configure();
}
Is there any way of using data annotations to compare two form field (eg. to confirm an email address) are the same, before allowing the form to be posted?
eg. can the regular expression data annotation use the match function to reference another property in a ViewModel?
Use the CompareAttribute
public string EmailAddress {get; set;}
[Compare(nameof(EmailAddress), ErrorMessage = "Emails mismatch")]
public string VerifiedEmailAddress { get; set; }
As one possibe option self-validation:
Implement an interface IValidatableObject with method Validate, where you can put your validation code.
public class TestModel : IValidatableObject
{
public string Email{ get; set; }
public string ConfirmEmail { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Email != ConfirmEmail)
{
yield return new ValidationResult("Emails mismatch", new [] { "ConfirmEmail" });
}
}
}
Please notice: this is only server-side validation.
I have a Model with 4 properties which are of type string. I know you can validate the length of a single property by using the StringLength annotation. However I want to validate the length of the 4 properties combined.
What is the MVC way to do this with data annotation?
I'm asking this because I'm new to MVC and want to do it the correct way before making my own solution.
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.
ExpressiveAnnotations gives you such a possibility:
[Required]
[AssertThat("Length(FieldA) + Length(FieldB) + Length(FieldC) + Length(FieldD) > 50")]
public string FieldA { get; set; }
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.
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
Code Project - Custom Validation Attribute in ASP.NET MVC3
Haacked - ASP.NET MVC 2 Custom Validation
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!
I'm trying to write my own Custom Validation attribute but I'm having some problems.
The attribute I'm trying to write is that when a user logs in, the password will be compared against the confirmation password.
namespace Data.Attributes
{
public class ComparePassword : ValidationAttribute
{
public string PasswordToCompareWith { get; set; }
public override bool IsValid(object value)
{
if (PasswordToCompareWith == (string)value)
{
return true;
}
return false;
}
}
Now my problem is when i'm trying to set the attribute like this in the model file:
[Required]
[ComparePassword(PasswordToCompareWith=ConfirmPassword)]
public string Password { get; set; }
[Required]
public string ConfirmPassword { get; set; }
}
I get the following error:
Error 1 An object reference is required for the non-static field, method,
or property 'Project.Data.Models.GebruikerRegistreerModel.ConfirmPassword.get'
It seems that VS is not accepting the confirmpassword in the PasswordToCompareWith=ConfirmPassword part.
What am I doing wrong?
According to this link http://devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-1 there is an special validation attribute now in MVC3:
public class RegisterModel
{
// skipped
[Required]
[ValidatePasswordLength]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation do not match.")]
public string ConfirmPassword { get; set; }
}
CompareAttribute is a new, very useful validator that is not actually
part of
System.ComponentModel.DataAnnotations,
but has been added to the
System.Web.Mvc DLL by the team. Whilst
not particularly well named (the only
comparison it makes is to check for
equality, so perhaps EqualTo would be
more obvious), it is easy to see from
the usage that this validator checks
that the value of one property equals
the value of another property. You can
see from the code, that the attribute
takes in a string property which is
the name of the other property that
you are comparing. The classic usage
of this type of validator is what we
are using it for here: password
confirmation.
Sorry to disappoint you but handling such a simple case like yours using Data Annotations could be a pain. You may take a look at this post.
FoolProof http://foolproof.codeplex.com/ seems to be the best solution.
public class SignUpViewModel
{
[Required]
public string Password { get; set; }
[EqualTo("Password", ErrorMessage="Passwords do not match.")]
public string RetypePassword { get; set; }
}
It is better than suggested PropertiesMustMatchAttribute as it adds the validation error for the "RetypePassword' instead of the global model level as PropertiesMustMatchAttribute does.
I don't know why this is made out to be such a big deal, just do this:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class ComparePassword: ValidationAttribute
{
public ComparePassword()
: base("Passwords must match.") { }
protected override ValidationResult IsValid (object value, ValidationContext validationContext)
{
if (value == null) return new ValidationResult("A password is required.");
// Make sure you change YourRegistrationModel to whatever the actual name is
if ((validationContext.ObjectType.Name != "YourRegistrationModel")
return new ValidationResult("This attribute is being used incorrectly.");
if (((YourRegistrationModel)validationContext.ObjectInstance).ConfirmPassword != value.ToString())
return new ValidationResult("Passwords must match.");
return ValidationResult.Success;
}
}
Now all you need to do is add [ComparePassword] to your password property, nothing to pass... simple and fairly clean
You can't pass a reference type to an attribute unless you do some rather lame reflection code.
In this situation, I would think creating a custom model binder would be a better idea and then checking the Password and ComparePassword at that point.
you need a STATIC method in your case:
EXAMPLE:
public static ValidationResult ValidateFrequency( double frequency, ValidationContext context )
{
if( context == null )
{
return ( ValidationResult.Success );
}
}
just as an example:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Web.Mvc;
using System.Web.Security;
namespace GDNET.Web.Mvc.Validation
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class ValidatePasswordLengthAttribute : ValidationAttribute, IClientValidatable
{
private const string defaultErrorMessage = "'{0}' must be at least {1} characters long.";
private readonly int minRequiredPasswordLength = Membership.Provider.MinRequiredPasswordLength;
public ValidatePasswordLengthAttribute()
: base(defaultErrorMessage)
{
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, minRequiredPasswordLength);
}
public override bool IsValid(object value)
{
string valueAsString = value as string;
return (valueAsString != null && valueAsString.Length >= minRequiredPasswordLength);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
return new[]
{
new ModelClientValidationStringLengthRule(FormatErrorMessage(metadata.GetDisplayName()), minRequiredPasswordLength, int.MaxValue)
};
}
}
}
source: https://code.google.com/p/gdnetprojects/source/browse/trunk/Experiments/Common/GDNET.Web.Mvc/Validation/ValidatePasswordLengthAttribute.cs?r=69