Changing the default ModelState error messages in

2020-02-12 04:39发布

问题:

I have my resource files in separate assembly MyApp.Resources.dll. I can use the resources without any problem but the issue appears when I want to change (localize) the default validation messages:

"The {0} field is required." and "The value '{0}' is not valid for {1}."

The solution DefaultModelBinder.ResourceClassKey = "MyApp.Resources.Global"; does not work because it requires the ResourceClassKey to be under App_GlobalResources folder in the web project.

What should be the fix for me ?

Regards

回答1:

I have found solution for this case (when resources are in separate assembly).

To get it working you should create custom ResourceProviderFactory and register it as default ResourceProviderFactoryType in <globalization> web.config section.

Setup Localization

// Modify web.config in run-time and setup custom ResourceProviderFactory
var globalization = WebConfigurationManager.GetSection("system.web/globalization") as GlobalizationSection;
var readonlyField = typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
readonlyField.SetValue(globalization, false);
globalization.ResourceProviderFactoryType = typeof(ValidationResourceProviderFactory).FullName;

var resourcesClass = typeof(ValidationResources).FullName; 
DefaultModelBinder.ResourceClassKey = resourcesClass;
ValidationExtensions.ResourceClassKey = resourcesClass;

ValidationResourceProviderFactory

public sealed class ValidationResourceProviderFactory: System.Web.Compilation.ResourceProviderFactory
{
    public ValidationResourceProviderFactory()
    {
    }

    public override IResourceProvider CreateGlobalResourceProvider(string classKey)
    {
        return new GlobalResourceProvider(classKey);
    }

    public override IResourceProvider CreateLocalResourceProvider(string virtualPath)
    {
        throw new NotImplementedException("Local resources are not supported yet");
    }
}

GlobalResourceProvider

public class GlobalResourceProvider : IResourceProvider
{
    public GlobalResourceProvider(string classKey)
    {
        Throw.IfBadArgument(() => String.IsNullOrEmpty(classKey), "classKey");

        var type = Type.GetType(classKey, false);
        if (type == null)
        {
            var asmName = classKey;
            var className = classKey;
            while(asmName.IndexOf(".") > -1 && type == null) 
            {
                asmName = asmName.Substring (0, asmName.LastIndexOf("."));
                className = classKey.Substring(asmName.Length + 1);
                type = Type.GetType(classKey + "," + asmName, false);
            }
        }

        Throw.IfNullArgument(type, "type");

        Manager = CreateResourceManager(classKey, type.Assembly);
    }

    public ResourceManager Manager { get; set; }

    #region IResourceProvider implementation

    public IResourceReader ResourceReader { get; set; }

    public object GetObject(string resourceKey, CultureInfo culture)
    {
        return Manager.GetObject(resourceKey, culture);
    }

    #endregion

    private ResourceManager CreateResourceManager(string classKey, Assembly assembly)
    {
        return new ResourceManager(classKey, assembly);
    }
}

UPD

RESX for ValidationResources

Just add new resources class as ValidationResources and place provided XML there

<?xml version="1.0" encoding="utf-8"?>
<root>
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
   <!--  Schema definited removed -->
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="Accept" xml:space="preserve">
    <value>Please enter a value with a valid mimetype.</value>
  </data>
  <data name="Creditcard" xml:space="preserve">
    <value>Please enter a valid credit card number.</value>
  </data>
  <data name="Date" xml:space="preserve">
    <value>Please enter a valid date.</value>
  </data>
  <data name="DateISO" xml:space="preserve">
    <value>Please enter a valid date (ISO).</value>
  </data>
  <data name="DateTime" xml:space="preserve">
    <value>Please enter a valid date and time.</value>
  </data>
  <data name="Digits" xml:space="preserve">
    <value>Please enter only digits.</value>
  </data>
  <data name="Email" xml:space="preserve">
    <value>Please enter a valid email address.</value>
  </data>
  <data name="EqualTo" xml:space="preserve">
    <value>Please enter the same value again.</value>
  </data>
  <data name="FieldMustBeDate" xml:space="preserve">
    <value>Please enter a valid date for "{0}".</value>
    <comment>Localization for legacy MVC ClientDataTypeModelValidatorProvider</comment>
  </data>
  <data name="FieldMustBeNumeric" xml:space="preserve">
    <value>Please enter a valid number for "{0}".</value>
    <comment>Localization for legacy MVC ClientDataTypeModelValidatorProvider</comment>
  </data>
  <data name="InvalidPropertyValue" xml:space="preserve">
    <value>Invalid property value: {0}</value>
  </data>
  <data name="Max" xml:space="preserve">
    <value>Please enter a value less than or equal to {0}.</value>
  </data>
  <data name="MaxLength" xml:space="preserve">
    <value>Please enter no more than {0} characters.</value>
  </data>
  <data name="Min" xml:space="preserve">
    <value>Please enter a value greater than or equal to {0}.</value>
  </data>
  <data name="MinLength" xml:space="preserve">
    <value>Please enter at least {0} characters.</value>
  </data>
  <data name="Number" xml:space="preserve">
    <value>Please enter a valid number.</value>
  </data>
  <data name="PropertyValueInvalid" xml:space="preserve">
    <value>The value "{0}" is invalid for the property "{1}"</value>
    <comment>Localization for legacy MVC DefaultModelBinder</comment>
  </data>
  <data name="PropertyValueRequired" xml:space="preserve">
    <value>The "{0}" field is required.</value>
    <comment>Localization for legacy MVC DefaultModelBinder</comment>
  </data>
  <data name="Range" xml:space="preserve">
    <value>Please enter a value between {1} and {2}.</value>
  </data>
  <data name="RangeClient" xml:space="preserve">
    <value>Please enter a value between {0} and {1}.</value>
  </data>
  <data name="RangeLength" xml:space="preserve">
    <value>Please enter a value between {0} and {1} characters long.</value>
  </data>
  <data name="Remote" xml:space="preserve">
    <value>Please fix this field.</value>
  </data>
  <data name="SignedInt" xml:space="preserve">
    <value>Please enter an integer value, sign allowed.</value>
  </data>
  <data name="Time" xml:space="preserve">
    <value>Please enter a valid time.</value>
  </data>
  <data name="UnsignedInt" xml:space="preserve">
    <value>Please enter a positive integer value.</value>
  </data>
  <data name="Url" xml:space="preserve">
    <value>Please enter a valid URL.</value>
  </data>
  <data name="ValueNull" xml:space="preserve">
    <value>&lt;null&gt;</value>
  </data>
</root>


回答2:

There should be static properties on the DefaultModelBinder that you can set that will change the localization of the error messages...

http://forums.asp.net/p/1512140/3608427.aspx

Create a global resource class in App_GlobalResources, and set DefaultModelBinder.ResourceClassKey to the name of this class (for example, if you made "Messages.resx", then set ResourceClassKey to "Messages").

There are two strings you can override in MVC 2:

The string value for "PropertyValueInvalid" is used when the data the user entered isn't compatible with the data type (for example, typing in "abc" for an integer field). The default message for this is: "The value '{0}' is not valid for {1}." The string value for "PropertyValueRequired" is used when the user did not enter any data for a field which is not nullable (for example, an integer field). The default message for this is: "A value is required."