Scenario :-
I am developing MVC 4 application, the website will run in several languages and will be hosted on Azure.
For localizing we are relying on the database rather than resource bundle approach.
Problem :-
I want to customize error messages at runtime, I want to localize the messages through the database.
I have tried to change attribute values through reflection but it had not worked.
Code :-
//Model
public class Home
{
[Required(ErrorMessage = "Hard coded error msg")]
public string LogoutLabel { get; set; }
}
//On controller
public ActionResult Index()
{
Home homeData = new Home();
foreach (PropertyInfo prop in homeData.GetType().GetProperties())
{
foreach (Attribute attribute in prop.GetCustomAttributes(false))
{
RequiredAttribute rerd = attribute as RequiredAttribute;
if (rerd != null)
{
rerd.ErrorMessage = "dynamic message";
}
}
}
return View(homeData);
}
On client side when validation takes place it shows me old message "Hard Coded error msg".
Please suggest how this can be customised if we donot want to use Resource bundle approach
You would better create and register your own DataAnnotationsModelMetadataProvider where you can just override the error messages. For more detail please see the answers to the similar question here MVC Validation Error Messages not hardcoded in Attributes
are you intended to implement this nested loop to localize validation message of all you'r entities ? i think no.a better solution is using Validator attribute.
for you'r class :
[Validator(typeof(HomeValidator))]
public class Home
{
public string LogoutLabel { get; set; }
}
now lets implement HomeValidator :
public class HomeValidator : AbstractValidator<Home>
{
public HomeValidator()
{
RuleFor(x => x.LogoutLabel ).NotEmpty().WithMessage("your localized message");
}
}
Related
I am creating asp.net mvc application in which I storing my error messages in my database, now I want to use required field annotation instead of custom js validations when user clicks submit, my models looks like below,
[Required(ErrorMessage = "static error message")]
public string AttributeValue { get; set; }
I want to add dynamic error message instead of -> (static error message)
is there a way to add dynamic error message from controller.
I had a requirement to set a regex expression dynamically, instead of creating my own validators as #Golda suggested in the comments, I cheated and just set the data-val errors using properties on my model, this is all the required attribute does anywhere with the help of a TagHelper.
Build your model as usual and add an additional property to hold the error message
[Required(ErrorMessage = "static error message")] // Leave this here for server side
public string AttributeValue { get; set; }
public string AttributeValueRequiredErrorMessage { get; set; }
In the controller make sure you set this property
public async Task<IActionResult> SomeAction()
{
.... // other logic
var attributeValueRequiredErrorMessage = callTheDb();
var model = new Model()
{
AttributeValueRequiredErrorMessage = attributeValueRequiredErrorMessage
}
return View(model);
}
Then in your view simply do this
<input asp-for="AttributeValue " data-val-required="#Model.AttributeValueRequiredErrorMessage " />
This would work for a quick win but I would probably look at an alternative if you were doing this in multiple places.
If you dont mind taking the hit server side, you could also implement the [Remote] attribute if you're in dotnet core, see docs: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-3.0#remote-attribute
How can I create a custom validation attribute with client side validation without implementing IClientValidatable?
How does System.ComponentModel.DataAnnotations.RequiredAttribute client side validate?
The reason to do this is because I'm using objects from classes in another project as models in my views and I don't want to add the System.Web.MVC reference to that project.
EDIT to add more information:
I know that IClientValidatable is used to add custom attributes to
the HTML to be used later by the unobtrusive validation.
I know I'll need to add the javascript code to made the validation in
the client.
What I don't know is how to use the information from the custom validation attribute to add the necessary attributes to the HTML for unobtrusive validation to work.
This is my custom validation attribute:
public class RequiredGuidAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
Guid? guidValue = value as Guid?;
if (guidValue == null)
return false;
return guidValue != Guid.Empty;
}
}
This is my property with the attribute applied:
[RequiredGuid(ErrorMessageResourceType = typeof(ClientOrderResources), ErrorMessageResourceName = "RequiredShippingMethod")]
public Guid ShippingMethodId
{
get { return GetProperty(ShippingMethodIdProperty); }
set { SetProperty(ShippingMethodIdProperty, value); }
}
And finally I'm rendering a hidden input for that property in the view using Html.HiddenFor.
Now, how can I get the error message from the attribute to apply it to the HTML? Should I do it my self using Reflection or there is a better way?
And then how can I tell Html.HiddenFor to use that information to add the necessary attributes to the HTML?
We had a similar problem. We have a model we use for our account creation that uses IClientValidatable on its custom attributes. However, we created a batch account creation process that sits outside of the website that we weren't able to reference System.Web.Mvc in. Because of this, when we called Validator.TryValidateObject, any custom validator that inherited from IClientValidatable was simply skipped. Here's what we were working with that was failing to validate outside of our website:
public class AgeValidatorAttribute : ValidationAttribute, IClientValidatable
{
public int AgeMin { get; set; }
public int AgeMax { get; set; }
public override bool IsValid(object value)
{
//run validation
}
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessageString,
ValidationType = "agevalidator"
};
rule.ValidationParameters["agemin"] = AgeMin;
rule.ValidationParameters["agemax"] = AgeMax;
yield return rule;
}
Removing System.Web.Mvc required us to also remove GetClientValidationRules and the IClientValidatable reference. In order to do this and still have client side validation, we had to create a new class:
public class AgeValidatorClientValidator : DataAnnotationsModelValidator<AgeValidatorAttribute>
{
private readonly string _errorMessage;
private readonly string _validationType;
public AgeValidatorClientValidator(ModelMetadata metadata, ControllerContext context, AgeValidatorAttribute attribute)
: base(metadata, context, attribute)
{
this._errorMessage = attribute.FormatErrorMessage(metadata.DisplayName);
this._validationType = "agevalidator";
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var rule = new ModelClientValidationRule
{
ErrorMessage = this._errorMessage,
ValidationType = this._validationType
};
rule.ValidationParameters["agemin"] = base.Attribute.AgeMin;
rule.ValidationParameters["agemax"] = base.Attribute.AgeMax;
yield return rule;
}
}
As you can see, it does essentially the same thing as it used to, it's just done using the DataAnnatotationsModelValidator rather than IClientValidatable. There's one more step we need to do to actually attach the DataAnnotationsModelValidator to the atttribute, and that's done in the Global.asax.cs Application_Start method
DataAnnotationsModelValidatorProvider.RegisterAdapter(
typeof(AgeValidatorAttribute), typeof(AgeValidatorClientValidator));
Now you can use this just as you would use a normal attribute:
[AgeValidator(AgeMax = 110, AgeMin = 18, ErrorMessage = "The member must be between 18 and 110 years old")]
public string DateOfBirth { get; set; }
I know this question is a year old, but I spent all day yesterday and half of today trying to figure this issue out. So I hope this helps somebody who runs into the same problem if OP hasn't figured the answer out yet.
Please note, I did not include any javascript in this writeup as it required no changes from the standard implementation of custom validation rules using jQuery.validate.
You can't have custom validation on the client unless you implement IClientValidatable. And for that you also need to add client script as well.
http://msdn.microsoft.com/en-us/vs2010trainingcourse_aspnetmvccustomvalidation_topic3.aspx
It is possible, i found this article on how to do it:
http://xhalent.wordpress.com/2011/05/12/custom-unobstrusive-jquery-validation-in-asp-net-mvc-3-using-dataannotationsmodelvalidatorprovider/
basically you have to create a DataAnnotationsModelValidator on your client an register it in Application_Start().
And don't forget that you still have to write the Javascript for client side validation.
I have the need to manually instansiate some controllers and therefore have this code:
var controller = Activator.CreateInstance(typeof(AccountController),
repository) as AccountController;
In the AccountController I have a method similar to this:
[AllowAnonymous]
[HttpPost]
public ApiJsonResult LogOn(LogOnAccountDto model)
{
ValidateModel(model);
if (ModelState.IsValid)
{
//...
}
}
I want my ModelState.IsValid to work, so therefore I call ValidateModel and pass it the model.
This fails, apparently because the controlContext isn't set.
I get this error:
Value cannot be null. Parameter name: controllerContext Description:
An unhandled exception occurred during the execution of the current
web request. Please review the stack trace for more information about
the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: controllerContext
So, how can I manually instansiate a IController in code - so that "everything" works?
Thanks in advance.
So, why do I need this?
I'm playing around with some architecture and game logic ideas for an "online strategy game".
I have an ASP.NET MVC 4 (Preview) application, which is my web version of the game. The idea is that the game should also be played on devices like Windows Phone, iPhone etc via NATIVE apps.
Therefore I need some API for my game (some kind of REST service which communicate via http/json).
As this API will be the public interface for the game, all the game logic will of course be located in side this API.
Therefore I want to use this API from both the "web version" and the "mobile version" of the game.
I have implemented this API as an Area inside ASP.NET MVC 4 (Preview). My first though were to actually do httpwebrequest from my "web version" to the API so I were using the API EXACTLY as the "mobile version" would.
But then I thought, that it might be better to actually just instansiate the controllers manually to avoid all the json/web-calling overhead I would get from calling the API the "right way".
So that's why I'm here now, I want to instansiate my controllers manually in code, because I want to use the exact logic in them.
Makes sense?
If you have a better idea please let me know - I'm doing this this for the learning of it, not produce a real product - at least thats not the goal right now - right now I'm just trying to learn some new stuff :)
You don't need to call ValidateModel directly.
At least I never needed to call it directly in any of my code and I haven't seen any examples which would call it either.
You can use the attributes from System.ComponentModel.DataAnnotations to control how your model is validated.
Let me give you an example, copy-pasted from some working code.
The model class:
(Basically just a DTO, nothing special.)
public class ArticleModel
{
public ArticleModel()
{
CategoryIds = new List<int>();
}
public int Id { get; set; }
[Required(ErrorMessage = "Field mandatory!")]
public string Title { get; set; }
[Required(ErrorMessage = "Field mandatory!")]
public string Text { get; set; }
public string Summary { get; set; }
public bool RefreshDate { get; set; }
public List<int> CategoryIds { get; set; }
}
The controller action:
(Instantiates an object for the ORM and saves it to the database.)
[HttpPost]
[ValidateInput(false)]
[SiteAuthorize(SiteAuthorization.SiteOwner)]
public ActionResult EditArticle(ArticleModel model)
{
var article = Repository.Retrieve<Article>().SingleOrDefault(x => x.Id == model.Id && x.Site == ColorfulUtility.CurrentSite);
if (article == null)
return RedirectToAction("ArticleList");
if (ModelState.IsValid)
{
if (model.RefreshDate)
article.Date = DateTime.Now;
article.Title = model.Title.SimpleTextToSafeHtml();
article.Text = model.Text.RichTextToSafeHtml();
article.Summary = model.Summary.RichTextToSafeHtml();
foreach (var category in ColorfulUtility.CurrentSite.ArticleCategories)
{
if (!article.Categories.Contains(category) && model.CategoryIds.Contains(category.Id))
{
article.Categories.Add(category);
}
else if (article.Categories.Contains(category) && !model.CategoryIds.Contains(category.Id))
{
article.Categories.Remove(category);
}
}
Repository.Flush();
return RedirectToAction("ArticleList");
}
return View("CreateArticle", model);
}
I am trying to implement custom attribute validation, similar to one demonstrated here in ScottGu's blog:
http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx
I have this custom validator attribute for Email:
public class EmailAttribute : RegularExpressionAttribute
{
public EmailAttribute() :
base("^[A-Za-z0-9](([_\\.\\-]?[a-zA-Z0-9]+)*)#([A-Za-z0-9]+)(([\\.\\-]?[a-zA-Z0-9]+)*)\\.([A-Za-z]{2,})$") { }
}
My class uses it like this:
[Required(ErrorMessage = ValidationCiM.MsgObaveznoPolje)]
[Email(ErrorMessage = ValidationCiM.MsgMailNeispravan)]
[StringLength(ValidationCiM.LenSrednjePolje, ErrorMessage = ValidationCiM.MsgSrednjePolje)]
public string Mail { get; set; }
and it all works well on server side, model is validated ok, and everything. But client side validation does not activate for this the second attribute, it works for Required, and it also works for StringLength but not for Email.
i have tried including both jquery and Microsoft ajax scripts, but there seems to be no difference.
In ScottGu's blog, he states that the custom validation if implemented like this should work without the need to add custom script.
Any ideas please?
Use IClientValidatable in ASP.NET MVC 3:
public class EmailAttribute : RegularExpressionAttribute, IClientValidatable
{
public EmailAttribute()
:base(#"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})$")
{
}
public System.Collections.Generic.IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRegexRule(this.ErrorMessageString, base.Pattern);
return new[] { rule };
}
}
What you actually needed to do was this (on application start):
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(EmailAttribute), typeof(RegularExpressionAttributeAdapter));
It hooks up the client validation to your custom attribute.
I'm quite new to asp.net mvc, and right know I'm trying to find out
a good practise to do input validation.
In the project we're going to use entity framework, where you can add
data annotations to properties in the following way:
[Required(ErrorMessage = "Please enter a product name")]
[Column]
public string Name { get; set; }
This is quite nice, however we have a multi language website (like most websites),
so we can't only show the error messages in English.
What can be a way to solve this? Can I change this errormessage #runtime, depending on the user's language?
Should I use Jquery client side validation?
Thanks for the input.
Update I've tried the code on the website of Phil Haack
This will do the trick with static resources however, we use resources that come from a database not static resources.
If I fill in the following for the dataannotations:
[MetadataType(typeof(IncidentsMetaData))]
public partial class INCIDENTS
{
private class IncidentsMetaData
{
[Required(ErrorMessageResourceType = typeof(CustomResourceProviders.DBResourceProviderFactory),
ErrorMessageResourceName="1277")]
public string SUBJECT { get; set; }
}
}
Then I get the following error:
The resource type 'CustomResourceProviders.DBResourceProviderFactory' does not have an accessible static property named '1277'.
Of course there is no such property, it should be accessed by a function.
Any idea what I could do about this?
tnx
You can inherit custom attribute from RequiredAttribute and set your own localized message for property ErrorMessage. It can looks like this:
public class LocalizedRequiredAttribute : RequiredAttribute
{
public LocalizedRequiredAttribute()
: base()
{
// prefix for the selection of localized messages from datebase
// e.x. for "Required" string, localized messages will be: "RuRequired", "EnRequired"
var currentCulture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
// logic to get value from datebase
// e.x. using Linq2Sql
using (var context = new dateBaseContext())
{
var query = (from x in context.LocalizedStrings
where x.NameKey == currentCulture + "Required"
select x.NameValue).SingleOrDefault();
if (query != null)
{
base.ErrorMessage = query;
}
else
{
base.ErrorMessage = "UndefinedName";
}
}
}
}
also and you inherit from DisplayNameAttribute and override DisplayName property:
public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
public LocalizedDisplayNameAttribute(string displayNameKey)
: base(displayNameKey)
{
}
public override string DisplayName
{
get
{
if (!string.IsNullOrEmpty(base.DisplayName))
{
// prefix for the selection of localized messages from datebase
// e.x. if DisplayName is "Country", localized messages will be: "RuCountry", "EnCountry"
var currentCulture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
// logic to get value from datebase
// e.x. using Linq2Sql
using (var context = new dateBaseContext())
{
var query = (from x in context.DisplayNames
where x.DisplayNameKey == currentCulture + base.DisplayName
select x.DisplayNameValue).SingleOrDefault();
if (query != null)
{
return query;
}
return base.DisplayName;
}
}
return "UndefinedName";
}
}
}
also you can create your custom validation attributes that inherits from ValidationAttribute class.
Take a look at this post, http://helios.ca/2010/02/17/asp-net-mvc-2-model-validation-with-localization/ good blog on the problem
Phil Haack has written a good blog post that covers how to do this. Essentially it is much the same except you use resource files to provide the messages.
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = Required")]
public string MyProperty{ get; set; }