23

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".

7 Answers 7

20

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!

Sign up to request clarification or add additional context in comments.

2 Comments

Great answer, but there's an issue with it. propertyDescriptoy.Name is not always used as the key to ModelState. You should use inherited CreateSubPropertyName method instead, to create the key, like this: ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name]; string propertyName = CreateSubPropertyName(bindingContext.ModelName, propertyMetadata.PropertyName); and then you can use bindingContext.ModelState[propertyName] instead.
Ow,... and GetErrorMessage in the attribute class should return ErrorMessage in case it's not empty (or the other way around). It doesn't take into account the case that the user of the attribute specifies the error message inline (without any resource).
6

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:

  1. Add the App_GlobalResources folder to your ASP.NET site
  2. Add a resources file called Messages.resx
  3. Inside the resources file declare a new string resource with the key PropertyValueRequired and some value
  4. 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");
}

2 Comments

do you know if the ResourceClassKey behavior has changed lately? This seems broken in MVC 3. The doc even says it should throw an exception for an invalid class, but it won't, even if I do DefaultModelBinder.ResourceClassKey = "lkjhjkjdswqq".
nevermind... just found your answer at stackoverflow.com/questions/5395040/… with Brad's quote on why this isn't working :)
4

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));

}

1 Comment

Just for the record, xVal is deprecated according to the xVal team. They only recommend you to use it with MVC 1.0, and it is no longer being actively maintained or developed.
3

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...

Comments

2

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.

Comments

1

Look up ModelState.AddError.

3 Comments

I know about ModelState.AddModelError(), but UpdateModel() automatically adds values into the error collection. Unless I am misunderstanding your answer?
Well when you validate, add something to the ModelState. Here is a great tutorial. blog.maartenballiauw.be/post/2008/08/29/…
I was kind of hoping not to have to bind everything myself, but maybe its the only way if I want my own messages?
0

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/

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.