views:

5696

answers:

9

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

+2  A: 

Look up ModelState.AddError.

Daniel A. White
I know about ModelState.AddModelError(), but UpdateModel() automatically adds values into the error collection. Unless I am misunderstanding your answer?
Giovanni Galbo
Well when you validate, add something to the ModelState. Here is a great tutorial. http://blog.maartenballiauw.be/post/2008/08/29/Form-validation-with-ASPNET-MVC-preview-5.aspx
Daniel A. White
I was kind of hoping not to have to bind everything myself, but maybe its the only way if I want my own messages?
Giovanni Galbo
+1  A: 

Get your models to implement the IErrorDataInfo interface. I don't have any example code with me here at work, but have a gander at this tutorial here:

http://www.asp.net/learn/mvc/tutorial-37-cs.aspx

HTHs, Charles

Charlino
IErrorDataInfo will not help when inserting a null into a value field, though the link you provided says that you'd need to create a model binder to customize the message,
Giovanni Galbo
+4  A: 

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

}
Alex
+2  A: 

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");
}
Darin Dimitrov
A: 

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 watever MVC validators you want to use...

+1  A: 

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.

A: 

I've used your way before didnt fixed resources file to work as expected (thanx again Lauri Larjo) details here http://stackoverflow.com/questions/1481376/asp-net-mvc-validationattribute-and-localized-errormessage (check reply from Lauri Larjo)

VIM4
A: 

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/

Omu
+3  A: 

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!

Cristi Todoran