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);
}
}
Related
I've
[Required]
[Display(Name ="Email")]
public string Email { get; set; }
[Required]
[Display (Name = "Password")]
public string Password { get; set; }
In my ViewModel. I'm able to localize this. Additionally, I was able to put a different localization to the 'Required' message [without specifying ResourceType and ResourceName manually] than the default Microsoft message using the resource file. How I did that? Here is the link:
https://stackoverflow.com/a/41385880/931305
Now, I want to remove the 'Name' attribute of the 'Display'. Because most of the time Display Name is always going to be the same as the actual Property name. If you notice both are 'Email'/'Password'. So it will make the code looking clean.
I was able to do this in classic ASP.NET MVC. Here is the link:
https://haacked.com/archive/2011/07/14/model-metadata-and-validation-localization-using-conventions.aspx/
Now, how do I do this in .NET Core 5? I'm unable to use IValidationAttributeAdapterProvider to inject 'Display'. [I was getting all 'validation' attributes, but not Display]
The best and standard solution is to using localization in ASP.NET Core application.
In summary, the steps to localize your application are rough as follows:
Add the required localization services
Configure the localization middleware and if necessary a culture
provider
Inject IStringLocalizer into your controllers and services to
localize strings
Inject IViewLocalizer into your views to localize strings in views
Add resource files for non-default cultures
Add a mechanism for users to choose their culture
Take a look at this article for a detailed walkthrough.
You could write an attribute like so
public sealed class MyDisplayAttribute : DisplayNameAttribute, IModelAttribute
{
private string _name = string.Empty;
public MyDisplayAttribute(string displayName)
{
_name= displayName;
}
public override string DisplayName => _name;
public string Name => nameof(MyDisplayAttribute);
}
usage:
public class MyModel
{
[MyDisplay("MyString")]
public string MyString { get; set; }
}
I am creating web application in ASP.Net MVC 5.
I need to add user defined languages. (So, it can work in any language).
I have added English text/messages in resources file.
While, for other languages , resources will be generated run time in App_GlobalResources folder.
With this custom resources, I can able to show labels (, buttons etc.) as per selected language.
But, I have issue with ErrorMessage , which is given as attribute in properties of model.
Model classes are in class library, and reference of class library project is added in MVC.
So, can't access resources from App_GlobalResources folder.
And, if I add resources under Project of model class, I can give customize message with following code.
[Required(ErrorMessage = "*")]
[System.Web.Mvc.Compare("Password", ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = "PasswordCompare")]
public string ConfirmPassword { get; set; }
But, with this code, I can't use App_GlobalResources.
What will be solution in this scenario?
Finally, I got solution:
Created custom attribute class.
[Required(ErrorMessage = "*")]
[CompareCustomAttribute("Password", ClassKey = "Resources", ResourceKey = "PasswordCompare")]
public string ConfirmPassword { get; set; }
Custom Attribute class is inheriting CompareAttribute class.
public sealed class CompareCustomAttribute : System.Web.Mvc.CompareAttribute
{
public CompareCustomAttribute(string otherProperty)
: base(otherProperty)
{
}
public string ResourceKey { get; set; }
public string ClassKey { get; set; }
public override string FormatErrorMessage(string name)
{
return Convert.ToString(HttpContext.GetGlobalResourceObject(this.ClassKey, this.ResourceKey));
}
}
In overridden FormatErrorMessage method, I have put code to get customized error message from Global Resources.
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; }
I am using NancyFX for my API Gateway and I have a model such as the following:
public class CreatePerson
{
[Required]
public string FirstName {get;set;}
[Required]
public string LastName {get;set;}
[Required]
[Phone]
public string Phone {get;set;}
[Required]
[MyCustomValidationRule]
public string ImagePath {get;set;}
}
It uses both a custom MyCustomValidationRule attribute as well as the provided Phone and Required attributes.
In my module I have the following:
public class PersonModule
{
public PersonModule()
{
Post["/",true] = async (context,cancel)=>
{
var request = this.BindAndValidate<CreatePerson>();
if(!ModelValidationResult.IsValid)
{
//THIS NEVER HITS
}
}
}
}
The [Required] attributes are working and if I omit any of the properties, it works fine. However, if I pass in an invalid phone (such as sdfsdgsdg or I do something that clearly breaks MyCustomValidationRule attribute, it is not caught. Furthermore, I have placed a breakpoint in the constructor of the MyCustomValidationRule attribute, and it never hits.
Why is this not hitting?
#TheJediCowboy. I had the same problem in my project.
I can't check my theory now, but I think you should add ValidationAdapter for each kind of attribute, example you can find on github
I think only 4 validation attribute will work in default package(Range, Regex, Required, StringLength), please check list of validation adapters github
For custom attributes required add custom adapter, I think.
UPDATED
This solution checked and works good.
For validation attribute (except Range, Regex, Required and StringLength) required add validation adapter (or override exiting). Nancy use all validation adapters that are in the project.
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();
}
}