I'm using ASP.NET MVC 3 with Fluent Validation. I'd like all my error messages to be worded and formatted the same, whether they are validation error messages or model binding error messages.
Let's say I have the following view-model:
[Validator(typeof(PersonValidator))]
public class Person
{
[ScaffoldColumn(false)] public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
To validate this using Fluent Validation, I might use something like this:
public class EditorValidator : AbstractValidator<EditorModel>
{
public EditorValidator()
{
RuleFor(model => model.Month.Value).InclusiveBetween(0, 120)
}
}
If the user enters "abc" for Age, this causes a model binding error, not a validation error. This is because "abc" is not an int. The system never even gets to the point of asking whether "abc" is between 0 and 120, because "abc" cannot be stored in Age.
This is fine and makes sense. The problem is that the resulting error message is:
The field Age must be a number.
I'd like the message to be formatted and worded like other error message produced by Fluent Validation. In this case, I would like:
'Age' must be a number.
I realize this is only a subtle difference, but I'd like to have control of model binding error messages.
How can I customize the model binding error messages to match the error messages used by Fluent Validation?
I'm not sure if there's another way to do this, but I use Data Annotations Extensions, also available via NuGet (Install-Package DataAnnotationsExtensions.MVC3) for this exact type of thing. This package will give you an IntegerAttribute, and from there you can specify an error message like so:
[Integer(ErrorMessage = "'Age' must be a number.")]
public int Age { get; set; }
Take a look at my answer here:
How to change 'data-val-number' message validation in MVC while it is generated by #Html helper
Actually, it's a very common question that you asked, so you should've googled the stackoverflow prior to posting.
Related
After researching various methods to implement custom form validation rules in MVC I have found, what I originally considered to be a straightforward bit of validation, to be rather more complex that I anticipated.
I have 2 text inputs, one or the other (or both) need to be populated. If both are NullOrEmpty we need to throw an error.
I have found DataAnnotations to have it's limitations when attempting to validate on multiple fields, giving me highlighting on both inputs and throwing a single error. Is this some beginners naivity?
I also played around with FluentValidation and was unable to get the results I was after.
Currently I am throwing the error in the Controller using:
ModelState.AddModelError("PropertyName", "You need to enter a Property Number or a Property Name")
ModelState.AddModelError("PropertyNumber", String.Empty)
This gives me highlighting on both fields and server-side validation. I am now finding it difficult binding custom client-side validation without using DataAnnotations.
Does anyone have any advice on how to do this properly? Any help or suggestions would be greatly appreciated. I need validation on the server/client, on both fields, with highlighting and a single error message.
Thanks in advance.
[Fool proof][1] validation library covers almost all kind of validation scenarios.
[RequiredIf]
[RequiredIfNot]
[RequiredIfTrue]
[RequiredIfFalse]
[RequiredIfEmpty]
[RequiredIfNotEmpty]
[RequiredIfRegExMatch]
[RequiredIfNotRegExMatch]
Applied to a Model:
public class CreateUserViewModel
{
[Required]
public string Username { get; set; }
[Required]
public string Department { get; set; }
[RequiredIfEmpty(ErrorMessage="error message"]
public string Role { get; set; }
}
I'm trying to create a MVC model validation attribute that fills in the following situation:
I have created several models (Birthday and PhoneNumber being excellant examples) that are submitted over multiple input fields on the view (month, day, year; areacode, exchange, suffix).
public class PhoneNumber
{
[RegularExpression("^([2-9][0-8][0-9])$", ErrorMessage = "Invalid Area Code")]
public virtual string AreaCode { get; set; }
[RegularExpression("^([2-9][0-9][0-9])$", ErrorMessage = "Invalid Phone Exchange")]
public virtual string Exchange { get; set; }
[RegularExpression("^([0-9][0-9][0-9][0-9])$", ErrorMessage = "Invalid Phone Suffix")]
public virtual string Suffix { get; set; }
}
I often nest these models inside other models (Person has a PhoneNumber and a Birthday, for example). Sometimes, in my views, a PhoneNumber is required, and sometimes it isn't.
I can handle these situations on the server side by using implementing the class as an IValidatableObject, but I run into trouble if I want to do client side validation, or even just do server side validation via attributes.
I imagine I will have to build my own custom validation attribute but I'm not even sure where to start in accessing object and attribute information on multiple levels. Has anyone encountered anything like this? Any good ideas for pointing me in the right direction?
Thanks in advance!
--------Update------
By using IClientValidatable, and GetClientValidationRules, I have access to the ModelMetadata can get the type of the container. The problem is that the container is birthday or phone number, not the type of the top level model, which is what is most important here. The ModelMetadata hasn't populated the Model property yet because one may not exist yet. What I'm really trying to do is get the type of the top level model. Anyone have any insight?
Depending on how you are writing your view, I think you may want to look at an extension called BeginCollectionItem http://nuget.org/packages/BeginCollectionItem
Validation gets a little messed up if you are doing
#Html.TextBoxFor( m => number.AreaCode )
instead of
#Html.TextBoxFor( m => m.AreaCode )
I am trying to realize valition on data type. I have used DataAnnotations, but for data type it's not showing customized message
for example when I' am trying enter string data into int typed field. How I can customize messages in this case?
If I had to guess, you sound like you want a custom message to display when validating one or more fields in your model. You can subclass the DataAnnotations.ValidationAttribute class and override the IsValid(object) method and finally setting a custom ErrorMessage value (where ErrorMessage already belongs to the ValidationAttribute class)
public class SuperDuperValidator : ValidationAttribute
{
public override bool IsValid(object value)
{
bool valid = false;
// do your validation logic here
return valid;
}
}
Finally, decorate your model property with the attribute
public class MyClass
{
[SuperDuperValidator(ErrorMessage="Something is wrong with MyInt")]
public int MyInt { get; set; }
}
If you're using out-of-the-box MVC3, this should be all you need to propertly validate a model (though your model will probably differ/have more properties, etc) So, in your [HttpPost] controller action, MVC will automagically bind MyClass and you will be able to use ModelState.IsValid to determine whether or not the posted data is, in fact, valid.
Pavel,
The DataAnnotations DataType attribute does not affect validation. It's used to decide how your input is rendered. In such a case, David's solution above works.
However, if you want to use only the built-in validation attributes, you probably need to use the Range attribute like this:
[Range(0, 10, ErrorMessage="Please enter a number between 0 and 10")]
public int MyInt { get ; set ;}
(Of course, you should really be using the ErrorMessageResourceName/Type parameters and extract out hard-coded error message strings into resx files.)
Make sure to let MVC know where to render your error message:
<%= Html.ValidationMessageFor(m => m.MyInt) %>
Or you can just use EditorForModel and it will set it up correctly.
I don't think this has been answered because I have the same issue.
If you have a Model with a property of type int and the user types in a string of "asd" then the MVC3 framework binding/validation steps in and results in your view displaying "The value 'asd' is not valid for <model property name or DisplayName here>".
To me the poster is asking can this message that the MVC3 framework is outputting be customized?
I'd like to know too. Whilst the message is not too bad if you label your field something that easily indicates an number is expected you might still want to include additional reasons so it says something like:
"The value 'asd' is not valid for <fieldname>; must be a positive whole number."
So that the user is not entering value after value and getting different error messages each time.
I'm developing in mvc 3 and have a little question.
I want to change the default error message for invalid data type.
let say I've a model with the prop Price, and I want his error message for input "aaa" will be "The only value you can enter here is a number".
what is the easiest way of doing that?
(I want to do it for all of my models)
You could use a Regular Expression data annotation on your model property, e.g.:
[RegularExpression(#"^[0-9\.]*$", ErrorMessage="The only value you can enter here is a number")]
public double Price { get; set; }
You should approach validation from a white list point of view - i.e. what should be allowed through, as opposed to a black list, which would be what is invalid.
More information here:
http://www.asp.net/mvc/tutorials/mvc-music-store-part-6
Hope this helps!
Sam
http://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx
Assuming that you are working with entity framework or Linq to SQL and your class name is Product. here is the example for that. create a partial class like below;
[MetadataType(typeof(Product.MetaData))]
public partial class Product {
private class MetaData {
[Required(ErrorMessage = "The only value you can enter here is a number")]
public decimal Price { get; set; }
}
}
you should add the following using statement in order to use dataanotations for validation;
using System.ComponentModel.DataAnnotations;
My baldness is growing more rapidly than it should be. I first posted this question a couple days ago. I now know the problem and have it working... sort of. Another problem surfaced in it's place.
To solve the previous problem, I manually created the name to requestedDays[{0}].DateOfLeave where {0} was a Guid. This allowed my controller to properly receive the List<> of values.
Using this article's method, the name generated is requestedDays[{0}].DayRequested.DateOfLeave which my controller doesn't properly receive because the name has the class in it, DayRequested.DateOfLeave.
[Authorize, HttpPost]
public ActionResult Create(LeaveRequest leaveRequest, List<DayRequested> requestedDays)
{
}
I have tried to figure out work-arounds with the manual generation, but nothing I have tried works thus far. You can see my validation method here. I do know about the second part of Sanderson's article on validation however, it is quite hard to validate something that isn't being passed into the method.
This is my ViewModel I am using in my partial view.
public class LeaveRequestRow
{
public LeaveRequestRow(DayRequested dayRequested, List<SelectListItem> leaveRequestType)
{
this.DayRequested = dayRequested;
this.LeaveRequestType = leaveRequestType;
}
public List<SelectListItem> LeaveRequestType { set; get; }
public DayRequested DayRequested { set; get; }
}
Does anyone have any ideas on how to proceed? Should I convert my dropdown to a jQuery build control and stop using the ViewModel?
Binding 1-N controller arguments of complex types can be kind of tricky.
Your code examples are not meshing with my fried end of day Friday brain but I'll give it a shot.
Assuming the LeaveRequest class looks like this:
public class LeaveRequest {
public string Text { get; set; }
public string Number { get; set; }
}
The posted form keys must be:
leaveRequest.Text
leaveRequset.Number
That is the easy part. The 1-N binding of a list of DayRequested gets a little weird. Say the DayRequested object looks like this:
public class DayRequested {
public string Words { get; set; }
public string Data { get; set; }
}
Your posted form keys look like:
requestedDays[0].Data
requestedDays[0].Words
requestedDays[1].Data
requestedDays[1].Words
requestedDays[2].Data
requestedDays[2].Words
requestedDays[3].Data
requestedDays[3].Words
The default MVC binder should then trun all 10 form values into your two method arguments ... a POCO and a List of POCOs.
I have solved this, though not as elegantly as I had hoped. All TextBoxFor had to be changed to TextBox along with the addtional changes needed with doing this. The names then were correctly generated and I could move on. This did break the ability for the validation message to appear next to the field, though ValidationSummary still does work. I will be working on fixing that later on and post code samples and a solution on my website.