The scenario is this:
I have a list of countries in a dropdown box and have a select attribute called "Not Found" I had to include this as this was a text field so there is a lot of rubbish!
So when a user creates a from lets say, if they choose the "Not Found" option, I want an error to say "select valid country" pretty easy...
But I am having trouble finding the correct annotation
[???(ErrorMessage = "Select a valid country.")]
public string Country
{
get
set
}
But what attribute do I need to put when the ?s are?
Thanks
I think you want to create a custom attribute. Something like this:
class YourValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
// Do your validation check....and return a ValidationResult
return ValidationResult.Success;
}
}
Then annotate your class with it:
[YourValidationAttribute]
public string Country
{
get
set
}
See this too: https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.validationattribute%28v=vs.110%29.aspx
I would use the predefined CustomValidation Annotation.
https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.customvalidationattribute(v=vs.95).aspx
Related
I'm using entity framework code first in an ASP MVC project, and I'd like to change the error message that appears for validation of a numeric type.
I have a property like
public decimal Amount1 { get; set; }
If I enter a non-number in the field, I get the message: The field Amount1 must be a number. How do I change that message?
For other validations, like Required I can just use the ErrorMessage parameter like: [Required(ErrorMessage = "My message...")]
Is there something similar for validating types?
Thank you.
Unfortunately Microsoft didn't expose any interfaces to change the default messages.
But if you are desperate enough to change these non friendly messages, you can do so by creating validation attribute for decimal, creating corresponding validator and finally register it with DataAnnotationsModelValidatorProvider at the application startup. Hope this helps.
UPDATE:
Sample below
Step 1: Create validation attribute
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false)]
public class ValidDecimalAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
if (value == null || value.ToString().Length == 0) {
return ValidationResult.Success;
}
decimal d;
return !decimal.TryParse(value.ToString(), out d) ? new ValidationResult(ErrorMessage) : ValidationResult.Success;
}
}
Step 2: Create validator
public class ValidDecimalValidator : DataAnnotationsModelValidator<ValidDecimal>
{
public ValidDecimalValidator(ModelMetadata metadata, ControllerContext context, ValidDecimal attribute)
: base(metadata, context, attribute)
{
if (!attribute.IsValid(context.HttpContext.Request.Form[metadata.PropertyName]))
{
var propertyName = metadata.PropertyName;
context.Controller.ViewData.ModelState[propertyName].Errors.Clear();
context.Controller.ViewData.ModelState[propertyName].Errors.Add(attribute.ErrorMessage);
}
}
}
Step 3: Register the adapter in Global.asax under Application_Start() method or Main() method
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(ValidDecimal), typeof(ValidDecimalValidator));
Step 4: Finally decorate your property in your model with this attribute
[ValidDecimal(ErrorMessage = "Only decimal numbers allowed")]
public decimal CPEHours { get; set; }
Hope it helps.
I couldn't find a clean solution. If there is something like [Required] you could override it in the same way. Only option I find is to remove and add another error into the model state. Again NOT the best option if you have better alternates, but does the job. This example only works if you have something like must be a number at the end. You can create a filter with this kind of loop:
foreach (var m in ModelState)
{
var errors = m.Value.Errors;
foreach (var error in errors)
{
if (error.ErrorMessage.EndsWith("must be a number"))
{
errors.Remove(error);
ModelState.AddModelError(m.Key, $"This is my own validation");
}
}
}
While it's not possible to change the whole message, you can at least change the string used to reference the field. Use the [Display(Name = "amount field"] attribute, like:
[BindProperty]
[Display(Name = "line length")]
public decimal? LineLength { get; set; }
If the user enters a string into a field like this, they will at least see an error message that reads "The value 'sdf' is not valid for line length."
Not a complete solution, but good enough in many scenarios.
In an ASP.NET MVC project I'm trying to validate several fields against a regular expression. However I would like to have only one validation message displayed if any of them fails (and highlight the ones failing).
I can make a custom validation for that, and annotating one of them with the id's of the rest kind of work although only highlights the one decorated with the attribute. But it looks to me as an overkill as I just want to reduce the message to one.
In the same form I will try to do the same for two checkboxes, both must be checked.
So as far as I understand if I use the summary to put a generic message won't be able to tell if it's failing for the checkboxes or for the fields.
Is there a simple way of achieving this?
You could write a custom validation that targets your entire view model class. I wrote the following when I wanted to check that at least one property was set. You can see that this targets the class itself and should therefore give you one message.
/// <summary>
/// A configurable class wide attribute that is used to determine if at least one property of a class has received a value.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class SingleValueConfigurableAttribute : ValidationAttribute, IClientValidatable
{
public SingleValueConfigurableAttribute(string errorKey)
{ ErrorMessage = Properties.Settings.Default[errorKey].ToString(); }
public override bool IsValid(object value)
{
var typeInfo = value.GetType();
var propertyInfo = typeInfo.GetProperties();
return propertyInfo.Any(property => null != property.GetValue(value, null));
}
public override string FormatErrorMessage(string name)
{
return ErrorMessage;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = String.IsNullOrEmpty(ErrorMessage) ? FormatErrorMessage(metadata.DisplayName) : ErrorMessage,
ValidationType = "enforcetrue"
};
}
}
Is it possible to use Data Annotation attribute to manipulate text and return a new one after manipulation?
For example I want to validate a string property for having special characters or multiple spaces between words then return a new string to replace the original property's value.
How possible is that using Data Annotation?
A bit late to answer (2 years!), but yes you can modify the value being validated in a custom DataAnnotations attribute. The key is overriding the IsValid(Object, ValidationContext) method of ValidationAttribute and performing a little reflection magic:
public class MyCustomValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext ctx)
{
// get the property
var prop = ctx.ObjectType.GetProperty(ctx.MemberName);
// get the current value (assuming it's a string property)
var oldVal = prop.GetValue(ctx.ObjectInstance) as string;
// create a new value, perhaps by manipulating the current one
var newVal = "???";
// set the new value
prop.SetValue(ctx.ObjectInstance, newVal);
return base.IsValid(value, ctx);
}
}
Corak's suggestion is the best way to do it. However, you can write your base class and using reflection you can do whatever you want with the contents of type members.
This isn't with a data annotation but just an attribute.
So yes via various methods already discussed here:
How to get and modify a property value through a custom Attribute?
Change Attribute's parameter at runtime
It's interesting to note the various solutions from validation to sub classes to 'you can't'
Here is a package that will probably have what you are expecting: Dado.ComponentModel.Mutations
This example will ensure invalid characters are removed from a string. It doesn't introduce validation, but the System.ComponentModel.Annotations can be used alongside Dado.ComponentModel.Mutations.
public partial class ApplicationUser
{
[ToLower, RegexReplace(#"[^a-z0-9_]")]
public virtual string UserName { get; set; }
}
// Then to preform mutation
var user = new ApplicationUser() {
UserName = "M#X_speed.01!"
}
new MutationContext<ApplicationUser>(user).Mutate();
After the call to Mutate(), user.UserName will be mutated to mx_speed01.
I'm using the CustomValidationAttribute like this
[CustomValidation(typeof(MyValidator),"Validate",ErrorMessage = "Foo")]
And my validator contains this code
public class MyValidator {
public static ValidationResult Validate(TestProperty testProperty, ValidationContext validationContext) {
if (string.IsNullOrEmpty(testProperty.Name)) {
return new ValidationResult(""); <-- how can I get the error message from the custom validation attribute?
}
return ValidationResult.Success;
}
}
So how can I get the error message from the custom validation attribute?
I know this is a little of an old post, but I will provide an better answer to the question.
The asker wants to use the CustomValidationAttribute and pass in an error message using the ErrorMessage property.
If you would like your static method to use the error message that you provided when decorating your property, then you return either:
new ValidationResult(string.Empty) or ValidationResult("") or ValidationResult(null).
The CustomValidationAttribute overrides the FormatErrorMessage of its base class and does a conditional check for string.IsNullOrEmpty.
There's no reliable way to get the error message from the attribute. Alternatively you could write a custom validation attribute:
[MyValidator(ErrorMessage = "Foo")]
public TestProperty SomeProperty { get; set; }
like this:
public class MyValidatorAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var testProperty = (TestProperty)value;
if (testProperty == null || string.IsNullOrEmpty(testProperty.Name))
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
In this case the error message will be inferred from the custom validation attribute.
You can look into the following posting to get some ideas on how to do what you want to do (they use JS):
Custom validator error text through javascript?
Hope this helps.
The only way I have found that works is to validate the model from the post back method using TryValidateObject and if it fails, show the model again - then the error will show up.
[HttpPost]
public ActionResult Standard(Standard model)
{
var valContext = new ValidationContext(model, null, null);
var valResults = new List<ValidationResult>();;
bool b = Validator.TryValidateObject(model, valContext, valResults, true);
if(!b)
return View(model);
...
I've tried to override error message when input incorrect data type in input field on HTML form.
For example I have the model like this.
public class Person
{
public string FirstName {get;set;}
public int Age {get;set;}
}
For view, I put text input for Age to get it value.
When type some string in Age text box like 'test' and press submit button.
I got this error message
The value 'xxx' is not valid for Age
However, I want to change this message and try many way. There 's nothing effect this message value.
Please could you help me to solve this problem.
After spending a couple of hours looking around, I see no one really has a suitable answer to this. I have found a solution that works for me so I thought I'd post it.
The problem for me was validating a nullable int. When a non valid value was entered by the user, the default message "The value 'dsfd' is not valid for Issue'.
The solution I came up with was to remove the errors from the model state and add my own.
The classes to perform the validation are below:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false)]
public class ValidInteger : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null || value.ToString().Length == 0)
{
return ValidationResult.Success;
}
int i;
return !int.TryParse(value.ToString(), out i) ? new ValidationResult(ErrorMessage) : ValidationResult.Success;
}
}
public class ValidIntegerValidator : DataAnnotationsModelValidator<ValidInteger>
{
public ValidIntegerValidator(ModelMetadata metadata, ControllerContext context, ValidInteger attribute)
: base(metadata, context, attribute)
{
if(!attribute.IsValid(context.HttpContext.Request.Form[attribute.ObjectId]))
{
var propertyName = metadata.PropertyName;
context.Controller.ViewData.ModelState[propertyName].Errors.Clear();
context.Controller.ViewData.ModelState[propertyName].Errors.Add(attribute.ErrorMessage);
}
}
}
Don't forget you'll also need to register the adapter in the global Application_Start()
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(ValidInteger), typeof(ValidIntegerValidator));
And decorate your property with the new attribute
[ValidInteger(ErrorMessage = "Please enter a valid number for issue")]
The response from wechel dated February 27, 12 worked for me, except that I needed to replace the line
if(!attribute.IsValid(context.HttpContext.Request.Form[attribute.ObjectId]))
with
if (!attribute.IsValid(context.HttpContext.Request.Form[metadata.PropertyName]))
I am guessing that the change is needed because I am using MVC4 and the code snippet provided was written in an earlier version of MVC?
You can use DataAnnotations to override the default error messages in MVC, as well as provide your own validation for whatever fields you need to. See the following:
http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx
http://www.asp.net/mvc/tutorials/validation-with-the-data-annotation-validators-cs
If you are using EF, you will need to create a MetaData class off the EF generated class and then add the data annotations. The end of the 2nd article covers how to write these classes for entity framework.
In your specific case you will want to do something like:
using System.ComponentModel.DataAnnotations;
public class Person
{
public string FirstName {get;set;}
[Range(0, 110, ErrorMessage = "<your error message>")]
public int Age {get;set;}
}
UPDATE I did forget one thing that is easy to overlook, you need to include the following JS files for the data annotations to be picked up client side without having to do a post:
MicrosoftAjax.js
MicrosfotMvcValidation.js
These should be stock in the Scripts folder of your project (if you are using MVC 2), and you need to include them either on your page or in your master page,
<script src="<%= Url.Content("~/Scripts/MicrosoftAjax.js") %>" type="text/javascript"></script>
<script src="<%= Url.Content("~/Scripts/MicrosoftMvcValidation.js") %>" type="text/javascript"></script>
you will also need to include
<% Html.EnableClientValidation(); %>
on the page in question.
The client side validation will not let you submit the form until all fields meet validation requirements, as Kaspars Ozols points out you will still need to call Model.IsValid on your controller.
I just wanted to show the Range attribute error message so I used the answer from wechel and Christna and changed it so the RangeAttribute is used. After adding the Validator class, only a custom Validator needs to be created and registered in the global.asax as shown in wechel's answer.
You also need to add a validation message with name "FieldRangeValidation" to your resource bundle. In my project it contains the following text: "Value must be between {0} and {1}"
public class ValidIntegerRangeValidator : DataAnnotationsModelValidator<RangeAttribute>
{
public ValidIntegerRangeValidator(ModelMetadata metadata, ControllerContext context, RangeAttribute attribute)
: base(metadata, context, attribute)
{
try
{
if (attribute.IsValid(context.HttpContext.Request.Form[metadata.PropertyName]))
{
return;
}
}
catch (OverflowException)
{
}
var propertyName = metadata.PropertyName;
context.Controller.ViewData.ModelState[propertyName].Errors.Clear();
context.Controller.ViewData.ModelState[propertyName].Errors.Add(string.Format(Resources.Resources.FieldRangeValidation, attribute.Minimum, attribute.Maximum));
}
}
if (ModelState["Age"].Errors.Count > 0)
{
ModelState["Age"].Errors.Clear();
ModelState["Age"].Errors.Add("<your error message>");
}