Support user overrides of system-provided `ModelError` messages
- #2969 - add `ModelBindingMessages` for configuration and `IBindingMetadataProvider` overrides - use `interface` to avoid `new` oddities when adding a setter to an `abstract` property - add `IModelBindingMessages` to `ModelMetadata` for use in rest of the product code - plumb the various bits through the system - add integration tests using a custom `IBindingMetadataProvider`s to override messages nits: - remove unused resources - use `AttemptedValue` and not `model` in `SimpleTypeModelBinder`
This commit is contained in:
parent
0d6edf240a
commit
28aec3f5cc
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Provider for error messages the model binding system detects.
|
||||
/// </summary>
|
||||
public interface IModelBindingMessageProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Error message the model binding system adds when a property with an associated
|
||||
/// <c>BindRequiredAttribute</c> is not bound.
|
||||
/// </summary>
|
||||
/// <value>Default <see cref="string"/> is "A value for the '{0}' property was not provided.".</value>
|
||||
Func<string, string> MissingBindRequiredValueAccessor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Error message the model binding system adds when either the key or the value of a
|
||||
/// <see cref="System.Collections.Generic.KeyValuePair{TKey, TValue}"/> is bound but not both.
|
||||
/// </summary>
|
||||
/// <value>Default <see cref="string"/> is "A value is required.".</value>
|
||||
Func<string> MissingKeyOrValueAccessor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Error message the model binding system adds when a <c>null</c> value is bound to a
|
||||
/// non-<see cref="Nullable"/> property.
|
||||
/// </summary>
|
||||
/// <value>Default <see cref="string"/> is "The value '{0}' is invalid.".</value>
|
||||
Func<string, string> ValueMustNotBeNullAccessor { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -242,6 +242,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </remarks>
|
||||
public abstract bool IsRequired { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IModelBindingMessageProvider"/> instance.
|
||||
/// </summary>
|
||||
public abstract IModelBindingMessageProvider ModelBindingMessageProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating where the current metadata should be ordered relative to other properties
|
||||
/// in its containing type.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
/// </summary>
|
||||
public class BindingMetadata
|
||||
{
|
||||
private ModelBindingMessageProvider _messageProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ModelBinding.BindingSource"/>.
|
||||
/// See <see cref="ModelMetadata.BindingSource"/>.
|
||||
|
|
@ -50,6 +52,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
/// </summary>
|
||||
public bool? IsReadOnly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Metadata.ModelBindingMessageProvider"/> instance. See
|
||||
/// <see cref="ModelMetadata.ModelBindingMessageProvider"/>.
|
||||
/// </summary>
|
||||
public ModelBindingMessageProvider ModelBindingMessageProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
return _messageProvider;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
_messageProvider = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ModelBinding.IPropertyBindingPredicateProvider"/>.
|
||||
/// See <see cref="ModelMetadata.PropertyBindingPredicateProvider"/>.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
/// </summary>
|
||||
public class DefaultBindingMetadataProvider : IBindingMetadataProvider
|
||||
{
|
||||
private readonly ModelBindingMessageProvider _messageProvider;
|
||||
|
||||
public DefaultBindingMetadataProvider(ModelBindingMessageProvider messageProvider)
|
||||
{
|
||||
if (messageProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(messageProvider));
|
||||
}
|
||||
|
||||
_messageProvider = messageProvider;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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<IPropertyBindingPredicateProvider>().ToArray();
|
||||
if (predicateProviders.Length > 0)
|
||||
|
|
@ -62,7 +78,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
var bindingBehavior = context.PropertyAttributes.OfType<BindingBehaviorAttribute>().FirstOrDefault();
|
||||
if (bindingBehavior == null)
|
||||
{
|
||||
bindingBehavior =
|
||||
bindingBehavior =
|
||||
context.Key.ContainerType.GetTypeInfo()
|
||||
.GetCustomAttributes(typeof(BindingBehaviorAttribute), inherit: true)
|
||||
.OfType<BindingBehaviorAttribute>()
|
||||
|
|
|
|||
|
|
@ -418,6 +418,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IModelBindingMessageProvider ModelBindingMessageProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
return BindingMetadata.ModelBindingMessageProvider;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string NullDisplayText
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Read / write <see cref="IModelBindingMessageProvider"/> implementation.
|
||||
/// </summary>
|
||||
public class ModelBindingMessageProvider : IModelBindingMessageProvider
|
||||
{
|
||||
private Func<string, string> _missingBindRequiredValueAccessor;
|
||||
private Func<string> _missingKeyOrValueAccessor;
|
||||
private Func<string, string> _valueMustNotBeNullAccessor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModelBindingMessageProvider"/> class.
|
||||
/// </summary>
|
||||
public ModelBindingMessageProvider()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModelBindingMessageProvider"/> class based on
|
||||
/// <paramref name="originalProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="originalProvider">The <see cref="ModelBindingMessageProvider"/> to duplicate.</param>
|
||||
public ModelBindingMessageProvider(ModelBindingMessageProvider originalProvider)
|
||||
{
|
||||
if (originalProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(originalProvider));
|
||||
}
|
||||
|
||||
MissingBindRequiredValueAccessor = originalProvider.MissingBindRequiredValueAccessor;
|
||||
MissingKeyOrValueAccessor = originalProvider.MissingKeyOrValueAccessor;
|
||||
ValueMustNotBeNullAccessor = originalProvider.ValueMustNotBeNullAccessor;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Func<string, string> MissingBindRequiredValueAccessor
|
||||
{
|
||||
get
|
||||
{
|
||||
return _missingBindRequiredValueAccessor;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
_missingBindRequiredValueAccessor = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Func<string> MissingKeyOrValueAccessor
|
||||
{
|
||||
get
|
||||
{
|
||||
return _missingKeyOrValueAccessor;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
_missingKeyOrValueAccessor = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Func<string, string> ValueMustNotBeNullAccessor
|
||||
{
|
||||
get
|
||||
{
|
||||
return _valueMustNotBeNullAccessor;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
_valueMustNotBeNullAccessor = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
InputFormatters = new FormatterCollection<IInputFormatter>();
|
||||
OutputFormatters = new FormatterCollection<IOutputFormatter>();
|
||||
ModelBinders = new List<IModelBinder>();
|
||||
ModelBindingMessageProvider = new ModelBindingMessageProvider();
|
||||
ModelMetadataDetailsProviders = new List<IMetadataDetailsProvider>();
|
||||
ModelValidatorProviders = new List<IModelValidatorProvider>();
|
||||
ValidationExcludeFilters = new ExcludeTypeValidationFilterCollection();
|
||||
|
|
@ -87,7 +88,14 @@ namespace Microsoft.AspNet.Mvc
|
|||
public IList<IModelBinder> ModelBinders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IMetadataDetailsProvider"/> instances that will be used to
|
||||
/// Gets the default <see cref="IModelBindingMessageProvider"/>. Changes here are copied to the
|
||||
/// <see cref="ModelMetadata.ModelBindingMessageProvider"/> property of all <see cref="ModelMetadata"/>
|
||||
/// instances unless overridden in a custom <see cref="IBindingMetadataProvider"/>.
|
||||
/// </summary>
|
||||
public ModelBindingMessageProvider ModelBindingMessageProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IMetadataDetailsProvider"/> instances that will be used to
|
||||
/// create <see cref="ModelMetadata"/> instances.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
|
|
@ -112,7 +120,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
public FormatterCollection<IOutputFormatter> OutputFormatters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 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 */*. <see langword="false"/> by default.
|
||||
/// </summary>
|
||||
public bool RespectBrowserAcceptHeader { get; set; }
|
||||
|
|
|
|||
|
|
@ -74,22 +74,6 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("ActionExecutor_UnexpectedTaskInstance"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replacing the action context is not supported.
|
||||
/// </summary>
|
||||
internal static string ActionContextAccessor_SetValueNotSupported
|
||||
{
|
||||
get { return GetString("ActionContextAccessor_SetValueNotSupported"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replacing the action context is not supported.
|
||||
/// </summary>
|
||||
internal static string FormatActionContextAccessor_SetValueNotSupported()
|
||||
{
|
||||
return GetString("ActionContextAccessor_SetValueNotSupported");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An action invoker could not be created for action '{0}'.
|
||||
/// </summary>
|
||||
|
|
@ -171,51 +155,19 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// The supplied route values are ambiguous and can select multiple sets of actions.
|
||||
/// The value '{0}' is invalid.
|
||||
/// </summary>
|
||||
internal static string ActionSelector_GetCandidateActionsIsAmbiguous
|
||||
internal static string ModelBinding_NullValueNotValid
|
||||
{
|
||||
get { return GetString("ActionSelector_GetCandidateActionsIsAmbiguous"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The supplied route values are ambiguous and can select multiple sets of actions.
|
||||
/// </summary>
|
||||
internal static string FormatActionSelector_GetCandidateActionsIsAmbiguous()
|
||||
{
|
||||
return GetString("ActionSelector_GetCandidateActionsIsAmbiguous");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property '{0}' is of type '{1}', but this method requires a value of type '{2}'.
|
||||
/// </summary>
|
||||
internal static string ArgumentPropertyUnexpectedType
|
||||
{
|
||||
get { return GetString("ArgumentPropertyUnexpectedType"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property '{0}' is of type '{1}', but this method requires a value of type '{2}'.
|
||||
/// </summary>
|
||||
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"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value '{0}' is invalid.
|
||||
/// </summary>
|
||||
internal static string Common_ValueNotValidForProperty
|
||||
internal static string FormatModelBinding_NullValueNotValid(object p0)
|
||||
{
|
||||
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);
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ModelBinding_NullValueNotValid"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -282,38 +234,6 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("AsyncResultFilter_InvalidShortCircuit"), p0, p1, p2, p3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unable to locate an implementation of IAuthorizationService.
|
||||
/// </summary>
|
||||
internal static string AuthorizeAttribute_AuthorizationServiceMustBeDefined
|
||||
{
|
||||
get { return GetString("AuthorizeAttribute_AuthorizationServiceMustBeDefined"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unable to locate an implementation of IAuthorizationService.
|
||||
/// </summary>
|
||||
internal static string FormatAuthorizeAttribute_AuthorizationServiceMustBeDefined()
|
||||
{
|
||||
return GetString("AuthorizeAttribute_AuthorizationServiceMustBeDefined");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnAuthorization is not implemented by this filter, use OnAuthorizationAsync instead.
|
||||
/// </summary>
|
||||
internal static string AuthorizeAttribute_OnAuthorizationNotImplemented
|
||||
{
|
||||
get { return GetString("AuthorizeAttribute_OnAuthorizationNotImplemented"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnAuthorization is not implemented by this filter, use OnAuthorizationAsync instead.
|
||||
/// </summary>
|
||||
internal static string FormatAuthorizeAttribute_OnAuthorizationNotImplemented()
|
||||
{
|
||||
return GetString("AuthorizeAttribute_OnAuthorizationNotImplemented");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type provided to '{0}' must implement '{1}'.
|
||||
/// </summary>
|
||||
|
|
@ -826,22 +746,6 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("Format_NotValid"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The property '{0}' on controller '{1}' cannot be activated.
|
||||
/// </summary>
|
||||
internal static string ControllerFactory_PropertyCannotBeActivated
|
||||
{
|
||||
get { return GetString("ControllerFactory_PropertyCannotBeActivated"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The property '{0}' on controller '{1}' cannot be activated.
|
||||
/// </summary>
|
||||
internal static string FormatControllerFactory_PropertyCannotBeActivated(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ControllerFactory_PropertyCannotBeActivated"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' cache profile is not defined.
|
||||
/// </summary>
|
||||
|
|
@ -938,22 +842,6 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("BindingSource_CannotBeGreedy"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The provided binding source '{0}' is not a request-based binding source. '{1}' requires that the source must represent data from an HTTP request.
|
||||
/// </summary>
|
||||
internal static string BindingSource_MustBeFromRequest
|
||||
{
|
||||
get { return GetString("BindingSource_MustBeFromRequest"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The provided binding source '{0}' is not a request-based binding source. '{1}' requires that the source must represent data from an HTTP request.
|
||||
/// </summary>
|
||||
internal static string FormatBindingSource_MustBeFromRequest(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("BindingSource_MustBeFromRequest"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The property {0}.{1} could not be found.
|
||||
/// </summary>
|
||||
|
|
@ -1082,38 +970,6 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("ModelBinding_MissingBindRequiredMember"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A value is required.
|
||||
/// </summary>
|
||||
internal static string ModelBinding_ValueRequired
|
||||
{
|
||||
get { return GetString("ModelBinding_ValueRequired"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A value is required.
|
||||
/// </summary>
|
||||
internal static string FormatModelBinding_ValueRequired()
|
||||
{
|
||||
return GetString("ModelBinding_ValueRequired");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// More than one parameter and/or property is bound to the HTTP request's content.
|
||||
/// </summary>
|
||||
internal static string MultipleBodyParametersOrPropertiesAreNotAllowed
|
||||
{
|
||||
get { return GetString("MultipleBodyParametersOrPropertiesAreNotAllowed"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// More than one parameter and/or property is bound to the HTTP request's content.
|
||||
/// </summary>
|
||||
internal static string FormatMultipleBodyParametersOrPropertiesAreNotAllowed()
|
||||
{
|
||||
return GetString("MultipleBodyParametersOrPropertiesAreNotAllowed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type '{0}' does not implement the interface '{1}'.
|
||||
/// </summary>
|
||||
|
|
@ -1130,38 +986,6 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("PropertyBindingPredicateProvider_WrongType"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The model object inside the metadata claimed to be compatible with '{0}', but was actually '{1}'.
|
||||
/// </summary>
|
||||
internal static string ValidatableObjectAdapter_IncompatibleType
|
||||
{
|
||||
get { return GetString("ValidatableObjectAdapter_IncompatibleType"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The model object inside the metadata claimed to be compatible with '{0}', but was actually '{1}'.
|
||||
/// </summary>
|
||||
internal static string FormatValidatableObjectAdapter_IncompatibleType(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ValidatableObjectAdapter_IncompatibleType"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot convert value '{0}' to enum type '{1}'.
|
||||
/// </summary>
|
||||
internal static string ValueProviderResult_CannotConvertEnum
|
||||
{
|
||||
get { return GetString("ValueProviderResult_CannotConvertEnum"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot convert value '{0}' to enum type '{1}'.
|
||||
/// </summary>
|
||||
internal static string FormatValueProviderResult_CannotConvertEnum(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ValueProviderResult_CannotConvertEnum"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The parameter conversion from type '{0}' to type '{1}' failed because no type converter can convert between these types.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -129,9 +129,6 @@
|
|||
<data name="ActionExecutor_UnexpectedTaskInstance" xml:space="preserve">
|
||||
<value>The method '{0}' on type '{1}' returned a Task instance even though it is not an asynchronous method.</value>
|
||||
</data>
|
||||
<data name="ActionContextAccessor_SetValueNotSupported" xml:space="preserve">
|
||||
<value>Replacing the action context is not supported.</value>
|
||||
</data>
|
||||
<data name="ActionInvokerFactory_CouldNotCreateInvoker" xml:space="preserve">
|
||||
<value>An action invoker could not be created for action '{0}'.</value>
|
||||
</data>
|
||||
|
|
@ -147,13 +144,7 @@
|
|||
<data name="TypeMethodMustReturnNotNullValue" xml:space="preserve">
|
||||
<value>The '{0}' method of type '{1}' cannot return a null value.</value>
|
||||
</data>
|
||||
<data name="ActionSelector_GetCandidateActionsIsAmbiguous" xml:space="preserve">
|
||||
<value>The supplied route values are ambiguous and can select multiple sets of actions.</value>
|
||||
</data>
|
||||
<data name="ArgumentPropertyUnexpectedType" xml:space="preserve">
|
||||
<value>Property '{0}' is of type '{1}', but this method requires a value of type '{2}'.</value>
|
||||
</data>
|
||||
<data name="Common_ValueNotValidForProperty" xml:space="preserve">
|
||||
<data name="ModelBinding_NullValueNotValid" xml:space="preserve">
|
||||
<value>The value '{0}' is invalid.</value>
|
||||
</data>
|
||||
<data name="Invalid_IncludePropertyExpression" xml:space="preserve">
|
||||
|
|
@ -168,12 +159,6 @@
|
|||
<data name="AsyncResultFilter_InvalidShortCircuit" xml:space="preserve">
|
||||
<value>If an {0} cancels execution by setting the {1} property of {2} to 'true', then it cannot call the next filter by invoking {3}.</value>
|
||||
</data>
|
||||
<data name="AuthorizeAttribute_AuthorizationServiceMustBeDefined" xml:space="preserve">
|
||||
<value>Unable to locate an implementation of IAuthorizationService.</value>
|
||||
</data>
|
||||
<data name="AuthorizeAttribute_OnAuthorizationNotImplemented" xml:space="preserve">
|
||||
<value>OnAuthorization is not implemented by this filter, use OnAuthorizationAsync instead.</value>
|
||||
</data>
|
||||
<data name="FilterFactoryAttribute_TypeMustImplementIFilter" xml:space="preserve">
|
||||
<value>The type provided to '{0}' must implement '{1}'.</value>
|
||||
</data>
|
||||
|
|
@ -279,9 +264,6 @@
|
|||
<data name="Format_NotValid" xml:space="preserve">
|
||||
<value>The format provided is invalid '{0}'. A format must be a non-empty file-extension, optionally prefixed with a '.' character.</value>
|
||||
</data>
|
||||
<data name="ControllerFactory_PropertyCannotBeActivated" xml:space="preserve">
|
||||
<value>The property '{0}' on controller '{1}' cannot be activated.</value>
|
||||
</data>
|
||||
<data name="CacheProfileNotFound" xml:space="preserve">
|
||||
<value>The '{0}' cache profile is not defined.</value>
|
||||
</data>
|
||||
|
|
@ -300,9 +282,6 @@
|
|||
<data name="BindingSource_CannotBeGreedy" xml:space="preserve">
|
||||
<value>The provided binding source '{0}' is a greedy data source. '{1}' does not support greedy data sources.</value>
|
||||
</data>
|
||||
<data name="BindingSource_MustBeFromRequest" xml:space="preserve">
|
||||
<value>The provided binding source '{0}' is not a request-based binding source. '{1}' requires that the source must represent data from an HTTP request.</value>
|
||||
</data>
|
||||
<data name="Common_PropertyNotFound" xml:space="preserve">
|
||||
<value>The property {0}.{1} could not be found.</value>
|
||||
</data>
|
||||
|
|
@ -327,21 +306,9 @@
|
|||
<data name="ModelBinding_MissingBindRequiredMember" xml:space="preserve">
|
||||
<value>A value for the '{0}' property was not provided.</value>
|
||||
</data>
|
||||
<data name="ModelBinding_ValueRequired" xml:space="preserve">
|
||||
<value>A value is required.</value>
|
||||
</data>
|
||||
<data name="MultipleBodyParametersOrPropertiesAreNotAllowed" xml:space="preserve">
|
||||
<value>More than one parameter and/or property is bound to the HTTP request's content.</value>
|
||||
</data>
|
||||
<data name="PropertyBindingPredicateProvider_WrongType" xml:space="preserve">
|
||||
<value>The type '{0}' does not implement the interface '{1}'.</value>
|
||||
</data>
|
||||
<data name="ValidatableObjectAdapter_IncompatibleType" xml:space="preserve">
|
||||
<value>The model object inside the metadata claimed to be compatible with '{0}', but was actually '{1}'.</value>
|
||||
</data>
|
||||
<data name="ValueProviderResult_CannotConvertEnum" xml:space="preserve">
|
||||
<value>Cannot convert value '{0}' to enum type '{1}'.</value>
|
||||
</data>
|
||||
<data name="ValueProviderResult_NoConverterExists" xml:space="preserve">
|
||||
<value>The parameter conversion from type '{0}' to type '{1}' failed because no type converter can convert between these types.</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -457,6 +457,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
|
||||
public override IModelBindingMessageProvider ModelBindingMessageProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override string NullDisplayText
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -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<IModelBinder>();
|
||||
innerBinder
|
||||
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||
|
|
@ -161,7 +163,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
// Assert
|
||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||
|
||||
Assert.Equal(default(KeyValuePair<string, string>), Assert.IsType<KeyValuePair<string, string>>(result.Model));
|
||||
var model = Assert.IsType<KeyValuePair<string, string>>(result.Model);
|
||||
Assert.Equal(default(KeyValuePair<string, string>), model);
|
||||
Assert.Equal("modelName", result.Key);
|
||||
Assert.True(result.IsModelSet);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<string, int>)
|
||||
};
|
||||
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<string, int>))
|
||||
.BindingDetails((System.Action<ModelBinding.Metadata.BindingMetadata>)(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<string, int>)
|
||||
};
|
||||
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<string, int>)
|
||||
};
|
||||
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<string, int>))
|
||||
.BindingDetails((System.Action<ModelBinding.Metadata.BindingMetadata>)(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<string, int>)
|
||||
};
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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<ModelBinding.Metadata.BindingMetadata>)(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<Order10>(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; }
|
||||
|
|
|
|||
|
|
@ -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<ModelBinding.Metadata.BindingMetadata>)(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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue