Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC4 Unobtrusive validation localization

Problem:

I have issues getting the default messages to be localized for implicit [Required] attributes using unobtrusive jquery validation. I do not want to put [Required] on every int (and other non-nullable types) in my model and the ressource file associated. I am wondering if anyone has tested the ASP.NET MVC4 Dev Preview and noticed the same issue? When I look at the mvc code it clearly seems like it should work.

Attempted solution:

Added in the global.asax:

DefaultModelBinder.ResourceClassKey = "ErrorMessages";

Have a resource file called "ErrorMessages.resx" and "ErrorMessages.fr.resx" in the global resources with PropertyValueInvalid and PropertyValueRequired.

Interesting information:

A good thing I have noticed is that they fixed the "Field must be a number" or "Field must be a date" from being hard coded in an internal sealed class.

ClientDataTypeModelValidatorProvider.ResourceClassKey = "ErrorMessages"; 

Does work if you have a resource file called "ErrorMessages.resx" and "ErrorMessages.fr.resx" in the global ressources folder and FieldMustBeNumeric/FieldMustBeDate

like image 514
Nick-ACNB Avatar asked Nov 30 '11 20:11

Nick-ACNB


1 Answers

I know this is old, but to get localised messages into the metadata is to subclass DataAnnotationsModelValidator and override GetClientValidationRules and Validate to supply your own messages.

You register the adapter using DataAnnotationsModelValidatorProvider.RegisterAdapterFactory.

I built a wrapper factory builder to create the factory delegate. The out parameter is here as I use this inside a loop as I discover all adapters in the assembly via reflection, so I need to get the attribute type for each adapter in order to call RegisterAdpaterFactory. I could have inlined the registration but I do other stuff after this using the adapter/attribute information

public static class ModelValidationFactory
{
    /// <summary>
    /// Builds a Lamda expression with the Func&lt;ModelMetadata, ControllerContext, ValidationAttribute, ModelValidator&gt; signature
    /// to instantiate a strongly typed constructor.  This used by the <see cref="DataAnnotationsModelValidatorProvider.RegisterAdapterFactory"/>
    /// and used (ultimately) by <see cref="ModelValidatorProviderCollection.GetValidators"/> 
    /// </summary>
    /// <param name="adapterType">Adapter type, expecting subclass of <see cref="ValidatorResourceAdapterBase{TAttribute}"/> where T is one of the <see cref="ValidationAttribute"/> attributes</param>
    /// <param name="attrType">The <see cref="ValidationAttribute"/> generic argument for the adapter</param>
    /// <returns>The constructor invoker for the adapter. <see cref="DataAnnotationsModelValidationFactory"/></returns>
    public static DataAnnotationsModelValidationFactory BuildFactory(Type adapterType, out Type attrType)
    {
        attrType = adapterType.BaseType.GetGenericArguments()[0];

        ConstructorInfo ctor = adapterType.GetConstructor(new[] { typeof(ModelMetadata), typeof(ControllerContext), attrType });

        ParameterInfo[] paramsInfo = ctor.GetParameters();

        ParameterExpression modelMetadataParam = Expression.Parameter(typeof(ModelMetadata), "metadata");
        ParameterExpression contextParam = Expression.Parameter(typeof(ControllerContext), "context");
        ParameterExpression attributeParam = Expression.Parameter(typeof(ValidationAttribute), "attribute");

        Expression[] ctorCallArgs = new Expression[]
        {
            modelMetadataParam,
            contextParam,
            Expression.TypeAs( attributeParam, attrType )
        };

        NewExpression ctorInvoker = Expression.New(ctor, ctorCallArgs);

        // ( ModelMetadata metadata, ControllerContext context, ValidationAttribute attribute ) => new {AdapterType}(metadata, context, ({AttrType})attribute)
        return Expression
            .Lambda(typeof(DataAnnotationsModelValidationFactory), ctorInvoker, modelMetadataParam, contextParam, attributeParam)
            .Compile()
            as DataAnnotationsModelValidationFactory;
    }
}

This also works in MVC3, and i think MVC2 as well.

like image 52
Robert Slaney Avatar answered Oct 05 '22 03:10

Robert Slaney