Include parameter type's attributes in ModelMetadata
- #7595
- #7595 relates to #7350 but does not have the same root cause
- did _not_ revert the src changes in #7350 fix (d995b0418a)
- make non-`[Obsolete]` `ModelAttributes` constructor overload `internal`
- should generally use `static` methods and not any constructor
- change some unit tests to use `[Obsolete]` constructor overloads (with suppressions)
- fix test `ParameterBinderExtensions` to use current `ParameterBinder.BindModelAsync(...)` overload
- found some tests updated `IModelMetadataProvider`, `MvcOptions`, etc. instances but didn't register them in DI
- extend `ModelBindingTestHelper` and `ModelBindingTestContext`
- reorder some tests to use correct `MvcOptions` and `IModelMetadataProvider` everywhere
- fixes above issues
nits:
- take a few VS suggestions
- remove an old comment indended only for PR "Reviewers:"
This commit is contained in:
parent
864a4e39c3
commit
e0e96ce53b
|
|
@ -5,8 +5,6 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||
{
|
||||
|
|
@ -56,7 +54,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
/// If this instance represents a parameter, the set of attributes for that parameter.
|
||||
/// Otherwise, <c>null</c>.
|
||||
/// </param>
|
||||
public ModelAttributes(IEnumerable<object> typeAttributes, IEnumerable<object> propertyAttributes, IEnumerable<object> parameterAttributes)
|
||||
internal ModelAttributes(
|
||||
IEnumerable<object> typeAttributes,
|
||||
IEnumerable<object> propertyAttributes,
|
||||
IEnumerable<object> parameterAttributes)
|
||||
{
|
||||
if (propertyAttributes != null)
|
||||
{
|
||||
|
|
@ -73,7 +74,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
else if (parameterAttributes != null)
|
||||
{
|
||||
// Represents a parameter
|
||||
Attributes = ParameterAttributes = parameterAttributes.ToArray();
|
||||
if (typeAttributes == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(typeAttributes));
|
||||
}
|
||||
|
||||
ParameterAttributes = parameterAttributes.ToArray();
|
||||
TypeAttributes = typeAttributes.ToArray();
|
||||
Attributes = ParameterAttributes.Concat(TypeAttributes).ToArray();
|
||||
}
|
||||
else if (typeAttributes != null)
|
||||
{
|
||||
|
|
@ -89,7 +97,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
|
||||
/// <summary>
|
||||
/// Gets the set of all attributes. If this instance represents the attributes for a property, the attributes
|
||||
/// on the property definition are before those on the property's <see cref="Type"/>.
|
||||
/// on the property definition are before those on the property's <see cref="Type"/>. If this instance
|
||||
/// represents the attributes for a parameter, the attributes on the parameter definition are before those on
|
||||
/// the parameter's <see cref="Type"/>.
|
||||
/// </summary>
|
||||
public IReadOnlyList<object> Attributes { get; }
|
||||
|
||||
|
|
@ -106,10 +116,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
public IReadOnlyList<object> ParameterAttributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the set of attributes on the <see cref="Type"/>. If this instance represents a property,
|
||||
/// then <see cref="TypeAttributes"/> contains attributes retrieved from
|
||||
/// <see cref="PropertyInfo.PropertyType"/>. If this instance represents a parameter, then
|
||||
/// the value is <c>null</c>.
|
||||
/// Gets the set of attributes on the <see cref="Type"/>. If this instance represents a property, then
|
||||
/// <see cref="TypeAttributes"/> contains attributes retrieved from <see cref="PropertyInfo.PropertyType"/>.
|
||||
/// If this instance represents a parameter, then contains attributes retrieved from
|
||||
/// <see cref="ParameterInfo.ParameterType"/>.
|
||||
/// </summary>
|
||||
public IReadOnlyList<object> TypeAttributes { get; }
|
||||
|
||||
|
|
@ -120,7 +130,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
/// </param>
|
||||
/// <param name="property">A <see cref="PropertyInfo"/> for which attributes need to be resolved.
|
||||
/// </param>
|
||||
/// <returns>A <see cref="ModelAttributes"/> instance with the attributes of the property.</returns>
|
||||
/// <returns>
|
||||
/// A <see cref="ModelAttributes"/> instance with the attributes of the property and its <see cref="Type"/>.
|
||||
/// </returns>
|
||||
public static ModelAttributes GetAttributesForProperty(Type type, PropertyInfo property)
|
||||
{
|
||||
if (type == null)
|
||||
|
|
@ -146,7 +158,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
|
||||
return new ModelAttributes(typeAttributes, propertyAttributes, null);
|
||||
return new ModelAttributes(typeAttributes, propertyAttributes, parameterAttributes: null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -170,18 +182,27 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
attributes = attributes.Concat(metadataType.GetTypeInfo().GetCustomAttributes());
|
||||
}
|
||||
|
||||
return new ModelAttributes(attributes, null, null);
|
||||
return new ModelAttributes(attributes, propertyAttributes: null, parameterAttributes: null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attributes for the given <paramref name="parameterInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="parameterInfo">The <see cref="ParameterInfo"/> for which attributes need to be resolved.
|
||||
/// <param name="parameterInfo">
|
||||
/// The <see cref="ParameterInfo"/> for which attributes need to be resolved.
|
||||
/// </param>
|
||||
/// <returns>A <see cref="ModelAttributes"/> instance with the attributes of the <see cref="ParameterInfo"/>.</returns>
|
||||
/// <returns>
|
||||
/// A <see cref="ModelAttributes"/> instance with the attributes of the parameter and its <see cref="Type"/>.
|
||||
/// </returns>
|
||||
public static ModelAttributes GetAttributesForParameter(ParameterInfo parameterInfo)
|
||||
{
|
||||
return new ModelAttributes(null, null, parameterInfo.GetCustomAttributes());
|
||||
// Prior versions called IModelMetadataProvider.GetMetadataForType(...) and therefore
|
||||
// GetAttributesForType(...) for parameters. Maintain that set of attributes (including those from an
|
||||
// ModelMetadataTypeAttribute reference) for back-compatibility.
|
||||
var typeAttributes = GetAttributesForType(parameterInfo.ParameterType).TypeAttributes;
|
||||
var parameterAttributes = parameterInfo.GetCustomAttributes();
|
||||
|
||||
return new ModelAttributes(typeAttributes, propertyAttributes: null, parameterAttributes);
|
||||
}
|
||||
|
||||
private static Type GetMetadataType(Type type)
|
||||
|
|
|
|||
|
|
@ -176,7 +176,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
{
|
||||
// Arrange
|
||||
var attributes = new object[] { new ModelBinderAttribute(typeof(object)), new ControllerAttribute(), new BindNeverAttribute(), };
|
||||
var modelAttributes = new ModelAttributes(Enumerable.Empty<object>(), null, null);
|
||||
var propertyFilterProvider = Mock.Of<IPropertyFilterProvider>();
|
||||
var modelType = typeof(Guid);
|
||||
var provider = new TestModelMetadataProvider();
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
var context = new BindingMetadataProviderContext(
|
||||
ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo),
|
||||
new ModelAttributes(null, null, parameterAttributes));
|
||||
new ModelAttributes(Array.Empty<object>(), null, parameterAttributes));
|
||||
|
||||
var provider = new DefaultBindingMetadataProvider();
|
||||
|
||||
|
|
@ -302,7 +302,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
var context = new BindingMetadataProviderContext(
|
||||
ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo),
|
||||
new ModelAttributes(null, null, parameterAttributes));
|
||||
new ModelAttributes(Array.Empty<object>(), null, parameterAttributes));
|
||||
|
||||
var provider = new DefaultBindingMetadataProvider();
|
||||
|
||||
|
|
@ -325,7 +325,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
var context = new BindingMetadataProviderContext(
|
||||
ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo),
|
||||
new ModelAttributes(null, null, parameterAttributes));
|
||||
new ModelAttributes(Array.Empty<object>(), null, parameterAttributes));
|
||||
|
||||
var provider = new DefaultBindingMetadataProvider();
|
||||
|
||||
|
|
@ -348,7 +348,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
var context = new BindingMetadataProviderContext(
|
||||
ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo),
|
||||
new ModelAttributes(null, null, parameterAttributes));
|
||||
new ModelAttributes(Array.Empty<object>(), null, parameterAttributes));
|
||||
|
||||
var provider = new DefaultBindingMetadataProvider();
|
||||
|
||||
|
|
@ -371,7 +371,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
var context = new BindingMetadataProviderContext(
|
||||
ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo),
|
||||
new ModelAttributes(null, null, parameterAttributes));
|
||||
new ModelAttributes(Array.Empty<object>(), null, parameterAttributes));
|
||||
|
||||
var provider = new DefaultBindingMetadataProvider();
|
||||
|
||||
|
|
@ -395,7 +395,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
var context = new BindingMetadataProviderContext(
|
||||
ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo),
|
||||
new ModelAttributes(null, null, parameterAttributes));
|
||||
new ModelAttributes(Array.Empty<object>(), null, parameterAttributes));
|
||||
|
||||
var provider = new DefaultBindingMetadataProvider();
|
||||
|
||||
|
|
|
|||
|
|
@ -17,12 +17,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Test
|
|||
{
|
||||
private static readonly Dictionary<string, StringValues> _backingStore = new Dictionary<string, StringValues>
|
||||
{
|
||||
// Reviewers: A fair number of cases in the following dataset seem impossible for jQuery.ajax() to send
|
||||
// given how $.ajax() and $.param() are defined i.e. $.param() won't add a bare name after ']'. Also, the
|
||||
// names we generate for <input/> elements are always valid. Should we remove these weird / invalid cases
|
||||
// e.g. normalizing "property[]Value"? (That normalizes to "propertyValue".) See also
|
||||
// https://github.com/jquery/jquery/blob/1ea092a54b00aa4d902f4e22ada3854d195d4a18/src/serialize.js#L13-L92
|
||||
// The alternative is to handle "[]name" and "[index]name" cases while normalizing keys.
|
||||
{ "[]", new[] { "found" } },
|
||||
{ "[]property1", new[] { "found" } },
|
||||
{ "property2[]", new[] { "found" } },
|
||||
|
|
|
|||
|
|
@ -212,7 +212,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
|
|||
|
||||
// Assert
|
||||
var defaultMetadata = Assert.IsType<DefaultModelMetadata>(metadata);
|
||||
Assert.Empty(defaultMetadata.Attributes.Attributes);
|
||||
|
||||
// Not exactly "no attributes" due to SerializableAttribute on object.
|
||||
Assert.IsType<SerializableAttribute>(Assert.Single(defaultMetadata.Attributes.Attributes));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -229,11 +231,19 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
|
|||
|
||||
// Assert
|
||||
var defaultMetadata = Assert.IsType<DefaultModelMetadata>(metadata);
|
||||
Assert.Equal(2, defaultMetadata.Attributes.Attributes.Count);
|
||||
var attribute1 = Assert.IsType<ModelAttribute>(defaultMetadata.Attributes.Attributes[0]);
|
||||
Assert.Equal("ParamAttrib1", attribute1.Value);
|
||||
var attribute2 = Assert.IsType<ModelAttribute>(defaultMetadata.Attributes.Attributes[1]);
|
||||
Assert.Equal("ParamAttrib2", attribute2.Value);
|
||||
Assert.Collection(
|
||||
// Take(2) to ignore SerializableAttribute on object.
|
||||
defaultMetadata.Attributes.Attributes.Take(2),
|
||||
attribute =>
|
||||
{
|
||||
var modelAttribute = Assert.IsType<ModelAttribute>(attribute);
|
||||
Assert.Equal("ParamAttrib1", modelAttribute.Value);
|
||||
},
|
||||
attribute =>
|
||||
{
|
||||
var modelAttribute = Assert.IsType<ModelAttribute>(attribute);
|
||||
Assert.Equal("ParamAttrib2", modelAttribute.Value);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||
|
|
@ -188,10 +189,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
.GetParameters()[0]);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(attributes.Attributes);
|
||||
// Not exactly "no attributes" due to SerializableAttribute on object.
|
||||
Assert.IsType<SerializableAttribute>(Assert.Single(attributes.Attributes));
|
||||
Assert.Empty(attributes.ParameterAttributes);
|
||||
Assert.Null(attributes.TypeAttributes);
|
||||
Assert.Null(attributes.PropertyAttributes);
|
||||
Assert.Equal(attributes.Attributes, attributes.TypeAttributes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -204,12 +206,40 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
.GetParameters()[1]);
|
||||
|
||||
// Assert
|
||||
Assert.IsType<RequiredAttribute>(attributes.Attributes[0]);
|
||||
Assert.IsType<RangeAttribute>(attributes.Attributes[1]);
|
||||
Assert.IsType<RequiredAttribute>(attributes.ParameterAttributes[0]);
|
||||
Assert.IsType<RangeAttribute>(attributes.ParameterAttributes[1]);
|
||||
Assert.Null(attributes.TypeAttributes);
|
||||
Assert.Collection(
|
||||
// Take(2) to ignore ComVisibleAttribute, SerializableAttribute, ... on int.
|
||||
attributes.Attributes.Take(2),
|
||||
attribute => Assert.IsType<RequiredAttribute>(attribute),
|
||||
attribute => Assert.IsType<RangeAttribute>(attribute));
|
||||
Assert.Collection(
|
||||
attributes.ParameterAttributes,
|
||||
attribute => Assert.IsType<RequiredAttribute>(attribute),
|
||||
attribute => Assert.IsType<RangeAttribute>(attribute));
|
||||
Assert.Null(attributes.PropertyAttributes);
|
||||
Assert.Collection(
|
||||
// Take(1) because the attribute or attributes after SerializableAttribute are framework-specific.
|
||||
attributes.TypeAttributes.Take(1),
|
||||
attribute => Assert.IsType<SerializableAttribute>(attribute));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributesForParameter_IncludesTypeAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var parameters = typeof(MethodWithParamAttributesType)
|
||||
.GetMethod(nameof(MethodWithParamAttributesType.Method))
|
||||
.GetParameters();
|
||||
|
||||
// Act
|
||||
var attributes = ModelAttributes.GetAttributesForParameter(parameters[2]);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(attributes.Attributes,
|
||||
attribute => Assert.IsType<BindRequiredAttribute>(attribute),
|
||||
attribute => Assert.IsType<ClassValidator>(attribute));
|
||||
Assert.IsType<BindRequiredAttribute>(Assert.Single(attributes.ParameterAttributes));
|
||||
Assert.Null(attributes.PropertyAttributes);
|
||||
Assert.IsType<ClassValidator>(Assert.Single(attributes.TypeAttributes));
|
||||
}
|
||||
|
||||
[ClassValidator]
|
||||
|
|
@ -272,7 +302,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
|
||||
private class ClassValidator : ValidationAttribute
|
||||
{
|
||||
public override Boolean IsValid(Object value)
|
||||
public override bool IsValid(object value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
@ -305,7 +335,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
private class MethodWithParamAttributesType
|
||||
{
|
||||
[IrrelevantAttribute] // We verify this is ignored
|
||||
public void Method(object noAttribs, [Required, Range(1, 100)] int validationAttribs)
|
||||
public void Method(
|
||||
object noAttributes,
|
||||
[Required, Range(1, 100)] int validationAttributes,
|
||||
[BindRequired] BaseModel mergedAttributes)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -314,4 +347,4 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
stringLocalizerFactory: null);
|
||||
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(new object[] { attribute }, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(new object[] { attribute }));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -113,7 +113,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new[] { dataType, };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { dataType, displayFormat, };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { editable };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new BindingMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateBindingMetadata(context);
|
||||
|
|
@ -180,7 +180,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { editable };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new BindingMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateBindingMetadata(context);
|
||||
|
|
@ -207,7 +207,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { display, displayName };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -234,7 +234,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { display, displayName };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -261,7 +261,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { display, displayName };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -298,7 +298,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { displayName };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -335,7 +335,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { display };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -367,7 +367,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { display };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -404,7 +404,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { display };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -441,7 +441,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { display };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -472,7 +472,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { display };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -509,7 +509,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { display };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -540,7 +540,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { display };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(string));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -588,7 +588,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { display };
|
||||
var key = ModelMetadataIdentity.ForType(typeof(DataAnnotationsMetadataProviderTest));
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -635,7 +635,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var key = ModelMetadataIdentity.ForType(type);
|
||||
var attributes = new object[0];
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -671,7 +671,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var key = ModelMetadataIdentity.ForType(type);
|
||||
var attributes = new object[0];
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -805,7 +805,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var key = ModelMetadataIdentity.ForType(type);
|
||||
var attributes = new object[0];
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -833,7 +833,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var attributes = new object[0];
|
||||
|
||||
var key = ModelMetadataIdentity.ForType(type);
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
var stringLocalizer = new Mock<IStringLocalizer>(MockBehavior.Strict);
|
||||
stringLocalizer
|
||||
|
|
@ -985,7 +985,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var key = ModelMetadataIdentity.ForType(type);
|
||||
var attributes = new object[0];
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -1015,7 +1015,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var key = ModelMetadataIdentity.ForType(typeof(EnumWithDisplayOrder));
|
||||
var attributes = new object[0];
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
|
@ -1119,7 +1119,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { required };
|
||||
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
|
||||
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
|
||||
var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateValidationMetadata(context);
|
||||
|
|
@ -1141,7 +1141,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { };
|
||||
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
|
||||
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
|
||||
var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
|
||||
context.ValidationMetadata.IsRequired = initialValue;
|
||||
|
||||
// Act
|
||||
|
|
@ -1164,7 +1164,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { new RequiredAttribute() };
|
||||
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
|
||||
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
|
||||
var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
|
||||
context.BindingMetadata.IsBindingRequired = initialValue;
|
||||
|
||||
// Act
|
||||
|
|
@ -1187,7 +1187,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
var attributes = new Attribute[] { };
|
||||
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
|
||||
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
|
||||
var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
|
||||
context.BindingMetadata.IsReadOnly = initialValue;
|
||||
|
||||
// Act
|
||||
|
|
@ -1208,7 +1208,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var attribute = new TestValidationAttribute();
|
||||
var attributes = new Attribute[] { attribute };
|
||||
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
|
||||
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
|
||||
var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateValidationMetadata(context);
|
||||
|
|
@ -1229,7 +1229,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var attribute = new TestValidationAttribute();
|
||||
var attributes = new Attribute[] { attribute };
|
||||
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
|
||||
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
|
||||
var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
|
||||
context.ValidationMetadata.ValidatorMetadata.Add(attribute);
|
||||
|
||||
// Act
|
||||
|
|
@ -1248,7 +1248,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var key = ModelMetadataIdentity.ForType(typeof(EnumWithLocalizedDisplayNames));
|
||||
var attributes = new object[0];
|
||||
|
||||
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null));
|
||||
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
|
||||
provider.CreateDisplayMetadata(context);
|
||||
|
||||
return context.DisplayMetadata.EnumGroupedDisplayNamesAndValues;
|
||||
|
|
@ -1274,6 +1274,26 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
useStringLocalizer ? stringLocalizerFactory.Object : null);
|
||||
}
|
||||
|
||||
private ModelAttributes GetModelAttributes(IEnumerable<object> typeAttributes)
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var modelAttributes = new ModelAttributes(typeAttributes);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
return modelAttributes;
|
||||
}
|
||||
|
||||
private ModelAttributes GetModelAttributes(
|
||||
IEnumerable<object> typeAttributes,
|
||||
IEnumerable<object> propertyAttributes)
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var modelAttributes = new ModelAttributes(propertyAttributes, typeAttributes);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
return modelAttributes;
|
||||
}
|
||||
|
||||
private class KVPEnumGroupAndNameComparer : IEqualityComparer<KeyValuePair<EnumGroupAndName, string>>
|
||||
{
|
||||
public static readonly IEqualityComparer<KeyValuePair<EnumGroupAndName, string>> Instance = new KVPEnumGroupAndNameComparer();
|
||||
|
|
|
|||
|
|
@ -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 System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
|
||||
|
|
@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
typeof(string),
|
||||
nameof(ClassWithDataMemberIsRequiredTrue.StringProperty),
|
||||
typeof(ClassWithDataMemberIsRequiredTrue));
|
||||
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
|
||||
var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
|
||||
|
||||
// Act
|
||||
provider.CreateBindingMetadata(context);
|
||||
|
|
@ -51,7 +52,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
typeof(string),
|
||||
nameof(ClassWithDataMemberIsRequiredFalse.StringProperty),
|
||||
typeof(ClassWithDataMemberIsRequiredFalse));
|
||||
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
|
||||
var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
|
||||
|
||||
context.BindingMetadata.IsBindingRequired = initialValue;
|
||||
|
||||
|
|
@ -76,7 +77,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
};
|
||||
|
||||
var key = ModelMetadataIdentity.ForType(typeof(ClassWithDataMemberIsRequiredTrue));
|
||||
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
|
||||
var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
|
||||
|
||||
context.BindingMetadata.IsBindingRequired = initialValue;
|
||||
|
||||
|
|
@ -99,7 +100,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
typeof(string),
|
||||
nameof(ClassWithoutAttributes.StringProperty),
|
||||
typeof(ClassWithoutAttributes));
|
||||
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], new object[0], null));
|
||||
var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], new object[0]));
|
||||
|
||||
context.BindingMetadata.IsBindingRequired = initialValue;
|
||||
|
||||
|
|
@ -127,7 +128,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
typeof(string),
|
||||
nameof(ClassWithDataMemberIsRequiredTrueWithoutDataContract.StringProperty),
|
||||
typeof(ClassWithDataMemberIsRequiredTrueWithoutDataContract));
|
||||
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null));
|
||||
var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
|
||||
|
||||
context.BindingMetadata.IsBindingRequired = initialValue;
|
||||
|
||||
|
|
@ -138,6 +139,17 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
Assert.Equal(initialValue, context.BindingMetadata.IsBindingRequired);
|
||||
}
|
||||
|
||||
private ModelAttributes GetModelAttributes(
|
||||
IEnumerable<object> typeAttributes,
|
||||
IEnumerable<object> propertyAttributes)
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var modelAttributes = new ModelAttributes(propertyAttributes, typeAttributes);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
return modelAttributes;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
private class ClassWithDataMemberIsRequiredTrue
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1067,7 +1067,9 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
return new DefaultMetadataDetails(
|
||||
key,
|
||||
new ModelAttributes(_attributes.Concat(entry.ModelAttributes.TypeAttributes).ToArray(), null, null));
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
new ModelAttributes(_attributes.Concat(entry.ModelAttributes.TypeAttributes).ToArray()));
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
||||
return entry;
|
||||
|
|
@ -1080,10 +1082,14 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
return new DefaultMetadataDetails(
|
||||
e.Key,
|
||||
new ModelAttributes(e.ModelAttributes.TypeAttributes, _attributes.Concat(e.ModelAttributes.PropertyAttributes), null));
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
new ModelAttributes(
|
||||
_attributes.Concat(e.ModelAttributes.PropertyAttributes),
|
||||
e.ModelAttributes.TypeAttributes));
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
|
@ -469,14 +468,17 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
public async Task ActionParameter_CustomModelBinder_CanCreateModels_ForParameterlessConstructorTypes()
|
||||
{
|
||||
// Arrange
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(binderProvider: new CustomComplexTypeModelBinderProvider());
|
||||
var parameter = new ParameterDescriptor()
|
||||
var testContext = ModelBindingTestHelper.GetTestContext();
|
||||
var modelState = testContext.ModelState;
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(
|
||||
testContext.MvcOptions,
|
||||
new CustomComplexTypeModelBinderProvider());
|
||||
|
||||
var parameter = new ParameterDescriptor
|
||||
{
|
||||
Name = "prefix",
|
||||
ParameterType = typeof(ClassWithNoDefaultConstructor)
|
||||
};
|
||||
var testContext = ModelBindingTestHelper.GetTestContext();
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
|
@ -850,7 +852,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
foreach (T t in _original)
|
||||
foreach (var t in _original)
|
||||
{
|
||||
yield return t;
|
||||
}
|
||||
|
|
@ -891,4 +893,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
|
|
@ -148,13 +149,63 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
// type. This should behave identically to such an attribute on an action parameter. (Tests such as
|
||||
// BindParameter_WithData_WithPrefix_GetsBound cover associating [ModelBinder] with an action parameter.)
|
||||
//
|
||||
// This is a regression test for aspnet/Mvc#4652
|
||||
// This is a regression test for aspnet/Mvc#4652 and aspnet/Mvc#7595
|
||||
[Theory]
|
||||
[MemberData(nameof(NullAndEmptyBindingInfo))]
|
||||
public async Task BinderTypeOnParameterType_WithData_EmptyPrefix_GetsBound(BindingInfo bindingInfo)
|
||||
{
|
||||
// Arrange
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder();
|
||||
var parameters = typeof(TestController).GetMethod(nameof(TestController.Action)).GetParameters();
|
||||
var parameter = new ControllerParameterDescriptor
|
||||
{
|
||||
Name = "Parameter1",
|
||||
BindingInfo = bindingInfo,
|
||||
ParameterInfo = parameters[0],
|
||||
ParameterType = typeof(Address),
|
||||
};
|
||||
|
||||
var testContext = ModelBindingTestHelper.GetTestContext();
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
||||
// Assert
|
||||
// ModelBindingResult
|
||||
Assert.True(modelBindingResult.IsModelSet);
|
||||
|
||||
// Model
|
||||
var address = Assert.IsType<Address>(modelBindingResult.Model);
|
||||
Assert.Equal("SomeStreet", address.Street);
|
||||
|
||||
// ModelState
|
||||
Assert.True(modelState.IsValid);
|
||||
var kvp = Assert.Single(modelState);
|
||||
Assert.Equal("Street", kvp.Key);
|
||||
var entry = kvp.Value;
|
||||
Assert.NotNull(entry);
|
||||
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
||||
Assert.NotNull(entry.RawValue); // Value is set by test model binder, no need to validate it.
|
||||
}
|
||||
|
||||
// Make sure the metadata is honored when a [ModelBinder] attribute is associated with an action parameter's
|
||||
// type. This should behave identically to such an attribute on an action parameter. (Tests such as
|
||||
// BindParameter_WithData_WithPrefix_GetsBound cover associating [ModelBinder] with an action parameter.)
|
||||
//
|
||||
// This is a regression test for aspnet/Mvc#4652
|
||||
[Theory]
|
||||
[MemberData(nameof(NullAndEmptyBindingInfo))]
|
||||
public async Task BinderTypeOnParameterType_WithDataEmptyPrefixAndVersion20_GetsBound(
|
||||
BindingInfo bindingInfo)
|
||||
{
|
||||
// Arrange
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(
|
||||
// ParameterBinder will use ModelMetadata for typeof(Address), not Parameter1's ParameterInfo.
|
||||
updateOptions: options => options.AllowValidatingTopLevelNodes = false);
|
||||
|
||||
var modelState = testContext.ModelState;
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
var parameter = new ParameterDescriptor
|
||||
{
|
||||
Name = "Parameter1",
|
||||
|
|
@ -162,9 +213,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
ParameterType = typeof(Address),
|
||||
};
|
||||
|
||||
var testContext = ModelBindingTestHelper.GetTestContext();
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
||||
|
|
@ -419,5 +467,12 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestController
|
||||
{
|
||||
public void Action(Address address)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -377,15 +377,11 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(string.Empty));
|
||||
request.ContentType = "application/json";
|
||||
});
|
||||
testContext.MvcOptions.AllowEmptyInputInBodyModelBinding = true;
|
||||
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
var addressRequired = ValidationAttributeUtil.GetRequiredErrorMessage("Address");
|
||||
|
||||
var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>();
|
||||
optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = true;
|
||||
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value);
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
|
@ -422,14 +418,10 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(string.Empty));
|
||||
request.ContentType = "application/json";
|
||||
});
|
||||
testContext.MvcOptions.AllowEmptyInputInBodyModelBinding = true;
|
||||
|
||||
var httpContext = testContext.HttpContext;
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>();
|
||||
optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = true;
|
||||
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value);
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
|
@ -626,10 +618,8 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
},
|
||||
options => options.AllowEmptyInputInBodyModelBinding = allowEmptyInputInBodyModelBindingSetting);
|
||||
|
||||
var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>();
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value);
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
|
@ -793,7 +783,16 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
.ForProperty<Person6>(nameof(Person6.Address))
|
||||
.BindingDetails(binding => binding.BindingSource = BindingSource.Body);
|
||||
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider);
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(
|
||||
request =>
|
||||
{
|
||||
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(inputText));
|
||||
request.ContentType = "application/json";
|
||||
},
|
||||
metadataProvider: metadataProvider);
|
||||
|
||||
var modelState = testContext.ModelState;
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
var parameter = new ParameterDescriptor
|
||||
{
|
||||
Name = "parameter-name",
|
||||
|
|
@ -801,15 +800,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
ParameterType = typeof(Person6),
|
||||
};
|
||||
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(
|
||||
request =>
|
||||
{
|
||||
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(inputText));
|
||||
request.ContentType = "application/json";
|
||||
});
|
||||
testContext.MetadataProvider = metadataProvider;
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
||||
|
|
@ -839,7 +829,16 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
.ForType<Address6>()
|
||||
.BindingDetails(binding => binding.BindingSource = BindingSource.Body);
|
||||
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider);
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(
|
||||
request =>
|
||||
{
|
||||
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(inputText));
|
||||
request.ContentType = "application/json";
|
||||
},
|
||||
metadataProvider: metadataProvider);
|
||||
|
||||
var modelState = testContext.ModelState;
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
var parameter = new ParameterDescriptor
|
||||
{
|
||||
Name = "parameter-name",
|
||||
|
|
@ -847,15 +846,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
ParameterType = typeof(Address6),
|
||||
};
|
||||
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(
|
||||
request =>
|
||||
{
|
||||
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(inputText));
|
||||
request.ContentType = "application/json";
|
||||
});
|
||||
testContext.MetadataProvider = metadataProvider;
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
||||
|
|
@ -890,4 +880,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ using Microsoft.AspNetCore.Http.Internal;
|
|||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -148,13 +147,11 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
request.ContentType = "application/json";
|
||||
});
|
||||
|
||||
var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>();
|
||||
optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = true;
|
||||
testContext.MvcOptions.AllowEmptyInputInBodyModelBinding = true;
|
||||
|
||||
var modelState = testContext.ModelState;
|
||||
var valueProvider = await CompositeValueProvider.CreateAsync(testContext);
|
||||
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value);
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(testContext, valueProvider, parameter);
|
||||
|
|
@ -1607,7 +1604,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
|
||||
var model = Assert.IsType<Order8>(modelBindingResult.Model);
|
||||
Assert.Equal("bill", model.Name);
|
||||
Assert.Equal(default(KeyValuePair<string, int>), model.ProductId);
|
||||
Assert.Equal(default, model.ProductId);
|
||||
|
||||
Assert.Single(modelState);
|
||||
Assert.Equal(0, modelState.ErrorCount);
|
||||
|
|
@ -1646,7 +1643,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
|
||||
var model = Assert.IsType<Order8>(modelBindingResult.Model);
|
||||
Assert.Null(model.Name);
|
||||
Assert.Equal(default(KeyValuePair<string, int>), model.ProductId);
|
||||
Assert.Equal(default, model.ProductId);
|
||||
|
||||
Assert.Empty(modelState);
|
||||
Assert.Equal(0, modelState.ErrorCount);
|
||||
|
|
@ -2728,4 +2725,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,12 +67,12 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
{
|
||||
// Arrange
|
||||
var options = new MvcOptions();
|
||||
var setup = new MvcCoreMvcOptionsSetup(new TestHttpRequestStreamReaderFactory());
|
||||
var modelBinderProvider = new TypeModelBinderProvider();
|
||||
|
||||
// Adding a custom model binder for Type to ensure it doesn't get called
|
||||
options.ModelBinderProviders.Insert(0, modelBinderProvider);
|
||||
|
||||
var setup = new MvcCoreMvcOptionsSetup(new TestHttpRequestStreamReaderFactory());
|
||||
setup.Configure(options);
|
||||
|
||||
// Remove the ExcludeBindingMetadataProvider
|
||||
|
|
@ -84,7 +84,20 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
}
|
||||
}
|
||||
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(options);
|
||||
var metadataProvider = TestModelMetadataProvider.CreateProvider(options.ModelMetadataDetailsProviders);
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(
|
||||
request =>
|
||||
{
|
||||
request.Form = new FormCollection(new Dictionary<string, StringValues>
|
||||
{
|
||||
{ "name", new[] { "Fred" } },
|
||||
{ "type", new[] { "SomeType" } },
|
||||
});
|
||||
},
|
||||
metadataProvider: metadataProvider,
|
||||
mvcOptions: options);
|
||||
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
var parameter = new ParameterDescriptor()
|
||||
{
|
||||
Name = "Parameter1",
|
||||
|
|
@ -92,15 +105,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
ParameterType = typeof(TypesBundle),
|
||||
};
|
||||
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(request =>
|
||||
{
|
||||
request.Form = new FormCollection(new Dictionary<string, StringValues>
|
||||
{
|
||||
{ "name", new[] { "Fred" } },
|
||||
{ "type", new[] { "SomeType" } },
|
||||
});
|
||||
});
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
||||
|
|
@ -145,4 +149,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,18 +196,20 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
public async Task GenericModelBinder_BindsCollection_ElementTypeUsesGreedyModelBinder_WithPrefix_Success()
|
||||
{
|
||||
// Arrange
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(binderProvider: new AddressBinderProvider());
|
||||
var parameter = new ParameterDescriptor()
|
||||
{
|
||||
Name = "parameter",
|
||||
ParameterType = typeof(Address[])
|
||||
};
|
||||
|
||||
// Need to have a key here so that the GenericModelBinder will recurse to bind elements.
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(
|
||||
request => request.QueryString = new QueryString("?parameter.index=0"));
|
||||
|
||||
var modelState = testContext.ModelState;
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(
|
||||
testContext.MvcOptions,
|
||||
new AddressBinderProvider());
|
||||
|
||||
var parameter = new ParameterDescriptor()
|
||||
{
|
||||
Name = "parameter",
|
||||
ParameterType = typeof(Address[])
|
||||
};
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
|
@ -638,4 +640,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
Assert.True(modelState.IsValid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,17 +104,20 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
() => $"Hurts when nothing is provided.");
|
||||
}));
|
||||
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider);
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(
|
||||
request =>
|
||||
{
|
||||
request.QueryString = new QueryString("?parameter.Value=10");
|
||||
},
|
||||
metadataProvider: metadataProvider);
|
||||
|
||||
var modelState = testContext.ModelState;
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
var parameter = new ParameterDescriptor
|
||||
{
|
||||
Name = "parameter",
|
||||
ParameterType = typeof(KeyValuePair<string, int>)
|
||||
};
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(request =>
|
||||
{
|
||||
request.QueryString = new QueryString("?parameter.Value=10");
|
||||
});
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
|
@ -188,18 +191,20 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
() => $"Hurts when nothing is provided.");
|
||||
}));
|
||||
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider);
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(
|
||||
request =>
|
||||
{
|
||||
request.QueryString = new QueryString("?parameter.Key=10");
|
||||
},
|
||||
metadataProvider: metadataProvider);
|
||||
|
||||
var modelState = testContext.ModelState;
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
var parameter = new ParameterDescriptor
|
||||
{
|
||||
Name = "parameter",
|
||||
ParameterType = typeof(KeyValuePair<string, int>)
|
||||
};
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(request =>
|
||||
{
|
||||
request.QueryString = new QueryString("?parameter.Key=10");
|
||||
});
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
|
@ -544,4 +549,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
Assert.Equal("value2", entry.RawValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
{
|
||||
public IModelMetadataProvider MetadataProvider { get; set; }
|
||||
|
||||
public MvcOptions MvcOptions { get; set; }
|
||||
|
||||
public T GetService<T>()
|
||||
{
|
||||
return (T)HttpContext.RequestServices.GetService(typeof(T));
|
||||
|
|
|
|||
|
|
@ -26,17 +26,21 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
public static ModelBindingTestContext GetTestContext(
|
||||
Action<HttpRequest> updateRequest = null,
|
||||
Action<MvcOptions> updateOptions = null,
|
||||
ControllerActionDescriptor actionDescriptor = null)
|
||||
ControllerActionDescriptor actionDescriptor = null,
|
||||
IModelMetadataProvider metadataProvider = null,
|
||||
MvcOptions mvcOptions = null)
|
||||
{
|
||||
var httpContext = GetHttpContext(updateRequest, updateOptions);
|
||||
var httpContext = GetHttpContext(metadataProvider, updateRequest, updateOptions, mvcOptions);
|
||||
var services = httpContext.RequestServices;
|
||||
metadataProvider = metadataProvider ?? services.GetRequiredService<IModelMetadataProvider>();
|
||||
var options = services.GetRequiredService<IOptions<MvcOptions>>();
|
||||
|
||||
var context = new ModelBindingTestContext()
|
||||
var context = new ModelBindingTestContext
|
||||
{
|
||||
ActionDescriptor = actionDescriptor ?? new ControllerActionDescriptor(),
|
||||
HttpContext = httpContext,
|
||||
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
|
||||
MetadataProvider = metadataProvider,
|
||||
MvcOptions = options.Value,
|
||||
RouteData = new RouteData(),
|
||||
ValueProviderFactories = new List<IValueProviderFactory>(options.Value.ValueProviderFactories),
|
||||
};
|
||||
|
|
@ -80,10 +84,8 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
IModelBinderProvider binderProvider = null,
|
||||
MvcOptions mvcOptions = null)
|
||||
{
|
||||
var services = GetServices();
|
||||
var options = mvcOptions != null
|
||||
? Options.Create(mvcOptions)
|
||||
: services.GetRequiredService<IOptions<MvcOptions>>();
|
||||
var services = GetServices(metadataProvider, mvcOptions: mvcOptions);
|
||||
var options = services.GetRequiredService<IOptions<MvcOptions>>();
|
||||
|
||||
if (binderProvider != null)
|
||||
{
|
||||
|
|
@ -102,15 +104,15 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
|
||||
public static IModelBinderFactory GetModelBinderFactory(
|
||||
IModelMetadataProvider metadataProvider,
|
||||
IOptions<MvcOptions> options = null)
|
||||
IServiceProvider services = null)
|
||||
{
|
||||
var services = GetServices();
|
||||
|
||||
if (options == null)
|
||||
if (services == null)
|
||||
{
|
||||
options = services.GetRequiredService<IOptions<MvcOptions>>();
|
||||
services = GetServices(metadataProvider);
|
||||
}
|
||||
|
||||
var options = services.GetRequiredService<IOptions<MvcOptions>>();
|
||||
|
||||
return new ModelBinderFactory(metadataProvider, options, services);
|
||||
}
|
||||
|
||||
|
|
@ -136,23 +138,52 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
}
|
||||
|
||||
private static HttpContext GetHttpContext(
|
||||
IModelMetadataProvider metadataProvider,
|
||||
Action<HttpRequest> updateRequest = null,
|
||||
Action<MvcOptions> updateOptions = null)
|
||||
Action<MvcOptions> updateOptions = null,
|
||||
MvcOptions mvcOptions = null)
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Features.Set<IHttpRequestLifetimeFeature>(new CancellableRequestLifetimeFeature());
|
||||
|
||||
updateRequest?.Invoke(httpContext.Request);
|
||||
|
||||
httpContext.RequestServices = GetServices(updateOptions);
|
||||
httpContext.RequestServices = GetServices(metadataProvider, updateOptions, mvcOptions);
|
||||
return httpContext;
|
||||
}
|
||||
|
||||
public static IServiceProvider GetServices(Action<MvcOptions> updateOptions = null)
|
||||
public static IServiceProvider GetServices(
|
||||
IModelMetadataProvider metadataProvider,
|
||||
Action<MvcOptions> updateOptions = null,
|
||||
MvcOptions mvcOptions = null)
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddAuthorization();
|
||||
serviceCollection.AddSingleton(new ApplicationPartManager());
|
||||
if (metadataProvider != null)
|
||||
{
|
||||
serviceCollection.AddSingleton(metadataProvider);
|
||||
}
|
||||
else if (updateOptions != null || mvcOptions != null)
|
||||
{
|
||||
serviceCollection.AddSingleton(services =>
|
||||
{
|
||||
var optionsAccessor = services.GetRequiredService<IOptions<MvcOptions>>();
|
||||
return TestModelMetadataProvider.CreateProvider(optionsAccessor.Value.ModelMetadataDetailsProviders);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceCollection.AddSingleton<IModelMetadataProvider>(services =>
|
||||
{
|
||||
return TestModelMetadataProvider.CreateDefaultProvider();
|
||||
});
|
||||
}
|
||||
|
||||
if (mvcOptions != null)
|
||||
{
|
||||
serviceCollection.AddSingleton(Options.Create(mvcOptions));
|
||||
}
|
||||
|
||||
serviceCollection
|
||||
.AddMvc()
|
||||
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
|
||||
|
|
|
|||
|
|
@ -1,23 +1,60 @@
|
|||
// 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.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||
{
|
||||
public static class ParameterBinderExtensions
|
||||
{
|
||||
public static async Task<ModelBindingResult> BindModelAsync(
|
||||
public static Task<ModelBindingResult> BindModelAsync(
|
||||
this ParameterBinder parameterBinder,
|
||||
ParameterDescriptor parameter,
|
||||
ParameterDescriptor parameter,
|
||||
ControllerContext context)
|
||||
{
|
||||
var valueProvider = await CompositeValueProvider.CreateAsync(context);
|
||||
|
||||
return await parameterBinder.BindModelAsync(context, valueProvider, parameter);
|
||||
var optionsAccessor = context.HttpContext.RequestServices.GetService<IOptions<MvcOptions>>();
|
||||
Assert.NotNull(optionsAccessor?.Value); // Guard
|
||||
var modelMetadataProvider = context.HttpContext.RequestServices.GetService<IModelMetadataProvider>();
|
||||
Assert.NotNull(modelMetadataProvider); // Guard
|
||||
|
||||
// Imitate a bit of ControllerBinderDelegateProvider and PageBinderFactory
|
||||
ParameterInfo parameterInfo;
|
||||
if (parameter is ControllerParameterDescriptor controllerParameterDescriptor)
|
||||
{
|
||||
parameterInfo = controllerParameterDescriptor.ParameterInfo;
|
||||
}
|
||||
else if (parameter is HandlerParameterDescriptor handlerParameterDescriptor)
|
||||
{
|
||||
parameterInfo = handlerParameterDescriptor.ParameterInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
parameterInfo = null;
|
||||
}
|
||||
|
||||
ModelMetadata metadata;
|
||||
if (optionsAccessor.Value.AllowValidatingTopLevelNodes &&
|
||||
modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase &&
|
||||
parameterInfo != null)
|
||||
{
|
||||
metadata = modelMetadataProviderBase.GetMetadataForParameter(parameterInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
metadata = modelMetadataProvider.GetMetadataForType(parameter.ParameterType);
|
||||
}
|
||||
|
||||
return parameterBinder.BindModelAsync(parameter, context, modelMetadataProvider, metadata);
|
||||
}
|
||||
|
||||
public static async Task<ModelBindingResult> BindModelAsync(
|
||||
this ParameterBinder parameterBinder,
|
||||
ParameterDescriptor parameter,
|
||||
|
|
@ -26,8 +63,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
ModelMetadata modelMetadata)
|
||||
{
|
||||
var valueProvider = await CompositeValueProvider.CreateAsync(context);
|
||||
|
||||
var modelBinderFactory = ModelBindingTestHelper.GetModelBinderFactory(modelMetadataProvider);
|
||||
var modelBinderFactory = ModelBindingTestHelper.GetModelBinderFactory(
|
||||
modelMetadataProvider,
|
||||
context.HttpContext.RequestServices);
|
||||
|
||||
var modelBinder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext
|
||||
{
|
||||
|
|
|
|||
|
|
@ -205,7 +205,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
.ForProperty<Person>(nameof(Person.Service))
|
||||
.BindingDetails(binding => binding.BindingSource = BindingSource.Services);
|
||||
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider);
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(metadataProvider: metadataProvider);
|
||||
var modelState = testContext.ModelState;
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
var parameter = new ParameterDescriptor
|
||||
{
|
||||
Name = "parameter-name",
|
||||
|
|
@ -213,10 +215,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
ParameterType = typeof(Person),
|
||||
};
|
||||
|
||||
var testContext = ModelBindingTestHelper.GetTestContext();
|
||||
testContext.MetadataProvider = metadataProvider;
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
||||
|
|
@ -245,7 +243,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
.ForType<JsonOutputFormatter>()
|
||||
.BindingDetails(binding => binding.BindingSource = BindingSource.Services);
|
||||
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider);
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(metadataProvider: metadataProvider);
|
||||
var modelState = testContext.ModelState;
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
var parameter = new ParameterDescriptor
|
||||
{
|
||||
Name = "parameter-name",
|
||||
|
|
@ -253,10 +253,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
ParameterType = typeof(JsonOutputFormatter),
|
||||
};
|
||||
|
||||
var testContext = ModelBindingTestHelper.GetTestContext();
|
||||
testContext.MetadataProvider = metadataProvider;
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
||||
|
|
@ -268,4 +264,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
Assert.Empty(modelState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -304,7 +303,16 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
binding.ModelBindingMessageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor(
|
||||
(value) => $"Hmm, '{ value }' is not a valid value.");
|
||||
});
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider);
|
||||
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(
|
||||
request =>
|
||||
{
|
||||
request.QueryString = QueryString.Create("Parameter1", "abcd");
|
||||
},
|
||||
metadataProvider: metadataProvider);
|
||||
|
||||
var modelState = testContext.ModelState;
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
var parameter = new ParameterDescriptor()
|
||||
{
|
||||
Name = "Parameter1",
|
||||
|
|
@ -312,13 +320,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
ParameterType = parameterType
|
||||
};
|
||||
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(request =>
|
||||
{
|
||||
request.QueryString = QueryString.Create("Parameter1", "abcd");
|
||||
});
|
||||
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
||||
|
|
@ -405,8 +406,16 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
binding.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
|
||||
value => $"Hurts when '{ value }' is provided.");
|
||||
});
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider);
|
||||
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(
|
||||
request =>
|
||||
{
|
||||
request.QueryString = QueryString.Create("Parameter1", string.Empty);
|
||||
},
|
||||
metadataProvider: metadataProvider);
|
||||
|
||||
var modelState = testContext.ModelState;
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
var parameter = new ParameterDescriptor
|
||||
{
|
||||
Name = "Parameter1",
|
||||
|
|
@ -414,11 +423,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
|
||||
ParameterType = parameterType
|
||||
};
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(request =>
|
||||
{
|
||||
request.QueryString = QueryString.Create("Parameter1", string.Empty);
|
||||
});
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
|
||||
|
|
|
|||
|
|
@ -1608,7 +1608,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
ParameterType = typeof(Order11)
|
||||
};
|
||||
|
||||
MvcOptions testOptions = null;
|
||||
var input = "{\"Zip\":\"47\"}";
|
||||
var testContext = ModelBindingTestHelper.GetTestContext(
|
||||
request =>
|
||||
|
|
@ -1621,10 +1620,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
options =>
|
||||
{
|
||||
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Address)));
|
||||
testOptions = options;
|
||||
});
|
||||
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testOptions);
|
||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
var detailsProvider = new DefaultCompositeMetadataDetailsProvider(
|
||||
Enumerable.Empty<IMetadataDetailsProvider>());
|
||||
var key = ModelMetadataIdentity.ForType(typeof(DateTime));
|
||||
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
|
||||
|
|
|
|||
Loading…
Reference in New Issue