ASP.NET MVC Validation Help - asp.net-mvc

I'm doing some simple validation inside my Controller I know this would better placed inside something like a service layer but for this I want to keep it inside the Controller
The idea is to check that a valid url is being entered into a url field to display an image, e.g. http://domain.com/myawesomeimage.png would be valid and http://domain.com/ would not be valid.
// ValidateInt has a default value of 0
int ValidateInt = 0;
// If the url entered (if one at all) does not have correct extension then increment the ValidateInt
if (!ArticleToEdit.image.Contains(".jpg"))
ValidateInt++;
if (!ArticleToEdit.image.Contains(".jpeg"))
ValidateInt++;
if (!ArticleToEdit.image.Contains(".png"))
ValidateInt++;
if (!ArticleToEdit.image.Contains(".gif"))
ValidateInt++;
if (!ArticleToEdit.image.Contains(".bmp"))
ValidateInt++;
// if ValidateInt is bigger than 0 then the url is invalid
if (ValidateInt > 0)
ModelState.AddModelError("Image", "Please enter a valid URL.");
EDITED CODE

The problem with your code
Your code is invalid, because your model state will always have at least 4 errors. Even though the URL would be correct. Your code requires that your URL must have all extensions which is of course incorrect. It can only have one. At most.
The solution
Use DataAnnotations instead and use regular expression validator. You're obviously already using some application model class called ArticleToCreate. You'll have to put data annotations attribute on the image property (one more observation: keey property names with Pascal casing so it's the same as .net):
public class ArticleToCreate
{
[RegularExpression(#"...")] // add regular expression that fulfils your requirements
public string Image { get; set; }
...
}
Then it all depends how complicated your regular expression is. The easiest one for your needs could be just that it starts with an http:// and end with the correct extension:
^http:\/\/.+\.(?:png|jpe?g|gif|bmp)$
And if you're directly providing your class instance to controller action it will get automatically validated for you without any additional code. This way you won't be able to forget to validate your objects manually.
Consider this controller action that automatically validates your model class object instance by validators defined on it (as per validator definition I've written above):
public ActionResult Create(ArticleToCreate data)
{
if (!this.ModelState.IsValid)
{
// handle invalid object
}
// handle valid object
}
This way your actions will focus on the processing part which is their main objective instead of focusing on too many aspects of your business process like validation for instance.
Shorter code = simpler code = easier to maintain = less bugs = less work = happy clients

Why not create custom ValidationAttributes (from DataAnnotations) and allow the Validation Engine do the work for you rather than worrying about where to put your logic?
I'm guessing it would look something like:
[AttributeUsage(AttributeTargets.Property |
AttributeTargets.Field, AllowMultiple = false)]
public class ValidImageUrlAttribute : ValidationAttribute
{
public string ErrorMessage { get; set; }
public override bool IsValid(object value)
{
var url = value as string;
if(!url.Contains(".jpg") || !url.Contains(".jpeg")
|| !url.Contains(".gif") || !url.Contains(".bmp")
|| !url.Contains(".png"))
{
return false;
}
return true;
}
public override string FormatErrorMessage(string name)
{
return ErrorMessage ?? base.FormatErrorMessage(name);
}
}
And then you could decorate your Model:
[Required(ErrorMessage = "Image URL is required.")]
[ValidImageUrl(ErrorMessage = "Valid Image URL is required.")]
public string ImageUrl { get; set; }

changed if (validate1 > 0) and it works fine :)

Related

Remote validation doesn't work when property to validate is inside a modal Bootstrap

I'm using ASP.NET MVC3.
When an user create an account, I need the chosen nickname be unique, so I use the Remote DataAnnotation like this :
public class UserModel
{
[Required]
[Remote("CheckNickname", "Validation", ErrorMessage = "This nickname is already used")]
public string Nickname { get; set; }
// ...
}
I used it in a strongly-typed view, via #Html.TextBoxFor(m => m.Nickname) and it perfeclty works.
However, I created another model with the exact same property.
public class MyOtherModel
{
// ...
[Required]
[Remote("CheckNickname", "Validation", ErrorMessage = "This nickname is already used")]
public string Nickname { get; set; }
}
I used this MyOtherModel.Nickname on a strongly-typed view via :
#Html.TextBoxFor(m => m.MyOtherModel.Nickname)
However, in this case only, the data passed to my CheckNickame() method is always null.
There are only two differences :
In the second case, the property I want to remotely validate is contained in another model (is it a problem ? I don't think so...)
In the second case, the property is displayed inside a modal bootstrap (is it a problem ?)
For information, this is what my CheckNickname() looks like :
public JsonResult CheckNickname(string nickname)
{
UserDAL userDAL = new UserDAL();
bool userIsAvailable = !userDAL.IsUserAlreadyInUse(nickname);
return Json(userIsAvailable, JsonRequestBehavior.AllowGet);
}
As I wrote it before, in the second case only, the parameter nickname is always null whereas it works as expected in the first case.
Is anyone knows why ?
Any help is appreciated.
UPDATE :
I created this method :
public JsonResult CheckNickname2([Bind(Prefix = "MyOtherModel")]string nickname)
{
UserDAL userDAL = new UserDAL();
bool userIsAvailable = !userDAL.IsUserAlreadyInUse(nickname);
return Json(userIsAvailable, JsonRequestBehavior.AllowGet);
}
The call is now :
http://mysite/Validation/CheckNickname2?MyOtherModel.Nickname=Alex
but if I put a breakpoint on CheckNickname2, the nickname paremeter is still null !
However, the call on the working validaton method is :
http://mysite/Validation/CheckNickname?Nickname=Alex
and this one works...
SOLUTION:
Ok, solved by changing [Bind(Prefix = "MyOtherModel")] to [Bind(Prefix = "MyOtherModel.Nickname")] as suggested by Stephen Muecke
In your second example, the html generated will be name="MyOtherModel.Nickname" so the key/value pair posted back will be MyOtherModel.Nickname:yourValue. Change the controller method to
public JsonResult CheckNickname([Bind(Prefix="MyOtherModel.Nickname")]string nickname)
which will effectively strip the prefix and bind correctly to parameter nickname
Note also that the modal usage may be a problem if this is adding dynamic content after the initial page has been rendered (in which case you need to re-parse the validator)

ASP MVC 4 Using a class to validate a get/set within a model

working on a project right now and I'm relatively new to MVC, I have a model which looks something like
public class MyViewModel
{
public string check1 {get;set;}
public string check2 {get;set;}
}
I also have a class which I want to use to validate these checks using a custom regex, such as
public class WebCommon
{
public static bool Validation(string validate)
{
bool validatestatus = false;
Regex check = new Regex(my regex here);
if (check.IsMatch(validate))
{
validatestatus = true;
return validatestatus;
}
else
{
return validationstatus;
}
}
}
Basically what I'd like to do is call that method inside the webcommon class, inside the model itself to check when I take in the values for the get;sets in my model, and if it returns false, then I want it to throw an error within the view saying that it is not a valid input. Thanks!
This question here is about making custom regular expression attrbutes and should answer your question.
Check out either Regular Expression Attribute
Or implement and IValidatableObject

ASP.NET Custom Localization

I've done quite a bit of research and I'm not sure how I should proceed with this.
Usual localization would change only when the language changes, so Hello for french would be Bonjour but my application needs to have special keywords for for certain users so UserX might say "Hello" needs to be "Allo".
I would like to have resource key with IdentityName_resourceKey and if this key is present take it otherwize fall back to resourceKey.
I'm thinking I need a custom ResourceProvider but my implementation is a simple if statement so I would not want to write a complete resource provider.
I wrote a extension of DisplayName attribute which works fine but this is not very good as I will need one of those for every data annotation attributes and this would not work if I use resources directly in pages or controllers...
public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
private readonly PropertyInfo _propertyInfo;
public LocalizedDisplayNameAttribute(string resourceKey, Type resourceType) : base(resourceKey)
{
var clientName = CustomMembership.Instance.CurrentUser.Client.Name;
_propertyInfo = resourceType.GetProperty(clientName + "_" + base.DisplayName, BindingFlags.Static | BindingFlags.Public)
?? resourceType.GetProperty(base.DisplayName, BindingFlags.Static | BindingFlags.Public);
}
public override string DisplayName
{
get
{
if (_propertyInfo == null)
{
return base.DisplayName;
}
return (string) _propertyInfo.GetValue(_propertyInfo.DeclaringType, null);
}
}
}
I'm looking for the best way to implement this with the least amount of code..
Thank you!
There is a better way, Data Annotations is your answer!
this is just a sample, you need go more deeper with System.Globalization.CultureInfo and Data Annotations (System.ComponentModel.DataAnnotations)
you can define your model class like this (assuming we have a resource file named CustomResourceValues with a value "strHello")
public class SomeObject(){
<Display(Name:="strHello", ResourceType:=GetType(My.Resources.CustomResourceValues))>
public string HelloMessage{ get; set; }
}
so, in our view the work must do it by the htmlhelper (assuming razor like render engine and the model is type of "SomeObject")
#Html.LabelFor(Function(x) x.HelloMessage)
basic info http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayattribute.resourcetype(v=vs.95).aspx

.NET MVC : Localizing RequiredAttribute (without resource files)

This is the problem that we're facing in .NET MVC 2.
We're trying to use DataAnnotations to take care of the Model Validation for us, like it's supposed to. The only problem that we're having is that we don't want the standard error messages (because we have multiple languages on our website).
We want to localize this, but the way the site is setup, is that all text comes from a database. So we'd like to have our error messages in the database as well.
So we wrote a custom RequiredAttribute, like this:
public class LocalizedRequiredAttribute : RequiredAttribute
{
public string LocalizedErrorMessage
{
get
{
return ErrorMessage;
}
set
{
ErrorMessage = value.Translate();
}
}
}
We wrote an extension to the String class to add the "Translate()" method, which does the necessary database lookup for the correct localized version.
We use our attribute like this:
[LocalizedRequired(LocalizedErrorMessage = "Naam is required")]
public string Name {get; set; }
This works, but only once.
If you visit the site in French first, you'll see the French error message stating that you're supposed to enter a value. If you visit the English site later, you'll still see the French error on the English page. The Setter seems to be called only once.
What can we do to prevent this behavior and refresh the error message every time the validation is run / the model is populated with values?
Thanks for any help you can give me.
Couldn't you fix this by moving your .Translate() from your setter to your getter? It makes sense that your setter is called only once.
Edit:
I assumed ErrorMessage was a virtual message, which is not the case.
Your only option might be to create Resource class (you don't need a resource file) that retrieves your values from the database.
[Required(ErrorMesageResourceName="FirstName", ErrorMessageResourceType=typeof(ABCResourceClass))]
public string Name {get; set; }
class ABCResourceClass{
public static String FirstName{
get{
return Translate("FirstName");
}
}
}
As you can infer from the example, the annotations framework calls the property with the name that matches the string you provide to ErrorMessageResourceName.
You could resort to some kindof code generation technique to create the ABCResourceClass if you have a lot of properties.
Just use method FormatErrorMessage() (whis is called everytime) to set ErrorMessage property
But it's hackish
public class ErrorLocalizedRequiredAttribute : RequiredAttribute
{
public ErrorLocalizedRequiredAttribute(string name)
{
Name = name;
}
public string Name
{ get; set; }
public override string FormatErrorMessage(string name)
{
//get translation from DB by Name
ErrorMessage = Localization.Translate(Name);
return base.FormatErrorMessage(name);
}
}
.
.
.
[ErrorLocalizedRequiredAttribute("EmailIsRequired")]
public string Email
{
get; set;
}

ASP.NET MVC - Custom validation message for value types

When I use UpdateModel or TryUpdateModel, the MVC framework is smart enough to know if you are trying to pass in a null into a value type (e.g. the user forgets to fill out the required Birth Day field) .
Unfortunately, I don't know how to override the default message, "A value is required." in the summary into something more meaningful ("Please enter in your Birth Day").
There has to be a way of doing this (without writing too much work-around code), but I can't find it. Any help?
EDIT
Also, I guess this would also be an issue for invalid conversions, e.g. BirthDay = "Hello".
Make your own ModelBinder by extending DefaultModelBinder:
public class LocalizationModelBinder : DefaultModelBinder
Override SetProperty:
base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
foreach (var error in bindingContext.ModelState[propertyDescriptor.Name].Errors.
Where(e => IsFormatException(e.Exception)))
{
if (propertyDescriptor.Attributes[typeof(TypeErrorMessageAttribute)] != null)
{
string errorMessage =
((TypeErrorMessageAttribute)propertyDescriptor.Attributes[typeof(TypeErrorMessageAttribute)]).GetErrorMessage();
bindingContext.ModelState[propertyDescriptor.Name].Errors.Remove(error);
bindingContext.ModelState[propertyDescriptor.Name].Errors.Add(errorMessage);
break;
}
}
Add the function bool IsFormatException(Exception e) to check if an Exception is a FormatException:
if (e == null)
return false;
else if (e is FormatException)
return true;
else
return IsFormatException(e.InnerException);
Create an Attribute class:
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)]
public class TypeErrorMessageAttribute : Attribute
{
public string ErrorMessage { get; set; }
public string ErrorMessageResourceName { get; set; }
public Type ErrorMessageResourceType { get; set; }
public TypeErrorMessageAttribute()
{
}
public string GetErrorMessage()
{
PropertyInfo prop = ErrorMessageResourceType.GetProperty(ErrorMessageResourceName);
return prop.GetValue(null, null).ToString();
}
}
Add the attribute to the property you wish to validate:
[TypeErrorMessage(ErrorMessageResourceName = "IsGoodType", ErrorMessageResourceType = typeof(AddLang))]
public bool IsGood { get; set; }
AddLang is a resx file and IsGoodType is the name of the resource.
And finally add this into Global.asax.cs Application_Start:
ModelBinders.Binders.DefaultBinder = new LocalizationModelBinder();
Cheers!
With the DefaultModelBinder it is possible to override the default required error message but unfortunately it would apply globally which IMHO renders it completely useless. But in case you decide to do it here's how:
Add the App_GlobalResources folder to your ASP.NET site
Add a resources file called Messages.resx
Inside the resources file declare a new string resource with the key PropertyValueRequired and some value
In Application_Start add the following line:
DefaultModelBinder.ResourceClassKey = "Messages";
As you can see there's no link between the model property you are validating and the error message.
In conclusion it is better to write custom validation logic to handle this scenario. One way would be to use a nullable type (System.Nullable<TValueType>) and then:
if (model.MyProperty == null ||
/** Haven't tested if this condition is necessary **/
!model.MyProperty.HasValue)
{
ModelState.AddModelError("MyProperty", "MyProperty is required");
}
I've been using the awesome xVal validation framework. It lets me do all my validation in the model (Even LINQ-SQL :)). It also emits the javascript required for client side validation.
EDIT: Sorry left out the link for how to get it working for LINQ-SQL
The basic workflow goes something like this.
public partial class YourClass
{
[Required(ErrorMessage = "Property is required.")]
[StringLength(200)]
public string SomeProperty{ get; set; }
}
try
{
// Validate the instance of your object
var obj = new YourClass() { SomeProperty = "" }
var errors = DataAnnotationsValidationRunner.GetErrors(obj);
// Do some more stuff e.g. Insert into database
}
catch (RulesException ex)
{
// e.g. control name 'Prefix.Title'
ex.AddModelStateErrors(ModelState, "Prefix");
ModelState.SetModelValue("Prefix.Title", new ValueProviderResult(ValueProvider["Prefix.Title"].AttemptedValue, collection["Prefix.Title"], System.Globalization.CultureInfo.CurrentCulture));
}
how about this?
[RegularExpression(#"^[a-zA-Z''-'\s]{1,40}$",
ErrorMessage = "Characters are not allowed.")]
That should allow you to tag properties with specific error messages for whatever MVC validators you want to use...
In ASP.NET MVC 1, I met this problem too.
In my project, there is a model or business object named "Entry", and its primary key EntryId is int? type, and the value of EntryId can be allowd to input by users.
So the problem is, when the field is blank or zero or some integer value that has existed, the custom error messages can be shown well, but if the value is some non-integer value like "a", i can not find a way to use the custom message to replace the default message like "The value 'a' is invalid".
when i track the error message in ModelState, i found when the value is non-integer, there will be two errors related to EntryId, and the first item's error message is blank...
Now i have to use such an ugly code to hack the problem.
if (ModelState["EntryId"].Errors.Count > 1)
{
ModelState["EntryId"].Errors.Clear(); //should not use ModelState["EntryId"].remove();
ModelState.AddModelError("EntryId", "必须为大于0的整数"); //必须为大于0的整数 means "it should be an integer value and great than 0"
}
but this makes controller fat, hope there is a real solution to solve it.
Look up ModelState.AddError.
yes, there is a way, you must use System.ComponentModel.DataAnnotations in combination with xVal and you are going to be able to set validation rules and messages (u can even use resource files for localization) for each of your property using Attributes
look here http://blog.codeville.net/2009/01/10/xval-a-validation-framework-for-aspnet-mvc/

Resources