I'm having an issue with a validation error in ASP.NET MVC 4.
I've applied a RegularExpression rule for a field which will accept only numeric input. Should the data contain any other character it should display an error message.
[RegularExpression("^[0-9]*$", ErrorMessageResourceType = typeof(Resources.General), ErrorMessageResourceName = "CodeMustBeNumeric")]
public string Code { get; set; }
Problem is, no matter what's the current culture setting, it always displays the default resource and not the localized one. Some more details to follow.
My resources:
The result:
As you can see, the current culture for all strings is Italian, but not for the validation error. What am I doing wrong?
I thought I was doing something wrong and there was an out-of-the-box way to solve this, but with a deeper search I found out the common solution is just creating a custom validation attribute which inhreits from the ASP.NET one.
So I did, and it works fine. Should anyone need it, here it is:
public class CustomRegularExpressionAttribute : RegularExpressionAttribute
{
public override string FormatErrorMessage(string name)
{
string currentLang = Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName;
CultureInfo ci = new CultureInfo(currentLang);
return HttpContext.GetGlobalResourceObject(ErrorMessageResourceType.Name.ToString(), ErrorMessageResourceName, ci).ToString();
}
public CustomRegularExpressionAttribute(string pattern)
: base(pattern)
{
}
}
And the new call:
[CustomRegularExpression("^[0-9]*$", ErrorMessageResourceType = typeof(Resources.General), ErrorMessageResourceName = "CodeMustBeNumeric")]
public string Code { get; set; }
Related
I have ASP.Net WebAPI based application. Below is my DTO.
public class CustomerTO
{
[Required(ErrorMessage="Name required")]
[StringLength(50, MinimumLength = 3, ErrorMessage = "Name invalid")]
public string Name { get; set; }
[Required(ErrorMessage="CountryId required")]
[Range(1,250,ErrorMessage="CountryId invalid")]
public int Country { get; set; }
}
My API Controller.
[HttpPost]
[AllowAnonymous]
public HttpResponseMessage Post([FromBody]CustomerTO model)
{
if (ModelState.IsValid)
{
//my stuff
}
else
{
var msg = ModelState.SelectMany(s => s.Value.Errors).FirstOrDefault().ErrorMessage;
}
}
If user passed any of the required field as Null, it returns the right Error message mentioned in the Data Annotations while if I pass string for CountryId, it enters into else condition(*ModelState.IsValid = false*)
But the ErrorMessage is empty.
While If I debug & put the below statement in quick watch.
msg = ModelState.SelectMany(s => s.Value.Errors).FirstOrDefault().Exception.Message;
It returns - Could not convert string to integer: en. Path 'Country', line 6, position 14.
Why in this scenario, I am not getting the Error message as CountryId Invalid
How do I get this?
Using a RegularExpressionAttribute does not prevent RangeAttribute from throwing an "Could not convert string to integer" exception. So just filter the right one:
var msg = ModelState.SelectMany(s => s.Value.Errors)
.FirstOrDefault(_ => _.Exception == null)
.ErrorMessage;
As far as I know, it is a common problem: SO question 1, SO question 2.
According to code, from any validation attribute there is creating a wrapper, derived from RequestFieldValidatorBase. Each wrapper calls IsValid method of ValidationAttribute. In the method Validate of RequestFieldValidatorBase passing form value for validation.
So, RequiredAttribute does not fails, because form value is not empty and is not null, and RangeAttribute does not fails, because it has problems converting this value to int.
To achieve your desired behaviour it is recommend to create your own validation attribute or use a RegularExpressionAttribute. You can take a look at this answer.
I believe the range validators dont cater for string entry, i.e. they only fire if it's a valid integer. It doesn't enforce the type passed in.
Try changing your annotation to a regex.
[RegularExpression("([1-9][0-9]*)", ErrorMessage = "Country code invalid")]
public string Country { get; set; }
With reference to this link Integer validation against non-required attributes in MVC
As dirty patch, I modified my property from int to string & decorated it with Regular Expression.
Is it possible to remove property name form the validation message? For example, instead of:
Field 'Name' should not be empty.
I want to show:
Field should not be empty.
I need to do this global, for all validators.
You can do this using the localization customization like so to make the change globally. You can then of course override specific errors with a custom format if you need a one-off change.
ValidatorOptions.ResourceProviderType = typeof(MyResources);
...
public class MyResources {
public static string notempty_error {
get {
return "Field should not be empty.";
}
}
}
easiest way would be to pass a custom message. You can also override it so it always uses that message.
[Required(ErrorMessage = "Field should not be Empty")]
public string Name { get; set; }
The title says it all, but I'll add a bit of background here.
Until recently, I've been using MVC's already-written CompareAttribute to compare two values, in this case a password and its confirmation. It's worked well, except this attribute does not display the display name, set by the [Display(Name = "Name")] attribute of the property being compared.
Here are the two properties being compared:
[Required]
[Display(Name = "New Password")]
public string New { get; set; }
[Compare("New")]
[Display(Name = "Confirm Password")]
public string ConfirmPassword { get; set; }
The validation message reads as follows:
'Confirm Password' and 'New' do not match.
This works, but it's obviously not as good as it should be. The New should read as New Password, as specified by the Display attribute.
I have gotten this working, although not completely. The following implementation (for some reason) fixes the issue of not getting the specified name of the property, but I'm not sure why:
public class CompareWithDisplayNameAttribute : CompareAttribute
{
public CompareWithDisplayNameAttribute(string otherProperty)
: base(otherProperty)
{
}
}
Now, even though this works, client-side validation does not work. I've received an answer in another question that suggests using something like this
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(CompareWithDisplayName), typeof(CompareAttributeAdapter))
in my Global.asax, however the CompareAttributeAdapter doesn't actually exist.
So here I am. I've got the Display attribute being used properly by my custom CompareWithDisplayName attribute, but client-side validation missing altogether.
How can I make client-side validation work with this solution in the cleanest way possible?
If you want your custom compare attribute to work with clientside validation you will need to implement IClientValidatable. This has GetValidationRules which is where you can do any custom validation you might wish.
Example
public class CompareWithDisplayNameAttribute : CompareAttribute, IClientValidatable
{
public CompareWithDisplayNameAttribute(string otherProperty)
: base(otherProperty)
{
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
ModelMetadata metadata, ControllerContext context)
{
// Custom validation goes here.
yield return new ModelClientValidationRule();
}
}
Before I start ... I can't easily migrate the project to MVC3. So.
The problem I'm having is that I've defined a custom validator attribute to check the max AND min length of a string property, StringLengthInRangeAttribute.
When the Controller calls ModelState.IsValid, on a list of Passengers only the validation of a Date property is throwing invalid, when nothing has been supplied. I guess that means my problem is not with the custom validator but all validation?
Update (additional info for clarity):
I have two symptoms of this problem :
1.The Required validator on the strings doesn't fire when they are empty
and
2.My custom validator never gets called (a breakpoint I set never gets hit).
Model:
public class Passenger
{
[Required(ErrorMessageResourceType = typeof(Resources.Messages.Passenger),
ErrorMessageResourceName = "RequireNumber")]
public int Number { get; set; }
[Required(ErrorMessageResourceType = typeof(Resources.Messages.Passenger),
ErrorMessageResourceName = "RequireSurname")]
[StringLengthInRange(MinLength = 2, MaxLength = 30, ErrorMessageResourceType = typeof(Resources.Messages.Passenger),
ErrorMessageResourceName = "MaxLengthSurname")]
public string Surname { get; set; }
}
Custom Validator:
public class StringLengthInRangeAttribute:ValidationAttribute
{
public int MinLength { get; set; }
public int MaxLength { get; set; }
public override bool IsValid(object value)
{
if (((string)value).Length < MinLength)
{
return false;
}
if (((string)value).Length > MaxLength)
{
return false;
}
return true;
}
}
Controller Action:
public ViewResult TailorHoliday(List<SearchAndBook.Models.ViewModels.Passenger> passengers,
int leadPassengerIndex)
{
if(!ModelState.IsValid)
{
return View("PassengerDetails", GetBookingState(_currentSession));
}
//...
return View();
}
Any advice appreciated. This is the first time I've used Data Annotations, so I'm quite prepared to feel stupid for missing something!
If you check the content of the ModelState property (in the debugger) you should be able to see every property that gets validated in ModelState.Keys and for each value you see the actual state in the ModelState.Values. If I look at your controller you seem to post a whole list of passengers. You should check as well, whether your values are really posted (use Firebug or Fiddler). Maybe your fields are outside the form.
Maybe you should show part of your view as well to spot the bug.
I'm building a MVC web application with C#. Since the site will be multilingual, I've implemented my own ResourceManager. This class is responsible for fetching the required resource strings from a database/cache depending on the currents thread culture and works fine so far.
My problem is, I'd like to use the my custom ResourceManager solution to fetch validation error messages when for example using the Required Attribute on a property. Can this be done?
The RequiredAttribute allows to use a custom resource manager:
[Required(
ErrorMessageResourceType = typeof(CustomResourceManager),
ErrorMessageResourceName = "ResourceKey")]
public string Username { get; set; }
UPDATE:
Another possibility is to write your custom attribute:
public class CustomRequiredAttribute : RequiredAttribute
{
public override string FormatErrorMessage(string name)
{
return YourCustomResourceManager.GetResource(name);
}
}