diff --git a/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/Metadata/IModelBindingMessageProvider.cs b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/Metadata/IModelBindingMessageProvider.cs
new file mode 100644
index 0000000000..63bae2b9ab
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/Metadata/IModelBindingMessageProvider.cs
@@ -0,0 +1,34 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
+{
+ ///
+ /// Provider for error messages the model binding system detects.
+ ///
+ public interface IModelBindingMessageProvider
+ {
+ ///
+ /// Error message the model binding system adds when a property with an associated
+ /// BindRequiredAttribute is not bound.
+ ///
+ /// Default is "A value for the '{0}' property was not provided.".
+ Func MissingBindRequiredValueAccessor { get; }
+
+ ///
+ /// Error message the model binding system adds when either the key or the value of a
+ /// is bound but not both.
+ ///
+ /// Default is "A value is required.".
+ Func MissingKeyOrValueAccessor { get; }
+
+ ///
+ /// Error message the model binding system adds when a null value is bound to a
+ /// non- property.
+ ///
+ /// Default is "The value '{0}' is invalid.".
+ Func ValueMustNotBeNullAccessor { get; }
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelMetadata.cs b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelMetadata.cs
index 9cff64881f..53304d93ca 100644
--- a/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelMetadata.cs
+++ b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelMetadata.cs
@@ -242,6 +242,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
///
public abstract bool IsRequired { get; }
+ ///
+ /// Gets the instance.
+ ///
+ public abstract IModelBindingMessageProvider ModelBindingMessageProvider { get; }
+
///
/// Gets a value indicating where the current metadata should be ordered relative to other properties
/// in its containing type.
diff --git a/src/Microsoft.AspNet.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs b/src/Microsoft.AspNet.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs
index 2a461e74c8..04f22c9c92 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs
@@ -3,6 +3,7 @@
using System;
using Microsoft.AspNet.Http;
+using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
@@ -23,6 +24,12 @@ namespace Microsoft.AspNet.Mvc.Internal
public static void ConfigureMvc(MvcOptions options)
{
+ // Set up default error messages
+ var messageProvider = options.ModelBindingMessageProvider;
+ messageProvider.MissingBindRequiredValueAccessor = Resources.FormatModelBinding_MissingBindRequiredMember;
+ messageProvider.MissingKeyOrValueAccessor = Resources.FormatKeyValuePair_BothKeyAndValueMustBePresent;
+ messageProvider.ValueMustNotBeNullAccessor = Resources.FormatModelBinding_NullValueNotValid;
+
// Set up ModelBinding
options.ModelBinders.Add(new BinderTypeBasedModelBinder());
options.ModelBinders.Add(new ServicesModelBinder());
@@ -48,7 +55,7 @@ namespace Microsoft.AspNet.Mvc.Internal
options.ValueProviderFactories.Add(new JQueryFormValueProviderFactory());
// Set up metadata providers
- options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider());
+ options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider(messageProvider));
options.ModelMetadataDetailsProviders.Add(new DefaultValidationMetadataProvider());
// Set up validators
diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/EmptyModelMetadataProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/EmptyModelMetadataProvider.cs
index 2a27b8eea4..5be3a27dfc 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/EmptyModelMetadataProvider.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/EmptyModelMetadataProvider.cs
@@ -1,5 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
namespace Microsoft.AspNet.Mvc.ModelBinding
@@ -7,8 +9,33 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public class EmptyModelMetadataProvider : DefaultModelMetadataProvider
{
public EmptyModelMetadataProvider()
- : base(new DefaultCompositeMetadataDetailsProvider(new IMetadataDetailsProvider[0]))
+ : base(new DefaultCompositeMetadataDetailsProvider(new IMetadataDetailsProvider[]
+ {
+ new MessageOnlyBindingProvider()
+ }))
{
}
+
+ private class MessageOnlyBindingProvider : IBindingMetadataProvider
+ {
+ private readonly ModelBindingMessageProvider _messageProvider = CreateMessageProvider();
+
+ public void GetBindingMetadata(BindingMetadataProviderContext context)
+ {
+ // Don't bother with ModelBindingMessageProvider copy constructor. No other provider can change the
+ // delegates.
+ context.BindingMetadata.ModelBindingMessageProvider = _messageProvider;
+ }
+
+ private static ModelBindingMessageProvider CreateMessageProvider()
+ {
+ return new ModelBindingMessageProvider
+ {
+ MissingBindRequiredValueAccessor = Resources.FormatModelBinding_MissingBindRequiredMember,
+ MissingKeyOrValueAccessor = Resources.FormatKeyValuePair_BothKeyAndValueMustBePresent,
+ ValueMustNotBeNullAccessor = Resources.FormatModelBinding_NullValueNotValid,
+ };
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/KeyValuePairModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/KeyValuePairModelBinder.cs
index f40f8d69a0..f64bc17fcc 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/KeyValuePairModelBinder.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/KeyValuePairModelBinder.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Threading.Tasks;
-using Microsoft.AspNet.Mvc.Core;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
@@ -30,7 +29,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
bindingContext.ModelState.TryAddModelError(
keyResult.Key,
- Resources.KeyValuePair_BothKeyAndValueMustBePresent);
+ bindingContext.ModelMetadata.ModelBindingMessageProvider.MissingKeyOrValueAccessor());
// Were able to get some data for this model.
// Always tell the model binding system to skip other model binders.
@@ -40,7 +39,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
bindingContext.ModelState.TryAddModelError(
valueResult.Key,
- Resources.KeyValuePair_BothKeyAndValueMustBePresent);
+ bindingContext.ModelMetadata.ModelBindingMessageProvider.MissingKeyOrValueAccessor());
// Were able to get some data for this model.
// Always tell the model binding system to skip other model binders.
diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/BindingMetadata.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/BindingMetadata.cs
index e7540642f2..9ef873b6e2 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/BindingMetadata.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/BindingMetadata.cs
@@ -10,6 +10,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
///
public class BindingMetadata
{
+ private ModelBindingMessageProvider _messageProvider;
+
///
/// Gets or sets the .
/// See .
@@ -50,6 +52,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
///
public bool? IsReadOnly { get; set; }
+ ///
+ /// Gets the instance. See
+ /// .
+ ///
+ public ModelBindingMessageProvider ModelBindingMessageProvider
+ {
+ get
+ {
+ return _messageProvider;
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+
+ _messageProvider = value;
+ }
+ }
+
///
/// Gets or sets the .
/// See .
diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/DefaultBindingMetadataProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/DefaultBindingMetadataProvider.cs
index 89adba0378..d951eb61d4 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/DefaultBindingMetadataProvider.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/DefaultBindingMetadataProvider.cs
@@ -14,6 +14,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
///
public class DefaultBindingMetadataProvider : IBindingMetadataProvider
{
+ private readonly ModelBindingMessageProvider _messageProvider;
+
+ public DefaultBindingMetadataProvider(ModelBindingMessageProvider messageProvider)
+ {
+ if (messageProvider == null)
+ {
+ throw new ArgumentNullException(nameof(messageProvider));
+ }
+
+ _messageProvider = messageProvider;
+ }
+
///
public void GetBindingMetadata([NotNull] BindingMetadataProviderContext context)
{
@@ -47,6 +59,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
}
}
+ // ModelBindingMessageProvider
+ // Provide a unique instance based on one passed to the constructor.
+ context.BindingMetadata.ModelBindingMessageProvider = new ModelBindingMessageProvider(_messageProvider);
+
// PropertyBindingPredicateProvider
var predicateProviders = context.Attributes.OfType().ToArray();
if (predicateProviders.Length > 0)
@@ -62,7 +78,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var bindingBehavior = context.PropertyAttributes.OfType().FirstOrDefault();
if (bindingBehavior == null)
{
- bindingBehavior =
+ bindingBehavior =
context.Key.ContainerType.GetTypeInfo()
.GetCustomAttributes(typeof(BindingBehaviorAttribute), inherit: true)
.OfType()
diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/DefaultModelMetadata.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/DefaultModelMetadata.cs
index a89c04260a..50b2f72015 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/DefaultModelMetadata.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/DefaultModelMetadata.cs
@@ -418,6 +418,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
}
}
+ ///
+ public override IModelBindingMessageProvider ModelBindingMessageProvider
+ {
+ get
+ {
+ return BindingMetadata.ModelBindingMessageProvider;
+ }
+ }
+
///
public override string NullDisplayText
{
diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/DefaultValidationMetadataProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/DefaultValidationMetadataProvider.cs
index 8963f870b8..17c324586f 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/DefaultValidationMetadataProvider.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/DefaultValidationMetadataProvider.cs
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
@@ -19,8 +18,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
if (attribute is IModelValidator || attribute is IClientModelValidator)
{
- // If another provider has already added this attribute, do not repeat it.
- // This will prevent attributes like RemoteAttribute (which implement ValidationAttribute and
+ // If another provider has already added this attribute, do not repeat it.
+ // This will prevent attributes like RemoteAttribute (which implement ValidationAttribute and
// IClientModelValidator) to be added to the ValidationMetadata twice.
// This is to ensure we do not end up with duplication validation rules on the client side.
if (!context.ValidationMetadata.ValidatorMetadata.Contains(attribute))
diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/ModelBindingMessageProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/ModelBindingMessageProvider.cs
new file mode 100644
index 0000000000..33c6dea706
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/Metadata/ModelBindingMessageProvider.cs
@@ -0,0 +1,95 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
+{
+ ///
+ /// Read / write implementation.
+ ///
+ public class ModelBindingMessageProvider : IModelBindingMessageProvider
+ {
+ private Func _missingBindRequiredValueAccessor;
+ private Func _missingKeyOrValueAccessor;
+ private Func _valueMustNotBeNullAccessor;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ModelBindingMessageProvider()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class based on
+ /// .
+ ///
+ /// The to duplicate.
+ public ModelBindingMessageProvider(ModelBindingMessageProvider originalProvider)
+ {
+ if (originalProvider == null)
+ {
+ throw new ArgumentNullException(nameof(originalProvider));
+ }
+
+ MissingBindRequiredValueAccessor = originalProvider.MissingBindRequiredValueAccessor;
+ MissingKeyOrValueAccessor = originalProvider.MissingKeyOrValueAccessor;
+ ValueMustNotBeNullAccessor = originalProvider.ValueMustNotBeNullAccessor;
+ }
+
+ ///
+ public Func MissingBindRequiredValueAccessor
+ {
+ get
+ {
+ return _missingBindRequiredValueAccessor;
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+
+ _missingBindRequiredValueAccessor = value;
+ }
+ }
+
+ ///
+ public Func MissingKeyOrValueAccessor
+ {
+ get
+ {
+ return _missingKeyOrValueAccessor;
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+
+ _missingKeyOrValueAccessor = value;
+ }
+ }
+
+ ///
+ public Func ValueMustNotBeNullAccessor
+ {
+ get
+ {
+ return _valueMustNotBeNullAccessor;
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+
+ _valueMustNotBeNullAccessor = value;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/MutableObjectModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/MutableObjectModelBinder.cs
index b917d18516..0057880cbc 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/MutableObjectModelBinder.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/MutableObjectModelBinder.cs
@@ -6,8 +6,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
-using Microsoft.AspNet.Mvc.Core;
-using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
@@ -414,7 +412,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
bindingContext.ModelState.TryAddModelError(
modelStateKey,
- Resources.FormatModelBinding_MissingBindRequiredMember(propertyName));
+ bindingContext.ModelMetadata.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(
+ propertyName));
}
// For each property that BindPropertiesAsync() attempted to bind, call the setter, recording
diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs
index 3368a48971..bc36685396 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs
@@ -3,7 +3,6 @@
using System;
using System.Threading.Tasks;
-using Microsoft.AspNet.Mvc.Core;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
@@ -51,7 +50,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
bindingContext.ModelState.TryAddModelError(
bindingContext.ModelName,
- Resources.FormatCommon_ValueNotValidForProperty(model));
+ bindingContext.ModelMetadata.ModelBindingMessageProvider.ValueMustNotBeNullAccessor(
+ valueProviderResult.ToString()));
return ModelBindingResult.FailedAsync(bindingContext.ModelName);
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs
index 92969b7783..fc4046d675 100644
--- a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs
@@ -28,6 +28,7 @@ namespace Microsoft.AspNet.Mvc
InputFormatters = new FormatterCollection();
OutputFormatters = new FormatterCollection();
ModelBinders = new List();
+ ModelBindingMessageProvider = new ModelBindingMessageProvider();
ModelMetadataDetailsProviders = new List();
ModelValidatorProviders = new List();
ValidationExcludeFilters = new ExcludeTypeValidationFilterCollection();
@@ -87,7 +88,14 @@ namespace Microsoft.AspNet.Mvc
public IList ModelBinders { get; }
///
- /// Gets a list of instances that will be used to
+ /// Gets the default . Changes here are copied to the
+ /// property of all
+ /// instances unless overridden in a custom .
+ ///
+ public ModelBindingMessageProvider ModelBindingMessageProvider { get; }
+
+ ///
+ /// Gets a list of instances that will be used to
/// create instances.
///
///
@@ -112,7 +120,7 @@ namespace Microsoft.AspNet.Mvc
public FormatterCollection OutputFormatters { get; }
///
- /// Gets or sets the flag which causes content negotiation to ignore Accept header
+ /// Gets or sets the flag which causes content negotiation to ignore Accept header
/// when it contains the media type */*. by default.
///
public bool RespectBrowserAcceptHeader { get; set; }
diff --git a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
index 1049f779ee..f866e339dd 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
@@ -74,22 +74,6 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("ActionExecutor_UnexpectedTaskInstance"), p0, p1);
}
- ///
- /// Replacing the action context is not supported.
- ///
- internal static string ActionContextAccessor_SetValueNotSupported
- {
- get { return GetString("ActionContextAccessor_SetValueNotSupported"); }
- }
-
- ///
- /// Replacing the action context is not supported.
- ///
- internal static string FormatActionContextAccessor_SetValueNotSupported()
- {
- return GetString("ActionContextAccessor_SetValueNotSupported");
- }
-
///
/// An action invoker could not be created for action '{0}'.
///
@@ -171,51 +155,19 @@ namespace Microsoft.AspNet.Mvc.Core
}
///
- /// The supplied route values are ambiguous and can select multiple sets of actions.
+ /// The value '{0}' is invalid.
///
- internal static string ActionSelector_GetCandidateActionsIsAmbiguous
+ internal static string ModelBinding_NullValueNotValid
{
- get { return GetString("ActionSelector_GetCandidateActionsIsAmbiguous"); }
- }
-
- ///
- /// The supplied route values are ambiguous and can select multiple sets of actions.
- ///
- internal static string FormatActionSelector_GetCandidateActionsIsAmbiguous()
- {
- return GetString("ActionSelector_GetCandidateActionsIsAmbiguous");
- }
-
- ///
- /// Property '{0}' is of type '{1}', but this method requires a value of type '{2}'.
- ///
- internal static string ArgumentPropertyUnexpectedType
- {
- get { return GetString("ArgumentPropertyUnexpectedType"); }
- }
-
- ///
- /// Property '{0}' is of type '{1}', but this method requires a value of type '{2}'.
- ///
- internal static string FormatArgumentPropertyUnexpectedType(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ArgumentPropertyUnexpectedType"), p0, p1, p2);
+ get { return GetString("ModelBinding_NullValueNotValid"); }
}
///
/// The value '{0}' is invalid.
///
- internal static string Common_ValueNotValidForProperty
+ internal static string FormatModelBinding_NullValueNotValid(object p0)
{
- get { return GetString("Common_ValueNotValidForProperty"); }
- }
-
- ///
- /// The value '{0}' is invalid.
- ///
- internal static string FormatCommon_ValueNotValidForProperty(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("Common_ValueNotValidForProperty"), p0);
+ return string.Format(CultureInfo.CurrentCulture, GetString("ModelBinding_NullValueNotValid"), p0);
}
///
@@ -282,38 +234,6 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("AsyncResultFilter_InvalidShortCircuit"), p0, p1, p2, p3);
}
- ///
- /// Unable to locate an implementation of IAuthorizationService.
- ///
- internal static string AuthorizeAttribute_AuthorizationServiceMustBeDefined
- {
- get { return GetString("AuthorizeAttribute_AuthorizationServiceMustBeDefined"); }
- }
-
- ///
- /// Unable to locate an implementation of IAuthorizationService.
- ///
- internal static string FormatAuthorizeAttribute_AuthorizationServiceMustBeDefined()
- {
- return GetString("AuthorizeAttribute_AuthorizationServiceMustBeDefined");
- }
-
- ///
- /// OnAuthorization is not implemented by this filter, use OnAuthorizationAsync instead.
- ///
- internal static string AuthorizeAttribute_OnAuthorizationNotImplemented
- {
- get { return GetString("AuthorizeAttribute_OnAuthorizationNotImplemented"); }
- }
-
- ///
- /// OnAuthorization is not implemented by this filter, use OnAuthorizationAsync instead.
- ///
- internal static string FormatAuthorizeAttribute_OnAuthorizationNotImplemented()
- {
- return GetString("AuthorizeAttribute_OnAuthorizationNotImplemented");
- }
-
///
/// The type provided to '{0}' must implement '{1}'.
///
@@ -826,22 +746,6 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("Format_NotValid"), p0);
}
- ///
- /// The property '{0}' on controller '{1}' cannot be activated.
- ///
- internal static string ControllerFactory_PropertyCannotBeActivated
- {
- get { return GetString("ControllerFactory_PropertyCannotBeActivated"); }
- }
-
- ///
- /// The property '{0}' on controller '{1}' cannot be activated.
- ///
- internal static string FormatControllerFactory_PropertyCannotBeActivated(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ControllerFactory_PropertyCannotBeActivated"), p0, p1);
- }
-
///
/// The '{0}' cache profile is not defined.
///
@@ -938,22 +842,6 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("BindingSource_CannotBeGreedy"), p0, p1);
}
- ///
- /// The provided binding source '{0}' is not a request-based binding source. '{1}' requires that the source must represent data from an HTTP request.
- ///
- internal static string BindingSource_MustBeFromRequest
- {
- get { return GetString("BindingSource_MustBeFromRequest"); }
- }
-
- ///
- /// The provided binding source '{0}' is not a request-based binding source. '{1}' requires that the source must represent data from an HTTP request.
- ///
- internal static string FormatBindingSource_MustBeFromRequest(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("BindingSource_MustBeFromRequest"), p0, p1);
- }
-
///
/// The property {0}.{1} could not be found.
///
@@ -1082,38 +970,6 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("ModelBinding_MissingBindRequiredMember"), p0);
}
- ///
- /// A value is required.
- ///
- internal static string ModelBinding_ValueRequired
- {
- get { return GetString("ModelBinding_ValueRequired"); }
- }
-
- ///
- /// A value is required.
- ///
- internal static string FormatModelBinding_ValueRequired()
- {
- return GetString("ModelBinding_ValueRequired");
- }
-
- ///
- /// More than one parameter and/or property is bound to the HTTP request's content.
- ///
- internal static string MultipleBodyParametersOrPropertiesAreNotAllowed
- {
- get { return GetString("MultipleBodyParametersOrPropertiesAreNotAllowed"); }
- }
-
- ///
- /// More than one parameter and/or property is bound to the HTTP request's content.
- ///
- internal static string FormatMultipleBodyParametersOrPropertiesAreNotAllowed()
- {
- return GetString("MultipleBodyParametersOrPropertiesAreNotAllowed");
- }
-
///
/// The type '{0}' does not implement the interface '{1}'.
///
@@ -1130,38 +986,6 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("PropertyBindingPredicateProvider_WrongType"), p0, p1);
}
- ///
- /// The model object inside the metadata claimed to be compatible with '{0}', but was actually '{1}'.
- ///
- internal static string ValidatableObjectAdapter_IncompatibleType
- {
- get { return GetString("ValidatableObjectAdapter_IncompatibleType"); }
- }
-
- ///
- /// The model object inside the metadata claimed to be compatible with '{0}', but was actually '{1}'.
- ///
- internal static string FormatValidatableObjectAdapter_IncompatibleType(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ValidatableObjectAdapter_IncompatibleType"), p0, p1);
- }
-
- ///
- /// Cannot convert value '{0}' to enum type '{1}'.
- ///
- internal static string ValueProviderResult_CannotConvertEnum
- {
- get { return GetString("ValueProviderResult_CannotConvertEnum"); }
- }
-
- ///
- /// Cannot convert value '{0}' to enum type '{1}'.
- ///
- internal static string FormatValueProviderResult_CannotConvertEnum(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ValueProviderResult_CannotConvertEnum"), p0, p1);
- }
-
///
/// The parameter conversion from type '{0}' to type '{1}' failed because no type converter can convert between these types.
///
diff --git a/src/Microsoft.AspNet.Mvc.Core/Resources.resx b/src/Microsoft.AspNet.Mvc.Core/Resources.resx
index b153558e3f..bedebefd24 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Resources.resx
+++ b/src/Microsoft.AspNet.Mvc.Core/Resources.resx
@@ -129,9 +129,6 @@
The method '{0}' on type '{1}' returned a Task instance even though it is not an asynchronous method.
-
- Replacing the action context is not supported.
-
An action invoker could not be created for action '{0}'.
@@ -147,13 +144,7 @@
The '{0}' method of type '{1}' cannot return a null value.
-
- The supplied route values are ambiguous and can select multiple sets of actions.
-
-
- Property '{0}' is of type '{1}', but this method requires a value of type '{2}'.
-
-
+
The value '{0}' is invalid.
@@ -168,12 +159,6 @@
If an {0} cancels execution by setting the {1} property of {2} to 'true', then it cannot call the next filter by invoking {3}.
-
- Unable to locate an implementation of IAuthorizationService.
-
-
- OnAuthorization is not implemented by this filter, use OnAuthorizationAsync instead.
-
The type provided to '{0}' must implement '{1}'.
@@ -279,9 +264,6 @@
The format provided is invalid '{0}'. A format must be a non-empty file-extension, optionally prefixed with a '.' character.
-
- The property '{0}' on controller '{1}' cannot be activated.
-
The '{0}' cache profile is not defined.
@@ -300,9 +282,6 @@
The provided binding source '{0}' is a greedy data source. '{1}' does not support greedy data sources.
-
- The provided binding source '{0}' is not a request-based binding source. '{1}' requires that the source must represent data from an HTTP request.
-
The property {0}.{1} could not be found.
@@ -327,21 +306,9 @@
A value for the '{0}' property was not provided.
-
- A value is required.
-
-
- More than one parameter and/or property is bound to the HTTP request's content.
-
The type '{0}' does not implement the interface '{1}'.
-
- The model object inside the metadata claimed to be compatible with '{0}', but was actually '{1}'.
-
-
- Cannot convert value '{0}' to enum type '{1}'.
-
The parameter conversion from type '{0}' to type '{1}' failed because no type converter can convert between these types.
diff --git a/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ModelMetadataTest.cs b/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ModelMetadataTest.cs
index 17dcce2206..ec7ae4a14a 100644
--- a/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ModelMetadataTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ModelMetadataTest.cs
@@ -457,6 +457,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
}
+ public override IModelBindingMessageProvider ModelBindingMessageProvider
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
public override string NullDisplayText
{
get
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/KeyValuePairModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/KeyValuePairModelBinderTest.cs
index 8c61635250..753b6b3cb7 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/KeyValuePairModelBinderTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/KeyValuePairModelBinderTest.cs
@@ -4,7 +4,6 @@
#if DNX451
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
@@ -54,7 +53,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
Assert.Null(result.Model);
Assert.False(bindingContext.ModelState.IsValid);
Assert.Equal("someName", bindingContext.ModelName);
- Assert.Equal(bindingContext.ModelState["someName.Value"].Errors.First().ErrorMessage, "A value is required.");
+ var state = bindingContext.ModelState["someName.Value"];
+ Assert.NotNull(state);
+ var error = Assert.Single(state.Errors);
+ Assert.Equal("A value is required.", error.ErrorMessage);
}
[Fact]
@@ -117,7 +119,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
{
innerResult = ModelBindingResult.Failed("somename.key");
}
-
+
var innerBinder = new Mock();
innerBinder
.Setup(o => o.BindModelAsync(It.IsAny()))
@@ -161,7 +163,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
// Assert
Assert.NotEqual(ModelBindingResult.NoResult, result);
- Assert.Equal(default(KeyValuePair), Assert.IsType>(result.Model));
+ var model = Assert.IsType>(result.Model);
+ Assert.Equal(default(KeyValuePair), model);
Assert.Equal("modelName", result.Key);
Assert.True(result.IsModelSet);
}
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/Metadata/DefaultBindingMetadataProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/Metadata/DefaultBindingMetadataProviderTest.cs
index b4eb111e5d..ca982f4ba5 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/Metadata/DefaultBindingMetadataProviderTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/Metadata/DefaultBindingMetadataProviderTest.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using Microsoft.AspNet.Mvc.Core;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
@@ -21,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForType(typeof(string)),
new ModelAttributes(attributes));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -45,7 +46,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForType(typeof(string)),
new ModelAttributes(attributes));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -68,7 +69,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForType(typeof(string)),
new ModelAttributes(attributes));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -92,7 +93,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForType(typeof(string)),
new ModelAttributes(attributes));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -115,7 +116,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForType(typeof(string)),
new ModelAttributes(attributes));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -139,7 +140,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForType(typeof(string)),
new ModelAttributes(attributes));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -161,7 +162,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)),
new ModelAttributes(propertyAttributes, typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -184,7 +185,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)),
new ModelAttributes(propertyAttributes, typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -207,7 +208,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)),
new ModelAttributes(propertyAttributes, typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -230,7 +231,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)),
new ModelAttributes(propertyAttributes, typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -253,7 +254,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)),
new ModelAttributes(propertyAttributes, typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -279,7 +280,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)),
new ModelAttributes(propertyAttributes, typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -297,7 +298,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(BindRequiredOnClass)),
new ModelAttributes(propertyAttributes: new object[0], typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -315,7 +316,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(BindNeverOnClass)),
new ModelAttributes(propertyAttributes: new object[0], typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -333,7 +334,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(InheritedBindNeverOnClass)),
new ModelAttributes(propertyAttributes: new object[0], typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -356,7 +357,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(BindNeverOnClass)),
new ModelAttributes(propertyAttributes, typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -379,7 +380,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(BindNeverOnClass)),
new ModelAttributes(propertyAttributes, typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -402,7 +403,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(InheritedBindNeverOnClass)),
new ModelAttributes(propertyAttributes, typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -425,7 +426,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(BindRequiredOnClass)),
new ModelAttributes(propertyAttributes, typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -444,7 +445,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
ModelMetadataIdentity.ForProperty(typeof(string), "Property", typeof(BindRequiredOverridesInheritedBindNever)),
new ModelAttributes(propertyAttributes: new object[0], typeAttributes: new object[0]));
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -473,7 +474,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
context.BindingMetadata.IsBindingAllowed = initialValue;
context.BindingMetadata.IsBindingRequired = initialValue;
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -504,7 +505,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
context.BindingMetadata.IsBindingAllowed = initialValue;
context.BindingMetadata.IsBindingRequired = initialValue;
- var provider = new DefaultBindingMetadataProvider();
+ var provider = new DefaultBindingMetadataProvider(CreateMessageProvider());
// Act
provider.GetBindingMetadata(context);
@@ -514,6 +515,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
Assert.Equal(initialValue, context.BindingMetadata.IsBindingRequired);
}
+ private static ModelBindingMessageProvider CreateMessageProvider()
+ {
+ return new ModelBindingMessageProvider
+ {
+ MissingBindRequiredValueAccessor = Resources.FormatModelBinding_MissingBindRequiredMember,
+ MissingKeyOrValueAccessor = Resources.FormatKeyValuePair_BothKeyAndValueMustBePresent,
+ ValueMustNotBeNullAccessor = Resources.FormatModelBinding_NullValueNotValid,
+ };
+ }
+
[BindNever]
private class BindNeverOnClass
{
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/MutableObjectModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/MutableObjectModelBinderTest.cs
index 1ae12bb4a3..a110f658f7 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/MutableObjectModelBinderTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/MutableObjectModelBinderTest.cs
@@ -787,7 +787,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
property => ModelBindingResult.Failed(property.PropertyName));
var nameProperty = containerMetadata.Properties[nameof(model.Name)];
results[nameProperty] = ModelBindingResult.Success(string.Empty, "John Doe");
-
+
var testableBinder = new TestableMutableObjectModelBinder();
// Act
@@ -1219,7 +1219,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
results[lastNameProperty] = ModelBindingResult.Success(
nameof(model.LastName),
"Doe");
-
+
var testableBinder = new TestableMutableObjectModelBinder();
// Act
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleTypeModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleTypeModelBinderTest.cs
index 602019f75c..36b9c04efd 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleTypeModelBinderTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleTypeModelBinderTest.cs
@@ -105,7 +105,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
Assert.Null(result.Model);
var error = Assert.Single(bindingContext.ModelState["theModelName"].Errors);
- Assert.Equal(error.ErrorMessage, "The value '' is invalid.", StringComparer.Ordinal);
+ Assert.Equal("The value '' is invalid.", error.ErrorMessage, StringComparer.Ordinal);
Assert.Null(error.Exception);
}
diff --git a/test/Microsoft.AspNet.Mvc.DataAnnotations.Test/ModelMetadataProviderTest.cs b/test/Microsoft.AspNet.Mvc.DataAnnotations.Test/ModelMetadataProviderTest.cs
index 0ececa2501..4b38426f03 100644
--- a/test/Microsoft.AspNet.Mvc.DataAnnotations.Test/ModelMetadataProviderTest.cs
+++ b/test/Microsoft.AspNet.Mvc.DataAnnotations.Test/ModelMetadataProviderTest.cs
@@ -1026,13 +1026,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
public AttributeInjectModelMetadataProvider(object[] attributes)
: base(new DefaultCompositeMetadataDetailsProvider(new IMetadataDetailsProvider[]
{
- new DefaultBindingMetadataProvider(),
+ new DefaultBindingMetadataProvider(CreateMessageProvider()),
new DataAnnotationsMetadataProvider(),
}))
{
_attributes = attributes;
}
+ private static ModelBindingMessageProvider CreateMessageProvider()
+ {
+ return new ModelBindingMessageProvider
+ {
+ MissingBindRequiredValueAccessor = name => $"A value for the '{ name }' property was not provided.",
+ MissingKeyOrValueAccessor = () => $"A value is required.",
+ ValueMustNotBeNullAccessor = value => $"The value '{ value }' is invalid.",
+ };
+ }
+
protected override DefaultMetadataDetails CreateTypeDetails(ModelMetadataIdentity key)
{
var entry = base.CreateTypeDetails(key);
diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs
index 39c0f46713..ceb01a9305 100644
--- a/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs
+++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Abstractions;
+using Microsoft.AspNet.Mvc.Controllers;
using Microsoft.AspNet.Mvc.ModelBinding;
using Xunit;
@@ -46,13 +47,183 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Key").Value;
Assert.Equal("key0", entry.AttemptedValue);
- Assert.Equal("key0", entry.RawValue);
+ Assert.Equal("key0", entry.RawValue);
entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Value").Value;
Assert.Equal("10", entry.AttemptedValue);
Assert.Equal("10", entry.RawValue);
}
+ [Fact]
+ public async Task KeyValuePairModelBinder_SimpleTypes_WithNoKey_AddsError()
+ {
+ // Arrange
+ var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
+ var parameter = new ParameterDescriptor
+ {
+ Name = "parameter",
+ ParameterType = typeof(KeyValuePair)
+ };
+ var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
+ {
+ request.QueryString = new QueryString("?parameter.Value=10");
+ });
+ var modelState = new ModelStateDictionary();
+
+ // Act
+ var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
+
+ // Assert
+ Assert.False(modelBindingResult.IsModelSet);
+ Assert.Equal(2, modelState.Count);
+
+ Assert.False(modelState.IsValid);
+ Assert.Equal(1, modelState.ErrorCount);
+
+ var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Key").Value;
+ var error = Assert.Single(entry.Errors);
+ Assert.Null(error.Exception);
+ Assert.Equal("A value is required.", error.ErrorMessage);
+
+ entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Value").Value;
+ Assert.Empty(entry.Errors);
+ Assert.Equal("10", entry.AttemptedValue);
+ Assert.Equal("10", entry.RawValue);
+ }
+
+ [Fact]
+ public async Task KeyValuePairModelBinder_SimpleTypes_WithNoKey_AndCustomizedMessage_AddsGivenMessage()
+ {
+ // Arrange
+ var metadataProvider = new TestModelMetadataProvider();
+ metadataProvider
+ .ForType(typeof(KeyValuePair))
+ .BindingDetails((System.Action)(binding =>
+ {
+ // A real details provider could customize message based on BindingMetadataProviderContext.
+ binding.ModelBindingMessageProvider.MissingKeyOrValueAccessor = () => $"Hurts when nothing is provided.";
+ }));
+
+ var argumentBinder = new DefaultControllerActionArgumentBinder(
+ metadataProvider,
+ ModelBindingTestHelper.GetObjectValidator());
+ var parameter = new ParameterDescriptor
+ {
+ Name = "parameter",
+ ParameterType = typeof(KeyValuePair)
+ };
+ var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
+ {
+ request.QueryString = new QueryString("?parameter.Value=10");
+ });
+ var modelState = new ModelStateDictionary();
+
+ // Act
+ var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
+
+ // Assert
+ Assert.False(modelBindingResult.IsModelSet);
+ Assert.Equal(2, modelState.Count);
+
+ Assert.False(modelState.IsValid);
+ Assert.Equal(1, modelState.ErrorCount);
+
+ var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Key").Value;
+ var error = Assert.Single(entry.Errors);
+ Assert.Null(error.Exception);
+ Assert.Equal("Hurts when nothing is provided.", error.ErrorMessage);
+
+ entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Value").Value;
+ Assert.Empty(entry.Errors);
+ Assert.Equal("10", entry.AttemptedValue);
+ Assert.Equal("10", entry.RawValue);
+ }
+
+ [Fact]
+ public async Task KeyValuePairModelBinder_SimpleTypes_WithNoValue_AddsError()
+ {
+ // Arrange
+ var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
+ var parameter = new ParameterDescriptor
+ {
+ Name = "parameter",
+ ParameterType = typeof(KeyValuePair)
+ };
+ var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
+ {
+ request.QueryString = new QueryString("?parameter.Key=10");
+ });
+ var modelState = new ModelStateDictionary();
+
+ // Act
+ var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
+
+ // Assert
+ Assert.False(modelBindingResult.IsModelSet);
+ Assert.Equal(2, modelState.Count);
+
+ Assert.False(modelState.IsValid);
+ Assert.Equal(1, modelState.ErrorCount);
+
+ var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Key").Value;
+ Assert.Empty(entry.Errors);
+ Assert.Equal("10", entry.AttemptedValue);
+ Assert.Equal("10", entry.RawValue);
+
+ entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Value").Value;
+ var error = Assert.Single(entry.Errors);
+ Assert.Null(error.Exception);
+ Assert.Equal("A value is required.", error.ErrorMessage);
+ }
+
+ [Fact]
+ public async Task KeyValuePairModelBinder_SimpleTypes_WithNoValue_AndCustomizedMessage_AddsGivenMessage()
+ {
+ // Arrange
+ var metadataProvider = new TestModelMetadataProvider();
+ metadataProvider
+ .ForType(typeof(KeyValuePair))
+ .BindingDetails((System.Action)(binding =>
+ {
+ // A real details provider could customize message based on BindingMetadataProviderContext.
+ binding.ModelBindingMessageProvider.MissingKeyOrValueAccessor = () => $"Hurts when nothing is provided.";
+ }));
+ var argumentBinder = new DefaultControllerActionArgumentBinder(
+ metadataProvider,
+ ModelBindingTestHelper.GetObjectValidator());
+
+ var parameter = new ParameterDescriptor
+ {
+ Name = "parameter",
+ ParameterType = typeof(KeyValuePair)
+ };
+ var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
+ {
+ request.QueryString = new QueryString("?parameter.Key=10");
+ });
+ var modelState = new ModelStateDictionary();
+
+ // Act
+ var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
+
+ // Assert
+ Assert.False(modelBindingResult.IsModelSet);
+ Assert.Equal(2, modelState.Count);
+
+ Assert.False(modelState.IsValid);
+ Assert.Equal(1, modelState.ErrorCount);
+
+ var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Key").Value;
+ Assert.Empty(entry.Errors);
+ Assert.Equal("10", entry.AttemptedValue);
+ Assert.Equal("10", entry.RawValue);
+
+ entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Value").Value;
+ var error = Assert.Single(entry.Errors);
+ Assert.Null(error.Exception);
+ Assert.Equal("Hurts when nothing is provided.", error.ErrorMessage);
+ }
+
[Fact]
public async Task KeyValuePairModelBinder_BindsKeyValuePairOfSimpleType_WithExplicitPrefix_Success()
{
@@ -90,7 +261,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix.Key").Value;
Assert.Equal("key0", entry.AttemptedValue);
- Assert.Equal("key0", entry.RawValue);
+ Assert.Equal("key0", entry.RawValue);
entry = Assert.Single(modelState, kvp => kvp.Key == "prefix.Value").Value;
Assert.Equal("10", entry.AttemptedValue);
@@ -130,7 +301,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
var entry = Assert.Single(modelState, kvp => kvp.Key == "Key").Value;
Assert.Equal("key0", entry.AttemptedValue);
- Assert.Equal("key0", entry.RawValue);
+ Assert.Equal("key0", entry.RawValue);
entry = Assert.Single(modelState, kvp => kvp.Key == "Value").Value;
Assert.Equal("10", entry.AttemptedValue);
@@ -207,7 +378,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Key").Value;
Assert.Equal("key0", entry.AttemptedValue);
- Assert.Equal("key0", entry.RawValue);
+ Assert.Equal("key0", entry.RawValue);
entry = Assert.Single(modelState, kvp => kvp.Key == "parameter.Value.Id").Value;
Assert.Equal("10", entry.AttemptedValue);
@@ -252,7 +423,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix.Key").Value;
Assert.Equal("key0", entry.AttemptedValue);
- Assert.Equal("key0", entry.RawValue);
+ Assert.Equal("key0", entry.RawValue);
entry = Assert.Single(modelState, kvp => kvp.Key == "prefix.Value.Id").Value;
Assert.Equal("10", entry.AttemptedValue);
@@ -293,7 +464,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
var entry = Assert.Single(modelState, kvp => kvp.Key == "Key").Value;
Assert.Equal("key0", entry.AttemptedValue);
- Assert.Equal("key0", entry.RawValue);
+ Assert.Equal("key0", entry.RawValue);
entry = Assert.Single(modelState, kvp => kvp.Key == "Value.Id").Value;
Assert.Equal("10", entry.AttemptedValue);
diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/MutableObjectModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/MutableObjectModelBinderIntegrationTest.cs
index 08d10d2686..55ca5f20f9 100644
--- a/test/Microsoft.AspNet.Mvc.IntegrationTests/MutableObjectModelBinderIntegrationTest.cs
+++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/MutableObjectModelBinderIntegrationTest.cs
@@ -10,6 +10,7 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features.Internal;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
+using Microsoft.AspNet.Mvc.Controllers;
using Microsoft.AspNet.Mvc.Infrastructure;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.Primitives;
@@ -859,8 +860,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements.
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
- request.QueryString =
- new QueryString("?Name=bill&ProductIds[0]=10&ProductIds[1]=11");
+ request.QueryString = new QueryString("?Name=bill&ProductIds[0]=10&ProductIds[1]=11");
});
var modelState = new ModelStateDictionary();
@@ -1032,8 +1032,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements.
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
- request.QueryString =
- new QueryString("?Name=bill&ProductIds[0]=10&ProductIds[1]=11");
+ request.QueryString = new QueryString("?Name=bill&ProductIds[0]=10&ProductIds[1]=11");
});
var modelState = new ModelStateDictionary();
@@ -1205,8 +1204,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements.
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
- request.QueryString =
- new QueryString("?Name=bill&ProductIds[0].Key=key0&ProductIds[0].Value=10");
+ request.QueryString = new QueryString("?Name=bill&ProductIds[0].Key=key0&ProductIds[0].Value=10");
});
var modelState = new ModelStateDictionary();
@@ -1378,8 +1376,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements.
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
- request.QueryString =
- new QueryString("?Name=bill&ProductId.Key=key0&ProductId.Value=10");
+ request.QueryString = new QueryString("?Name=bill&ProductId.Key=key0&ProductId.Value=10");
});
var modelState = new ModelStateDictionary();
@@ -1563,9 +1560,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
};
// No Data
- var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
- {
- });
+ var operationContext = ModelBindingTestHelper.GetOperationBindingContext();
var modelState = new ModelStateDictionary();
@@ -1589,6 +1584,53 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal("A value for the 'Customer' property was not provided.", error.ErrorMessage);
}
+ [Fact]
+ public async Task MutableObjectModelBinder_WithBindRequired_NoData_AndCustomizedMessage_AddsGivenMessage()
+ {
+ // Arrange
+ var metadataProvider = new TestModelMetadataProvider();
+ metadataProvider
+ .ForType(typeof(Order10))
+ .BindingDetails((Action)(binding =>
+ {
+ // A real details provider could customize message based on BindingMetadataProviderContext.
+ binding.ModelBindingMessageProvider.MissingBindRequiredValueAccessor =
+ name => $"Hurts when '{ name }' is not provided.";
+ }));
+ var argumentBinder = new DefaultControllerActionArgumentBinder(
+ metadataProvider,
+ ModelBindingTestHelper.GetObjectValidator());
+ var parameter = new ParameterDescriptor()
+ {
+ Name = "parameter",
+ ParameterType = typeof(Order10)
+ };
+
+ // No Data
+ var operationContext = ModelBindingTestHelper.GetOperationBindingContext();
+
+ var modelState = new ModelStateDictionary();
+
+ // Act
+ var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
+
+ // Assert
+ Assert.True(modelBindingResult.IsModelSet);
+
+ var model = Assert.IsType(modelBindingResult.Model);
+ Assert.Null(model.Customer);
+
+ Assert.Equal(1, modelState.Count);
+ Assert.Equal(1, modelState.ErrorCount);
+ Assert.False(modelState.IsValid);
+
+ var entry = Assert.Single(modelState, e => e.Key == "Customer").Value;
+ Assert.Null(entry.RawValue);
+ Assert.Null(entry.AttemptedValue);
+ var error = Assert.Single(modelState["Customer"].Errors);
+ Assert.Equal("Hurts when 'Customer' is not provided.", error.ErrorMessage);
+ }
+
private class Order11
{
public Person11 Customer { get; set; }
diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs
index ba2f524781..ebfed42fd2 100644
--- a/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs
+++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
+using Microsoft.AspNet.Mvc.Controllers;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.Primitives;
using Xunit;
@@ -262,7 +263,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
};
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
- request.QueryString = QueryString.Create("Parameter1", string.Empty);
+ request.QueryString = QueryString.Create("Parameter1", " ");
});
var modelState = new ModelStateDictionary();
@@ -277,6 +278,58 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// Model
Assert.Null(modelBindingResult.Model);
+ // ModelState
+ Assert.False(modelState.IsValid);
+ var key = Assert.Single(modelState.Keys);
+ Assert.Equal("Parameter1", key);
+ Assert.Equal(" ", modelState[key].AttemptedValue);
+ Assert.Equal(" ", modelState[key].RawValue);
+ var error = Assert.Single(modelState[key].Errors);
+ Assert.Equal("The value ' ' is invalid.", error.ErrorMessage, StringComparer.Ordinal);
+ Assert.Null(error.Exception);
+ }
+
+ [Theory]
+ [InlineData(typeof(int))]
+ [InlineData(typeof(bool))]
+ public async Task BindParameter_WithEmptyData_AndPerTypeMessage_AddsGivenMessage(Type parameterType)
+ {
+ // Arrange
+ var metadataProvider = new TestModelMetadataProvider();
+ metadataProvider
+ .ForType(parameterType)
+ .BindingDetails((Action)(binding =>
+ {
+ // A real details provider could customize message based on BindingMetadataProviderContext.
+ binding.ModelBindingMessageProvider.ValueMustNotBeNullAccessor =
+ value => $"Hurts when '{ value }' is provided.";
+ }));
+ var argumentBinder = new DefaultControllerActionArgumentBinder(
+ metadataProvider,
+ ModelBindingTestHelper.GetObjectValidator());
+ var parameter = new ParameterDescriptor
+ {
+ Name = "Parameter1",
+ BindingInfo = new BindingInfo(),
+
+ ParameterType = parameterType
+ };
+ var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
+ {
+ request.QueryString = QueryString.Create("Parameter1", string.Empty);
+ });
+ var modelState = new ModelStateDictionary();
+
+ // Act
+ var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
+
+ // Assert
+ // ModelBindingResult
+ Assert.False(modelBindingResult.IsModelSet);
+
+ // Model
+ Assert.Null(modelBindingResult.Model);
+
// ModelState
Assert.False(modelState.IsValid);
var key = Assert.Single(modelState.Keys);
@@ -284,7 +337,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal(string.Empty, modelState[key].AttemptedValue);
Assert.Equal(string.Empty, modelState[key].RawValue);
var error = Assert.Single(modelState[key].Errors);
- Assert.Equal(error.ErrorMessage, "The value '' is invalid.", StringComparer.Ordinal);
+ Assert.Equal("Hurts when '' is provided.", error.ErrorMessage, StringComparer.Ordinal);
Assert.Null(error.Exception);
}
diff --git a/test/Microsoft.AspNet.Mvc.TestCommon/TestModelMetadataProvider.cs b/test/Microsoft.AspNet.Mvc.TestCommon/TestModelMetadataProvider.cs
index 4fe13d7367..0c0d8dd61f 100644
--- a/test/Microsoft.AspNet.Mvc.TestCommon/TestModelMetadataProvider.cs
+++ b/test/Microsoft.AspNet.Mvc.TestCommon/TestModelMetadataProvider.cs
@@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
var detailsProviders = new IMetadataDetailsProvider[]
{
- new DefaultBindingMetadataProvider(),
+ new DefaultBindingMetadataProvider(CreateMessageProvider()),
new DefaultValidationMetadataProvider(),
new DataAnnotationsMetadataProvider(),
new DataMemberRequiredBindingMetadataProvider(),
@@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private TestModelMetadataProvider(TestModelMetadataDetailsProvider detailsProvider)
: base(new DefaultCompositeMetadataDetailsProvider(new IMetadataDetailsProvider[]
{
- new DefaultBindingMetadataProvider(),
+ new DefaultBindingMetadataProvider(CreateMessageProvider()),
new DefaultValidationMetadataProvider(),
new DataAnnotationsMetadataProvider(),
detailsProvider
@@ -76,6 +76,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return ForProperty(typeof(TContainer), propertyName);
}
+ private static ModelBindingMessageProvider CreateMessageProvider()
+ {
+ return new ModelBindingMessageProvider
+ {
+ MissingBindRequiredValueAccessor = name => $"A value for the '{ name }' property was not provided.",
+ MissingKeyOrValueAccessor = () => $"A value is required.",
+ ValueMustNotBeNullAccessor = value => $"The value '{ value }' is invalid.",
+ };
+ }
+
private class TestModelMetadataDetailsProvider :
IBindingMetadataProvider,
IDisplayMetadataProvider,