Support user overrides of three more framework-provided `ModelState`-related messages
- #3215 - add new accessor properties to `IModelBindingMessageProvider` and plumb them through - use in `ModelStateDictionary` when handling a `FormatException` or `OverflowException` - use in `ValidationHelpers` when handling a `ModelError` with `null` `ErrorMessage` - add new `ModelExplorer` parameter to `IHtmlGenerator.GenerateValidationMessage()` - plumb through to `ValidationHelpers.GetModelErrorMessageOrDefault()` Started from work @kichalla did on the `kiran/movemessages-to-messageprovider` branch in #3775. nits: - use helper methods more consistently in `HtmlHelper<T>`; slightly improves error checking - remove unused `Resources` class from `Microsoft.AspNet.Mvc` - make `ValidationHelpers` class `public`; already in `.Internal` namespace - split `GetUserErrorMessageOrDefault()` in two; rename to `GetModelErrorMessageOrDefault()` - fix some #YOLO wrapping
This commit is contained in:
parent
1144fc0e6c
commit
04453a2b4f
|
|
@ -30,5 +30,26 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>Default <see cref="string"/> is "The value '{0}' is invalid.".</value>
|
/// <value>Default <see cref="string"/> is "The value '{0}' is invalid.".</value>
|
||||||
Func<string, string> ValueMustNotBeNullAccessor { get; }
|
Func<string, string> ValueMustNotBeNullAccessor { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Error message the model binding system adds when <see cref="ModelError.Exception"/> is of type
|
||||||
|
/// <see cref="FormatException"/> or <see cref="OverflowException"/> and value is known.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>Default <see cref="string"/> is "The value '{0}' is not valid for {1}.".</value>
|
||||||
|
Func<string, string, string> AttemptedValueIsInvalidAccessor { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Error message the model binding system adds when <see cref="ModelError.Exception"/> is of type
|
||||||
|
/// <see cref="FormatException"/> or <see cref="OverflowException"/> and value is unknown.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>Default <see cref="string"/> is "The supplied value is invalid for {0}.".</value>
|
||||||
|
Func<string, string> UnknownValueIsInvalidAccessor { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fallback error message HTML and tag helpers display when a property is invalid but the
|
||||||
|
/// <see cref="ModelError"/>s have <c>null</c> <see cref="ModelError.ErrorMessage"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>Default <see cref="string"/> is "The value '{0}' is invalid.".</value>
|
||||||
|
Func<string, string> ValueIsInvalidAccessor { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -267,11 +267,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
string errorMessage;
|
string errorMessage;
|
||||||
if (entry == null)
|
if (entry == null)
|
||||||
{
|
{
|
||||||
errorMessage = Resources.FormatModelError_InvalidValue_GenericMessage(name);
|
errorMessage = metadata.ModelBindingMessageProvider.UnknownValueIsInvalidAccessor(name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
errorMessage = Resources.FormatModelError_InvalidValue_MessageWithModelValue(
|
errorMessage = metadata.ModelBindingMessageProvider.AttemptedValueIsInvalidAccessor(
|
||||||
entry.AttemptedValue,
|
entry.AttemptedValue,
|
||||||
name);
|
name);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -282,38 +282,6 @@ namespace Microsoft.AspNet.Mvc.Abstractions
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("BindingSource_MustBeGreedy"), p0, p1);
|
return string.Format(CultureInfo.CurrentCulture, GetString("BindingSource_MustBeGreedy"), p0, p1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The supplied value is invalid for {0}.
|
|
||||||
/// </summary>
|
|
||||||
internal static string ModelError_InvalidValue_GenericMessage
|
|
||||||
{
|
|
||||||
get { return GetString("ModelError_InvalidValue_GenericMessage"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The supplied value is invalid for {0}.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatModelError_InvalidValue_GenericMessage(object p0)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("ModelError_InvalidValue_GenericMessage"), p0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The value '{0}' is not valid for {1}.
|
|
||||||
/// </summary>
|
|
||||||
internal static string ModelError_InvalidValue_MessageWithModelValue
|
|
||||||
{
|
|
||||||
get { return GetString("ModelError_InvalidValue_MessageWithModelValue"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The value '{0}' is not valid for {1}.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatModelError_InvalidValue_MessageWithModelValue(object p0, object p1)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("ModelError_InvalidValue_MessageWithModelValue"), p0, p1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -168,10 +168,4 @@
|
||||||
<data name="BindingSource_MustBeGreedy" xml:space="preserve">
|
<data name="BindingSource_MustBeGreedy" xml:space="preserve">
|
||||||
<value>The provided binding source '{0}' is not a greedy data source. '{1}' only supports greedy data sources.</value>
|
<value>The provided binding source '{0}' is not a greedy data source. '{1}' only supports greedy data sources.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ModelError_InvalidValue_GenericMessage" xml:space="preserve">
|
|
||||||
<value>The supplied value is invalid for {0}.</value>
|
|
||||||
</data>
|
|
||||||
<data name="ModelError_InvalidValue_MessageWithModelValue" xml:space="preserve">
|
|
||||||
<value>The value '{0}' is not valid for {1}.</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -31,6 +31,9 @@ namespace Microsoft.AspNet.Mvc.Internal
|
||||||
messageProvider.MissingBindRequiredValueAccessor = Resources.FormatModelBinding_MissingBindRequiredMember;
|
messageProvider.MissingBindRequiredValueAccessor = Resources.FormatModelBinding_MissingBindRequiredMember;
|
||||||
messageProvider.MissingKeyOrValueAccessor = Resources.FormatKeyValuePair_BothKeyAndValueMustBePresent;
|
messageProvider.MissingKeyOrValueAccessor = Resources.FormatKeyValuePair_BothKeyAndValueMustBePresent;
|
||||||
messageProvider.ValueMustNotBeNullAccessor = Resources.FormatModelBinding_NullValueNotValid;
|
messageProvider.ValueMustNotBeNullAccessor = Resources.FormatModelBinding_NullValueNotValid;
|
||||||
|
messageProvider.AttemptedValueIsInvalidAccessor = Resources.FormatModelState_AttemptedValueIsInvalid;
|
||||||
|
messageProvider.UnknownValueIsInvalidAccessor = Resources.FormatModelState_UnknownValueIsInvalid;
|
||||||
|
messageProvider.ValueIsInvalidAccessor = Resources.FormatHtmlGeneration_ValueIsInvalid;
|
||||||
|
|
||||||
// Set up ModelBinding
|
// Set up ModelBinding
|
||||||
options.ModelBinders.Add(new BinderTypeBasedModelBinder());
|
options.ModelBinders.Add(new BinderTypeBasedModelBinder());
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
MissingBindRequiredValueAccessor = Resources.FormatModelBinding_MissingBindRequiredMember,
|
MissingBindRequiredValueAccessor = Resources.FormatModelBinding_MissingBindRequiredMember,
|
||||||
MissingKeyOrValueAccessor = Resources.FormatKeyValuePair_BothKeyAndValueMustBePresent,
|
MissingKeyOrValueAccessor = Resources.FormatKeyValuePair_BothKeyAndValueMustBePresent,
|
||||||
ValueMustNotBeNullAccessor = Resources.FormatModelBinding_NullValueNotValid,
|
ValueMustNotBeNullAccessor = Resources.FormatModelBinding_NullValueNotValid,
|
||||||
|
AttemptedValueIsInvalidAccessor = Resources.FormatModelState_AttemptedValueIsInvalid,
|
||||||
|
UnknownValueIsInvalidAccessor = Resources.FormatModelState_UnknownValueIsInvalid,
|
||||||
|
ValueIsInvalidAccessor = Resources.FormatHtmlGeneration_ValueIsInvalid,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
||||||
private Func<string, string> _missingBindRequiredValueAccessor;
|
private Func<string, string> _missingBindRequiredValueAccessor;
|
||||||
private Func<string> _missingKeyOrValueAccessor;
|
private Func<string> _missingKeyOrValueAccessor;
|
||||||
private Func<string, string> _valueMustNotBeNullAccessor;
|
private Func<string, string> _valueMustNotBeNullAccessor;
|
||||||
|
private Func<string, string, string> _attemptedValueIsInvalidAccessor;
|
||||||
|
private Func<string, string> _unknownValueIsInvalidAccessor;
|
||||||
|
private Func<string, string> _valueIsInvalidAccessor;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ModelBindingMessageProvider"/> class.
|
/// Initializes a new instance of the <see cref="ModelBindingMessageProvider"/> class.
|
||||||
|
|
@ -36,6 +39,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
||||||
MissingBindRequiredValueAccessor = originalProvider.MissingBindRequiredValueAccessor;
|
MissingBindRequiredValueAccessor = originalProvider.MissingBindRequiredValueAccessor;
|
||||||
MissingKeyOrValueAccessor = originalProvider.MissingKeyOrValueAccessor;
|
MissingKeyOrValueAccessor = originalProvider.MissingKeyOrValueAccessor;
|
||||||
ValueMustNotBeNullAccessor = originalProvider.ValueMustNotBeNullAccessor;
|
ValueMustNotBeNullAccessor = originalProvider.ValueMustNotBeNullAccessor;
|
||||||
|
AttemptedValueIsInvalidAccessor = originalProvider.AttemptedValueIsInvalidAccessor;
|
||||||
|
UnknownValueIsInvalidAccessor = originalProvider.UnknownValueIsInvalidAccessor;
|
||||||
|
ValueIsInvalidAccessor = originalProvider.ValueIsInvalidAccessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
@ -91,5 +97,59 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
||||||
_valueMustNotBeNullAccessor = value;
|
_valueMustNotBeNullAccessor = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Func<string, string, string> AttemptedValueIsInvalidAccessor
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _attemptedValueIsInvalidAccessor;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
_attemptedValueIsInvalidAccessor = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Func<string, string> UnknownValueIsInvalidAccessor
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _unknownValueIsInvalidAccessor;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
_unknownValueIsInvalidAccessor = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Func<string, string> ValueIsInvalidAccessor
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _valueIsInvalidAccessor;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
_valueIsInvalidAccessor = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -475,7 +475,7 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unable to find the required services. Please add all the required services by calling '{0}.{1}' inside the call to '{4}' in the application startup code.
|
/// Unable to find the required services. Please add all the required services by calling '{0}.{1}' inside the call to '{2}' in the application startup code.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string UnableToFindServices
|
internal static string UnableToFindServices
|
||||||
{
|
{
|
||||||
|
|
@ -483,11 +483,11 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unable to find the required services. Please add all the required services by calling '{0}.{1}' inside the call to '{4}' in the application startup code.
|
/// Unable to find the required services. Please add all the required services by calling '{0}.{1}' inside the call to '{2}' in the application startup code.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatUnableToFindServices(object p0, object p1, object p4)
|
internal static string FormatUnableToFindServices(object p0, object p1, object p2)
|
||||||
{
|
{
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("UnableToFindServices"), p0, p1, p4);
|
return string.Format(CultureInfo.CurrentCulture, GetString("UnableToFindServices"), p0, p1, p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -1066,6 +1066,54 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("AcceptHeaderParser_ParseAcceptHeader_InvalidValues"), p0);
|
return string.Format(CultureInfo.CurrentCulture, GetString("AcceptHeaderParser_ParseAcceptHeader_InvalidValues"), p0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value '{0}' is not valid for {1}.
|
||||||
|
/// </summary>
|
||||||
|
internal static string ModelState_AttemptedValueIsInvalid
|
||||||
|
{
|
||||||
|
get { return GetString("ModelState_AttemptedValueIsInvalid"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value '{0}' is not valid for {1}.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatModelState_AttemptedValueIsInvalid(object p0, object p1)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("ModelState_AttemptedValueIsInvalid"), p0, p1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The supplied value is invalid for {0}.
|
||||||
|
/// </summary>
|
||||||
|
internal static string ModelState_UnknownValueIsInvalid
|
||||||
|
{
|
||||||
|
get { return GetString("ModelState_UnknownValueIsInvalid"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The supplied value is invalid for {0}.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatModelState_UnknownValueIsInvalid(object p0)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("ModelState_UnknownValueIsInvalid"), p0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value '{0}' is invalid.
|
||||||
|
/// </summary>
|
||||||
|
internal static string HtmlGeneration_ValueIsInvalid
|
||||||
|
{
|
||||||
|
get { return GetString("HtmlGeneration_ValueIsInvalid"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value '{0}' is invalid.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatHtmlGeneration_ValueIsInvalid(object p0)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("HtmlGeneration_ValueIsInvalid"), p0);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
|
||||||
Version 2.0
|
Version 2.0
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
The primary goals of this format is to allow a simple XML format
|
||||||
that is mostly human readable. The generation and parsing of the
|
that is mostly human readable. The generation and parsing of the
|
||||||
various data types are done through the TypeConverter classes
|
various data types are done through the TypeConverter classes
|
||||||
associated with the data types.
|
associated with the data types.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
... ado.net/XML headers & schema ...
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
<resheader name="version">2.0</resheader>
|
<resheader name="version">2.0</resheader>
|
||||||
|
|
@ -26,36 +26,36 @@
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
<comment>This is a comment</comment>
|
<comment>This is a comment</comment>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
There are any number of "resheader" rows that contain simple
|
||||||
name/value pairs.
|
name/value pairs.
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
Each data row contains a name, and value. The row also contains a
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
text/value conversion through the TypeConverter architecture.
|
text/value conversion through the TypeConverter architecture.
|
||||||
Classes that don't support this are serialized and stored with the
|
Classes that don't support this are serialized and stored with the
|
||||||
mimetype set.
|
mimetype set.
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
The mimetype is used for serialized objects, and tells the
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
read any of the formats listed below.
|
read any of the formats listed below.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
value : The object must be serialized into a byte array
|
value : The object must be serialized into a byte array
|
||||||
: using a System.ComponentModel.TypeConverter
|
: using a System.ComponentModel.TypeConverter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
|
|
@ -325,4 +325,13 @@
|
||||||
<data name="AcceptHeaderParser_ParseAcceptHeader_InvalidValues" xml:space="preserve">
|
<data name="AcceptHeaderParser_ParseAcceptHeader_InvalidValues" xml:space="preserve">
|
||||||
<value>"Invalid values '{0}'."</value>
|
<value>"Invalid values '{0}'."</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ModelState_AttemptedValueIsInvalid" xml:space="preserve">
|
||||||
|
<value>The value '{0}' is not valid for {1}.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ModelState_UnknownValueIsInvalid" xml:space="preserve">
|
||||||
|
<value>The supplied value is invalid for {0}.</value>
|
||||||
|
</data>
|
||||||
|
<data name="HtmlGeneration_ValueIsInvalid" xml:space="preserve">
|
||||||
|
<value>The value '{0}' is invalid.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -64,11 +64,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
|
|
||||||
if (For != null)
|
if (For != null)
|
||||||
{
|
{
|
||||||
var tagBuilder = Generator.GenerateValidationMessage(ViewContext,
|
var tagBuilder = Generator.GenerateValidationMessage(
|
||||||
For.Name,
|
ViewContext,
|
||||||
message: null,
|
For.ModelExplorer,
|
||||||
tag: null,
|
For.Name,
|
||||||
htmlAttributes: null);
|
message: null,
|
||||||
|
tag: null,
|
||||||
|
htmlAttributes: null);
|
||||||
|
|
||||||
if (tagBuilder != null)
|
if (tagBuilder != null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,27 +3,44 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ViewFeatures.Internal
|
namespace Microsoft.AspNet.Mvc.ViewFeatures.Internal
|
||||||
{
|
{
|
||||||
internal static class ValidationHelpers
|
public static class ValidationHelpers
|
||||||
{
|
{
|
||||||
public static string GetUserErrorMessageOrDefault(ModelError modelError, ModelStateEntry entry)
|
public static string GetModelErrorMessageOrDefault(ModelError modelError)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(modelError != null);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(modelError.ErrorMessage))
|
if (!string.IsNullOrEmpty(modelError.ErrorMessage))
|
||||||
{
|
{
|
||||||
return modelError.ErrorMessage;
|
return modelError.ErrorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry == null)
|
// Default in the ValidationSummary case is no error message.
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetModelErrorMessageOrDefault(
|
||||||
|
ModelError modelError,
|
||||||
|
ModelStateEntry containingEntry,
|
||||||
|
ModelExplorer modelExplorer)
|
||||||
|
{
|
||||||
|
Debug.Assert(modelError != null);
|
||||||
|
Debug.Assert(containingEntry != null);
|
||||||
|
Debug.Assert(modelExplorer != null);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(modelError.ErrorMessage))
|
||||||
{
|
{
|
||||||
return string.Empty;
|
return modelError.ErrorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
var attemptedValue = entry.AttemptedValue ?? "null";
|
// Default in the ValidationMessage case is a fallback error message.
|
||||||
return Resources.FormatCommon_ValueNotValidForProperty(attemptedValue);
|
var attemptedValue = containingEntry.AttemptedValue ?? "null";
|
||||||
|
return modelExplorer.Metadata.ModelBindingMessageProvider.ValueIsInvalidAccessor(attemptedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns non-null list of model states, which caller will render in order provided.
|
// Returns non-null list of model states, which caller will render in order provided.
|
||||||
|
|
|
||||||
|
|
@ -698,22 +698,6 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("Common_PropertyNotFound"), p0, p1);
|
return string.Format(CultureInfo.CurrentCulture, GetString("Common_PropertyNotFound"), p0, p1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The value '{0}' is invalid.
|
|
||||||
/// </summary>
|
|
||||||
internal static string Common_ValueNotValidForProperty
|
|
||||||
{
|
|
||||||
get { return GetString("Common_ValueNotValidForProperty"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The value '{0}' is invalid.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatCommon_ValueNotValidForProperty(object p0)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("Common_ValueNotValidForProperty"), p0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// No URL for remote validation could be found.
|
/// No URL for remote validation could be found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
|
||||||
Version 2.0
|
Version 2.0
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
The primary goals of this format is to allow a simple XML format
|
||||||
that is mostly human readable. The generation and parsing of the
|
that is mostly human readable. The generation and parsing of the
|
||||||
various data types are done through the TypeConverter classes
|
various data types are done through the TypeConverter classes
|
||||||
associated with the data types.
|
associated with the data types.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
... ado.net/XML headers & schema ...
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
<resheader name="version">2.0</resheader>
|
<resheader name="version">2.0</resheader>
|
||||||
|
|
@ -26,36 +26,36 @@
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
<comment>This is a comment</comment>
|
<comment>This is a comment</comment>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
There are any number of "resheader" rows that contain simple
|
||||||
name/value pairs.
|
name/value pairs.
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
Each data row contains a name, and value. The row also contains a
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
text/value conversion through the TypeConverter architecture.
|
text/value conversion through the TypeConverter architecture.
|
||||||
Classes that don't support this are serialized and stored with the
|
Classes that don't support this are serialized and stored with the
|
||||||
mimetype set.
|
mimetype set.
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
The mimetype is used for serialized objects, and tells the
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
read any of the formats listed below.
|
read any of the formats listed below.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
value : The object must be serialized into a byte array
|
value : The object must be serialized into a byte array
|
||||||
: using a System.ComponentModel.TypeConverter
|
: using a System.ComponentModel.TypeConverter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
|
|
@ -247,9 +247,6 @@
|
||||||
<data name="Common_PropertyNotFound" xml:space="preserve">
|
<data name="Common_PropertyNotFound" xml:space="preserve">
|
||||||
<value>The property {0}.{1} could not be found.</value>
|
<value>The property {0}.{1} could not be found.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Common_ValueNotValidForProperty" xml:space="preserve">
|
|
||||||
<value>The value '{0}' is invalid.</value>
|
|
||||||
</data>
|
|
||||||
<data name="RemoteAttribute_NoUrlFound" xml:space="preserve">
|
<data name="RemoteAttribute_NoUrlFound" xml:space="preserve">
|
||||||
<value>No URL for remote validation could be found.</value>
|
<value>No URL for remote validation could be found.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -688,6 +688,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual TagBuilder GenerateValidationMessage(
|
public virtual TagBuilder GenerateValidationMessage(
|
||||||
ViewContext viewContext,
|
ViewContext viewContext,
|
||||||
|
ModelExplorer modelExplorer,
|
||||||
string expression,
|
string expression,
|
||||||
string message,
|
string message,
|
||||||
string tag,
|
string tag,
|
||||||
|
|
@ -754,8 +755,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
else if (modelError != null)
|
else if (modelError != null)
|
||||||
{
|
{
|
||||||
|
modelExplorer = modelExplorer ?? ExpressionMetadataProvider.FromStringExpression(
|
||||||
|
expression,
|
||||||
|
viewContext.ViewData,
|
||||||
|
_metadataProvider);
|
||||||
tagBuilder.InnerHtml.SetContent(
|
tagBuilder.InnerHtml.SetContent(
|
||||||
ValidationHelpers.GetUserErrorMessageOrDefault(modelError, entry));
|
ValidationHelpers.GetModelErrorMessageOrDefault(modelError, entry, modelExplorer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formContext != null)
|
if (formContext != null)
|
||||||
|
|
@ -818,7 +823,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
for (var i = 0; i < modelState.Errors.Count; i++)
|
for (var i = 0; i < modelState.Errors.Count; i++)
|
||||||
{
|
{
|
||||||
var modelError = modelState.Errors[i];
|
var modelError = modelState.Errors[i];
|
||||||
var errorText = ValidationHelpers.GetUserErrorMessageOrDefault(modelError, entry: null);
|
var errorText = ValidationHelpers.GetModelErrorMessageOrDefault(modelError);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(errorText))
|
if (!string.IsNullOrEmpty(errorText))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -676,7 +676,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IHtmlContent ValidationMessage(string expression, string message, object htmlAttributes, string tag)
|
public IHtmlContent ValidationMessage(string expression, string message, object htmlAttributes, string tag)
|
||||||
{
|
{
|
||||||
return GenerateValidationMessage(expression, message, htmlAttributes, tag);
|
return GenerateValidationMessage(
|
||||||
|
modelExplorer: null,
|
||||||
|
expression: expression,
|
||||||
|
message: message,
|
||||||
|
tag: tag,
|
||||||
|
htmlAttributes: htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -1135,17 +1140,19 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual IHtmlContent GenerateValidationMessage(
|
protected virtual IHtmlContent GenerateValidationMessage(
|
||||||
|
ModelExplorer modelExplorer,
|
||||||
string expression,
|
string expression,
|
||||||
string message,
|
string message,
|
||||||
object htmlAttributes,
|
string tag,
|
||||||
string tag)
|
object htmlAttributes)
|
||||||
{
|
{
|
||||||
var tagBuilder = _htmlGenerator.GenerateValidationMessage(
|
var tagBuilder = _htmlGenerator.GenerateValidationMessage(
|
||||||
ViewContext,
|
ViewContext,
|
||||||
expression: expression,
|
modelExplorer,
|
||||||
message: message,
|
expression,
|
||||||
tag: tag,
|
message,
|
||||||
htmlAttributes: htmlAttributes);
|
tag,
|
||||||
|
htmlAttributes);
|
||||||
if (tagBuilder == null)
|
if (tagBuilder == null)
|
||||||
{
|
{
|
||||||
return HtmlString.Empty;
|
return HtmlString.Empty;
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,10 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
var modelExplorer = GetModelExplorer(expression);
|
var modelExplorer = GetModelExplorer(expression);
|
||||||
return GenerateCheckBox(modelExplorer, GetExpressionName(expression), isChecked: null,
|
return GenerateCheckBox(
|
||||||
|
modelExplorer,
|
||||||
|
GetExpressionName(expression),
|
||||||
|
isChecked: null,
|
||||||
htmlAttributes: htmlAttributes);
|
htmlAttributes: htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,10 +99,13 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
throw new ArgumentNullException(nameof(expression));
|
throw new ArgumentNullException(nameof(expression));
|
||||||
}
|
}
|
||||||
|
|
||||||
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, MetadataProvider);
|
var modelExplorer = GetModelExplorer(expression);
|
||||||
|
return GenerateDropDown(
|
||||||
return GenerateDropDown(modelExplorer, ExpressionHelper.GetExpressionText(expression), selectList,
|
modelExplorer,
|
||||||
optionLabel, htmlAttributes);
|
GetExpressionName(expression),
|
||||||
|
selectList,
|
||||||
|
optionLabel,
|
||||||
|
htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -114,14 +120,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
throw new ArgumentNullException(nameof(expression));
|
throw new ArgumentNullException(nameof(expression));
|
||||||
}
|
}
|
||||||
|
|
||||||
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression,
|
var modelExplorer = GetModelExplorer(expression);
|
||||||
ViewData,
|
return GenerateDisplay(
|
||||||
MetadataProvider);
|
modelExplorer,
|
||||||
|
htmlFieldName ?? GetExpressionName(expression),
|
||||||
return GenerateDisplay(modelExplorer,
|
templateName,
|
||||||
htmlFieldName ?? ExpressionHelper.GetExpressionText(expression),
|
additionalViewData);
|
||||||
templateName,
|
|
||||||
additionalViewData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -133,7 +137,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
var modelExplorer = GetModelExplorer(expression);
|
var modelExplorer = GetModelExplorer(expression);
|
||||||
return GenerateDisplayName(modelExplorer, ExpressionHelper.GetExpressionText(expression));
|
return GenerateDisplayName(modelExplorer, GetExpressionName(expression));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -182,11 +186,10 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
throw new ArgumentNullException(nameof(expression));
|
throw new ArgumentNullException(nameof(expression));
|
||||||
}
|
}
|
||||||
|
|
||||||
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, MetadataProvider);
|
var modelExplorer = GetModelExplorer(expression);
|
||||||
|
|
||||||
return GenerateEditor(
|
return GenerateEditor(
|
||||||
modelExplorer,
|
modelExplorer,
|
||||||
htmlFieldName ?? ExpressionHelper.GetExpressionText(expression),
|
htmlFieldName ?? GetExpressionName(expression),
|
||||||
templateName,
|
templateName,
|
||||||
additionalViewData);
|
additionalViewData);
|
||||||
}
|
}
|
||||||
|
|
@ -233,11 +236,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
var modelExplorer = GetModelExplorer(expression);
|
var modelExplorer = GetModelExplorer(expression);
|
||||||
return GenerateLabel(
|
return GenerateLabel(modelExplorer, GetExpressionName(expression), labelText, htmlAttributes);
|
||||||
modelExplorer,
|
|
||||||
ExpressionHelper.GetExpressionText(expression),
|
|
||||||
labelText,
|
|
||||||
htmlAttributes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -252,7 +251,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
var modelExplorer = GetModelExplorer(expression);
|
var modelExplorer = GetModelExplorer(expression);
|
||||||
var name = ExpressionHelper.GetExpressionText(expression);
|
var name = GetExpressionName(expression);
|
||||||
|
|
||||||
return GenerateListBox(modelExplorer, name, selectList, htmlAttributes);
|
return GenerateListBox(modelExplorer, name, selectList, htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
@ -365,7 +364,8 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
throw new ArgumentNullException(nameof(expression));
|
throw new ArgumentNullException(nameof(expression));
|
||||||
}
|
}
|
||||||
|
|
||||||
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, MetadataProvider);
|
var modelExplorer =
|
||||||
|
ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, MetadataProvider);
|
||||||
if (modelExplorer == null)
|
if (modelExplorer == null)
|
||||||
{
|
{
|
||||||
var expressionName = GetExpressionName(expression);
|
var expressionName = GetExpressionName(expression);
|
||||||
|
|
@ -387,10 +387,13 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
throw new ArgumentNullException(nameof(expression));
|
throw new ArgumentNullException(nameof(expression));
|
||||||
}
|
}
|
||||||
|
|
||||||
return GenerateValidationMessage(ExpressionHelper.GetExpressionText(expression),
|
var modelExplorer = GetModelExplorer(expression);
|
||||||
|
return GenerateValidationMessage(
|
||||||
|
modelExplorer,
|
||||||
|
GetExpressionName(expression),
|
||||||
message,
|
message,
|
||||||
htmlAttributes,
|
tag,
|
||||||
tag);
|
htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -402,11 +405,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
var modelExplorer = GetModelExplorer(expression);
|
var modelExplorer = GetModelExplorer(expression);
|
||||||
return GenerateValue(
|
return GenerateValue(GetExpressionName(expression), modelExplorer.Model, format, useViewData: false);
|
||||||
ExpressionHelper.GetExpressionText(expression),
|
|
||||||
modelExplorer.Model,
|
|
||||||
format,
|
|
||||||
useViewData: false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,8 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
/// Generate a <input type="checkbox".../> element.
|
/// Generate a <input type="checkbox".../> element.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="viewContext">The <see cref="ViewContext"/> instance for the current scope.</param>
|
/// <param name="viewContext">The <see cref="ViewContext"/> instance for the current scope.</param>
|
||||||
/// <param name="modelExplorer">The <see cref="ModelExplorer"/> for the model.</param>
|
/// <param name="modelExplorer">The <see cref="ModelExplorer"/> for the <paramref name="expression"/>.</param>
|
||||||
/// <param name="expression">The model expression.</param>
|
/// <param name="expression">Expression name, relative to the current model.</param>
|
||||||
/// <param name="isChecked">The initial state of the checkbox element.</param>
|
/// <param name="isChecked">The initial state of the checkbox element.</param>
|
||||||
/// <param name="htmlAttributes">
|
/// <param name="htmlAttributes">
|
||||||
/// An <see cref="object"/> that contains the HTML attributes for the element. Alternatively, an
|
/// An <see cref="object"/> that contains the HTML attributes for the element. Alternatively, an
|
||||||
|
|
@ -323,8 +323,36 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
string format,
|
string format,
|
||||||
object htmlAttributes);
|
object htmlAttributes);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate a <paramref name="tag"/> element if the <paramref name="viewContext"/>'s
|
||||||
|
/// <see cref="ActionContext.ModelState"/> contains an error for the <paramref name="expression"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="viewContext">A <see cref="ViewContext"/> instance for the current scope.</param>
|
||||||
|
/// <param name="modelExplorer">The <see cref="ModelExplorer"/> for the <paramref name="expression"/>.</param>
|
||||||
|
/// <param name="expression">Expression name, relative to the current model.</param>
|
||||||
|
/// <param name="message">
|
||||||
|
/// The message to be displayed. If <c>null</c> or empty, method extracts an error string from the
|
||||||
|
/// <see cref="ModelBinding.ModelStateDictionary"/> object. Message will always be visible but client-side
|
||||||
|
/// validation may update the associated CSS class.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="tag">
|
||||||
|
/// The tag to wrap the <paramref name="message"/> in the generated HTML. Its default value is
|
||||||
|
/// <see cref="ViewContext.ValidationMessageElement"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="htmlAttributes">
|
||||||
|
/// An <see cref="object"/> that contains the HTML attributes for the element. Alternatively, an
|
||||||
|
/// <see cref="IDictionary{string, object}"/> instance containing the HTML attributes.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="TagBuilder"/> containing a <paramref name="tag"/> element if the
|
||||||
|
/// <paramref name="viewContext"/>'s <see cref="ActionContext.ModelState"/> contains an error for the
|
||||||
|
/// <paramref name="expression"/> or (as a placeholder) if client-side validation is enabled. <c>null</c> if
|
||||||
|
/// the <paramref name="expression"/> is valid and client-side validation is disabled.
|
||||||
|
/// </returns>
|
||||||
|
/// <remarks><see cref="ViewContext.ValidationMessageElement"/> is <c>"span"</c> by default.</remarks>
|
||||||
TagBuilder GenerateValidationMessage(
|
TagBuilder GenerateValidationMessage(
|
||||||
ViewContext viewContext,
|
ViewContext viewContext,
|
||||||
|
ModelExplorer modelExplorer,
|
||||||
string expression,
|
string expression,
|
||||||
string message,
|
string message,
|
||||||
string tag,
|
string tag,
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
// <auto-generated />
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
|
||||||
{
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Resources;
|
|
||||||
|
|
||||||
internal static class Resources
|
|
||||||
{
|
|
||||||
private static readonly ResourceManager _resourceManager
|
|
||||||
= new ResourceManager("Microsoft.AspNet.Mvc.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unable to find the required services. Please add all the required services by calling AddMvc() before calling UseMvc() in the Application Startup.
|
|
||||||
/// </summary>
|
|
||||||
internal static string UnableToFindServices
|
|
||||||
{
|
|
||||||
get { return GetString("UnableToFindServices"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unable to find the required services. Please add all the required services by calling AddMvc() before calling UseMvc() in the Application Startup.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatUnableToFindServices()
|
|
||||||
{
|
|
||||||
return GetString("UnableToFindServices");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
|
||||||
{
|
|
||||||
var value = _resourceManager.GetString(name);
|
|
||||||
|
|
||||||
System.Diagnostics.Debug.Assert(value != null);
|
|
||||||
|
|
||||||
if (formatterNames != null)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < formatterNames.Length; i++)
|
|
||||||
{
|
|
||||||
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
@ -736,6 +737,38 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
Assert.Equal(expected, error.ErrorMessage);
|
Assert.Equal(expected, error.ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ModelStateDictionary_AddsCustomErrorMessage_WhenModelStateNotSet()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = "Hmm, the supplied value is not valid for Length.";
|
||||||
|
var dictionary = new ModelStateDictionary();
|
||||||
|
|
||||||
|
var messageProvider = new ModelBindingMessageProvider
|
||||||
|
{
|
||||||
|
MissingBindRequiredValueAccessor = name => "Unexpected MissingBindRequiredValueAccessor use",
|
||||||
|
MissingKeyOrValueAccessor = () => "Unexpected MissingKeyOrValueAccessor use",
|
||||||
|
ValueMustNotBeNullAccessor = value => "Unexpected ValueMustNotBeNullAccessor use",
|
||||||
|
AttemptedValueIsInvalidAccessor =
|
||||||
|
(value, name) => "Unexpected InvalidValueWithKnownAttemptedValueAccessor use",
|
||||||
|
UnknownValueIsInvalidAccessor = name => $"Hmm, the supplied value is not valid for { name }.",
|
||||||
|
ValueIsInvalidAccessor = value => "Unexpected InvalidValueWithUnknownModelErrorAccessor use",
|
||||||
|
};
|
||||||
|
var bindingMetadataProvider = new DefaultBindingMetadataProvider(messageProvider);
|
||||||
|
var compositeProvider = new DefaultCompositeMetadataDetailsProvider(new[] { bindingMetadataProvider });
|
||||||
|
var provider = new DefaultModelMetadataProvider(compositeProvider);
|
||||||
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dictionary.TryAddModelError("key", new FormatException(), metadata);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var entry = Assert.Single(dictionary);
|
||||||
|
Assert.Equal("key", entry.Key);
|
||||||
|
var error = Assert.Single(entry.Value.Errors);
|
||||||
|
Assert.Equal(expected, error.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ModelStateDictionary_ReturnSpecificErrorMessage_WhenModelStateSet()
|
public void ModelStateDictionary_ReturnSpecificErrorMessage_WhenModelStateSet()
|
||||||
{
|
{
|
||||||
|
|
@ -754,6 +787,39 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
Assert.Equal(expected, error.ErrorMessage);
|
Assert.Equal(expected, error.ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ModelStateDictionary_AddsCustomErrorMessage_WhenModelStateSet()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = "Hmm, the value 'some value' is not valid for Length.";
|
||||||
|
var dictionary = new ModelStateDictionary();
|
||||||
|
dictionary.SetModelValue("key", new string[] { "some value" }, "some value");
|
||||||
|
|
||||||
|
var messageProvider = new ModelBindingMessageProvider
|
||||||
|
{
|
||||||
|
MissingBindRequiredValueAccessor = name => "Unexpected MissingBindRequiredValueAccessor use",
|
||||||
|
MissingKeyOrValueAccessor = () => "Unexpected MissingKeyOrValueAccessor use",
|
||||||
|
ValueMustNotBeNullAccessor = value => "Unexpected ValueMustNotBeNullAccessor use",
|
||||||
|
AttemptedValueIsInvalidAccessor =
|
||||||
|
(value, name) => $"Hmm, the value '{ value }' is not valid for { name }.",
|
||||||
|
UnknownValueIsInvalidAccessor = name => "Unexpected InvalidValueWithUnknownAttemptedValueAccessor use",
|
||||||
|
ValueIsInvalidAccessor = value => "Unexpected InvalidValueWithUnknownModelErrorAccessor use",
|
||||||
|
};
|
||||||
|
var bindingMetadataProvider = new DefaultBindingMetadataProvider(messageProvider);
|
||||||
|
var compositeProvider = new DefaultCompositeMetadataDetailsProvider(new[] { bindingMetadataProvider });
|
||||||
|
var provider = new DefaultModelMetadataProvider(compositeProvider);
|
||||||
|
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dictionary.TryAddModelError("key", new FormatException(), metadata);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var entry = Assert.Single(dictionary);
|
||||||
|
Assert.Equal("key", entry.Key);
|
||||||
|
var error = Assert.Single(entry.Value.Errors);
|
||||||
|
Assert.Equal(expected, error.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ModelStateDictionary_NoErrorMessage_ForNonFormatException()
|
public void ModelStateDictionary_NoErrorMessage_ForNonFormatException()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -522,6 +522,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
||||||
MissingBindRequiredValueAccessor = Resources.FormatModelBinding_MissingBindRequiredMember,
|
MissingBindRequiredValueAccessor = Resources.FormatModelBinding_MissingBindRequiredMember,
|
||||||
MissingKeyOrValueAccessor = Resources.FormatKeyValuePair_BothKeyAndValueMustBePresent,
|
MissingKeyOrValueAccessor = Resources.FormatKeyValuePair_BothKeyAndValueMustBePresent,
|
||||||
ValueMustNotBeNullAccessor = Resources.FormatModelBinding_NullValueNotValid,
|
ValueMustNotBeNullAccessor = Resources.FormatModelBinding_NullValueNotValid,
|
||||||
|
AttemptedValueIsInvalidAccessor = Resources.FormatModelState_AttemptedValueIsInvalid,
|
||||||
|
UnknownValueIsInvalidAccessor = Resources.FormatModelState_UnknownValueIsInvalid,
|
||||||
|
ValueIsInvalidAccessor = Resources.FormatHtmlGeneration_ValueIsInvalid,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1037,9 +1037,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
||||||
{
|
{
|
||||||
return new ModelBindingMessageProvider
|
return new ModelBindingMessageProvider
|
||||||
{
|
{
|
||||||
MissingBindRequiredValueAccessor = name => $"A value for the '{ name }' property was not provided.",
|
MissingBindRequiredValueAccessor =
|
||||||
|
name => $"A value for the '{ name }' property was not provided.",
|
||||||
MissingKeyOrValueAccessor = () => $"A value is required.",
|
MissingKeyOrValueAccessor = () => $"A value is required.",
|
||||||
ValueMustNotBeNullAccessor = value => $"The value '{ value }' is invalid.",
|
ValueMustNotBeNullAccessor = value => $"The value '{ value }' is invalid.",
|
||||||
|
AttemptedValueIsInvalidAccessor =
|
||||||
|
(value, name) => $"The value '{ value }' is not valid for { name }.",
|
||||||
|
UnknownValueIsInvalidAccessor = name => $"The supplied value is invalid for { name }.",
|
||||||
|
ValueIsInvalidAccessor = value => $"The value '{ value }' is invalid.",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
using Microsoft.AspNet.Mvc.Abstractions;
|
using Microsoft.AspNet.Mvc.Abstractions;
|
||||||
using Microsoft.AspNet.Mvc.Controllers;
|
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
#if !DNXCORE50
|
#if !DNXCORE50
|
||||||
using Microsoft.AspNet.Testing.xunit;
|
using Microsoft.AspNet.Testing.xunit;
|
||||||
|
|
@ -250,6 +249,64 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
Assert.Equal("The value 'abcd' is not valid for Int32.", error.ErrorMessage);
|
Assert.Equal("The value 'abcd' is not valid for Int32.", error.ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BindParameter_NonConvertableValue_GetsCustomErrorMessage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var parameterType = typeof(int);
|
||||||
|
var metadataProvider = new TestModelMetadataProvider();
|
||||||
|
metadataProvider
|
||||||
|
.ForType(parameterType)
|
||||||
|
.BindingDetails(binding =>
|
||||||
|
{
|
||||||
|
// A real details provider could customize message based on BindingMetadataProviderContext.
|
||||||
|
binding.ModelBindingMessageProvider.AttemptedValueIsInvalidAccessor =
|
||||||
|
(value, name) => $"Hmm, '{ value }' is not a valid value for '{ name }'.";
|
||||||
|
});
|
||||||
|
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(metadataProvider);
|
||||||
|
var parameter = new ParameterDescriptor()
|
||||||
|
{
|
||||||
|
Name = "Parameter1",
|
||||||
|
BindingInfo = new BindingInfo(),
|
||||||
|
ParameterType = parameterType
|
||||||
|
};
|
||||||
|
|
||||||
|
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
|
||||||
|
{
|
||||||
|
request.QueryString = QueryString.Create("Parameter1", "abcd");
|
||||||
|
});
|
||||||
|
|
||||||
|
var modelState = operationContext.ActionContext.ModelState;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, operationContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
|
||||||
|
// ModelBindingResult
|
||||||
|
Assert.False(modelBindingResult.IsModelSet);
|
||||||
|
|
||||||
|
// Model
|
||||||
|
Assert.Null(modelBindingResult.Model);
|
||||||
|
|
||||||
|
// ModelState
|
||||||
|
Assert.False(modelState.IsValid);
|
||||||
|
Assert.Equal(1, modelState.Count);
|
||||||
|
Assert.Equal(1, modelState.ErrorCount);
|
||||||
|
|
||||||
|
var key = Assert.Single(modelState.Keys);
|
||||||
|
Assert.Equal("Parameter1", key);
|
||||||
|
|
||||||
|
var entry = modelState[key];
|
||||||
|
Assert.Equal("abcd", entry.RawValue);
|
||||||
|
Assert.Equal("abcd", entry.AttemptedValue);
|
||||||
|
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
|
||||||
|
|
||||||
|
var error = Assert.Single(entry.Errors);
|
||||||
|
Assert.Null(error.Exception);
|
||||||
|
Assert.Equal($"Hmm, 'abcd' is not a valid value for 'Int32'.", error.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
#if DNXCORE50
|
#if DNXCORE50
|
||||||
[Theory]
|
[Theory]
|
||||||
#else
|
#else
|
||||||
|
|
@ -306,12 +363,12 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
var metadataProvider = new TestModelMetadataProvider();
|
var metadataProvider = new TestModelMetadataProvider();
|
||||||
metadataProvider
|
metadataProvider
|
||||||
.ForType(parameterType)
|
.ForType(parameterType)
|
||||||
.BindingDetails((Action<ModelBinding.Metadata.BindingMetadata>)(binding =>
|
.BindingDetails(binding =>
|
||||||
{
|
{
|
||||||
// A real details provider could customize message based on BindingMetadataProviderContext.
|
// A real details provider could customize message based on BindingMetadataProviderContext.
|
||||||
binding.ModelBindingMessageProvider.ValueMustNotBeNullAccessor =
|
binding.ModelBindingMessageProvider.ValueMustNotBeNullAccessor =
|
||||||
value => $"Hurts when '{ value }' is provided.";
|
value => $"Hurts when '{ value }' is provided.";
|
||||||
}));
|
});
|
||||||
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(metadataProvider);
|
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(metadataProvider);
|
||||||
|
|
||||||
var parameter = new ParameterDescriptor
|
var parameter = new ParameterDescriptor
|
||||||
|
|
|
||||||
|
|
@ -93,16 +93,22 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expectedViewContext = CreateViewContext();
|
var expectedViewContext = CreateViewContext();
|
||||||
|
var modelExpression = CreateModelExpression("Hello");
|
||||||
var generator = new Mock<IHtmlGenerator>();
|
var generator = new Mock<IHtmlGenerator>();
|
||||||
generator
|
generator
|
||||||
.Setup(mock =>
|
.Setup(mock => mock.GenerateValidationMessage(
|
||||||
mock.GenerateValidationMessage(expectedViewContext, "Hello", null, null, null))
|
expectedViewContext,
|
||||||
|
modelExpression.ModelExplorer,
|
||||||
|
modelExpression.Name,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null))
|
||||||
.Returns(new TagBuilder("span"))
|
.Returns(new TagBuilder("span"))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
|
|
||||||
var validationMessageTagHelper = new ValidationMessageTagHelper(generator.Object)
|
var validationMessageTagHelper = new ValidationMessageTagHelper(generator.Object)
|
||||||
{
|
{
|
||||||
For = CreateModelExpression("Hello")
|
For = modelExpression,
|
||||||
};
|
};
|
||||||
var expectedPreContent = "original pre-content";
|
var expectedPreContent = "original pre-content";
|
||||||
var expectedContent = "original content";
|
var expectedContent = "original content";
|
||||||
|
|
@ -155,6 +161,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
var setup = generator
|
var setup = generator
|
||||||
.Setup(mock => mock.GenerateValidationMessage(
|
.Setup(mock => mock.GenerateValidationMessage(
|
||||||
It.IsAny<ViewContext>(),
|
It.IsAny<ViewContext>(),
|
||||||
|
It.IsAny<ModelExplorer>(),
|
||||||
It.IsAny<string>(),
|
It.IsAny<string>(),
|
||||||
It.IsAny<string>(),
|
It.IsAny<string>(),
|
||||||
It.IsAny<string>(),
|
It.IsAny<string>(),
|
||||||
|
|
@ -214,6 +221,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
var setup = generator
|
var setup = generator
|
||||||
.Setup(mock => mock.GenerateValidationMessage(
|
.Setup(mock => mock.GenerateValidationMessage(
|
||||||
It.IsAny<ViewContext>(),
|
It.IsAny<ViewContext>(),
|
||||||
|
It.IsAny<ModelExplorer>(),
|
||||||
It.IsAny<string>(),
|
It.IsAny<string>(),
|
||||||
It.IsAny<string>(),
|
It.IsAny<string>(),
|
||||||
It.IsAny<string>(),
|
It.IsAny<string>(),
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
MissingBindRequiredValueAccessor = name => $"A value for the '{ name }' property was not provided.",
|
MissingBindRequiredValueAccessor = name => $"A value for the '{ name }' property was not provided.",
|
||||||
MissingKeyOrValueAccessor = () => $"A value is required.",
|
MissingKeyOrValueAccessor = () => $"A value is required.",
|
||||||
ValueMustNotBeNullAccessor = value => $"The value '{ value }' is invalid.",
|
ValueMustNotBeNullAccessor = value => $"The value '{ value }' is invalid.",
|
||||||
|
AttemptedValueIsInvalidAccessor = (value, name) => $"The value '{ value }' is not valid for { name }.",
|
||||||
|
UnknownValueIsInvalidAccessor = name => $"The supplied value is invalid for { name }.",
|
||||||
|
ValueIsInvalidAccessor = value => $"The value '{ value }' is invalid.",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -165,12 +165,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
|
||||||
// Act and assert
|
// Act and assert
|
||||||
var ex = Assert.Throws<ArgumentException>(
|
var ex = Assert.Throws<ArgumentException>(
|
||||||
"expression",
|
"expression",
|
||||||
() => htmlGenerator.GenerateValidationMessage(
|
() => htmlGenerator.GenerateValidationMessage(viewContext, null, null, "Message", "tag", null));
|
||||||
viewContext,
|
|
||||||
null,
|
|
||||||
"Message",
|
|
||||||
"tag",
|
|
||||||
null));
|
|
||||||
|
|
||||||
Assert.Equal(expected, ex.Message);
|
Assert.Equal(expected, ex.Message);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue