diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingInfo.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingInfo.cs
index d47cda1bc2..8ebd959b56 100644
--- a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingInfo.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingInfo.cs
@@ -65,6 +65,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
///
/// Constructs a new instance of from the given .
+ ///
+ /// This overload does not account for specified via . Consider using
+ /// overload, or
+ /// on the result of this method to to get a more accurate instance.
+ ///
///
/// A collection of attributes which are used to construct
///
@@ -134,6 +139,81 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
return isBindingInfoPresent ? bindingInfo : null;
}
+ ///
+ /// Constructs a new instance of from the given and .
+ ///
+ /// A collection of attributes which are used to construct .
+ /// The .
+ /// A new instance of if any binding metadata was discovered; otherwise or .
+ public static BindingInfo GetBindingInfo(IEnumerable attributes, ModelMetadata modelMetadata)
+ {
+ if (attributes == null)
+ {
+ throw new ArgumentNullException(nameof(attributes));
+ }
+
+ if (modelMetadata == null)
+ {
+ throw new ArgumentNullException(nameof(modelMetadata));
+ }
+
+ var bindingInfo = GetBindingInfo(attributes);
+ var isBindingInfoPresent = bindingInfo != null;
+
+ if (bindingInfo == null)
+ {
+ bindingInfo = new BindingInfo();
+ }
+
+ isBindingInfoPresent |= bindingInfo.TryApplyBindingInfo(modelMetadata);
+
+ return isBindingInfoPresent ? bindingInfo : null;
+ }
+
+ ///
+ /// Applies binding metadata from the specified .
+ ///
+ /// Uses values from if no value is already available.
+ ///
+ ///
+ /// The .
+ /// if any binding metadata from was applied;
+ /// otherwise.
+ public bool TryApplyBindingInfo(ModelMetadata modelMetadata)
+ {
+ if (modelMetadata == null)
+ {
+ throw new ArgumentNullException(nameof(modelMetadata));
+ }
+
+ var isBindingInfoPresent = false;
+ if (BinderModelName == null && modelMetadata.BinderModelName != null)
+ {
+ isBindingInfoPresent = true;
+ BinderModelName = modelMetadata.BinderModelName;
+ }
+
+ if (BinderType == null && modelMetadata.BinderType != null)
+ {
+ isBindingInfoPresent = true;
+ BinderType = modelMetadata.BinderType;
+ }
+
+ if (BindingSource == null && modelMetadata.BindingSource != null)
+ {
+ isBindingInfoPresent = true;
+ BindingSource = modelMetadata.BindingSource;
+ }
+
+ if (PropertyFilterProvider == null && modelMetadata.PropertyFilterProvider != null)
+ {
+ isBindingInfoPresent = true;
+ PropertyFilterProvider = modelMetadata.PropertyFilterProvider;
+ }
+
+ return isBindingInfoPresent;
+ }
+
private class CompositePropertyFilterProvider : IPropertyFilterProvider
{
private readonly IEnumerable _providers;
diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs
index 87791e85da..995ba57c4d 100644
--- a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs
@@ -97,7 +97,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
isFromRequest: false);
///
- /// A for and .
+ /// A for , , and .
///
public static readonly BindingSource FormFile = new BindingSource(
"FormFile",
diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/DefaultApiDescriptionProvider.cs b/src/Microsoft.AspNetCore.Mvc.ApiExplorer/DefaultApiDescriptionProvider.cs
index 50734ccded..5da3f8a1cb 100644
--- a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/DefaultApiDescriptionProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.ApiExplorer/DefaultApiDescriptionProvider.cs
@@ -618,8 +618,8 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
return new ApiParameterDescriptionContext
{
ModelMetadata = metadata,
- BinderModelName = bindingInfo?.BinderModelName ?? metadata.BinderModelName,
- BindingSource = bindingInfo?.BindingSource ?? metadata.BindingSource,
+ BinderModelName = bindingInfo?.BinderModelName,
+ BindingSource = bindingInfo?.BindingSource,
PropertyName = propertyName ?? metadata.Name,
};
}
@@ -716,9 +716,11 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
{
var propertyMetadata = modelMetadata.Properties[i];
var key = new PropertyKey(propertyMetadata, source);
+ var bindingInfo = BindingInfo.GetBindingInfo(Enumerable.Empty(), propertyMetadata);
+
var propertyContext = ApiParameterDescriptionContext.GetContext(
propertyMetadata,
- bindingInfo: null,
+ bindingInfo: bindingInfo,
propertyName: null);
if (Visited.Add(key))
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs
index b9a3e830c7..5de286610d 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs
@@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
-using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
@@ -19,11 +18,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{
public class DefaultApplicationModelProvider : IApplicationModelProvider
{
- private readonly ICollection _globalFilters;
+ private readonly MvcOptions _mvcOptions;
+ private readonly IModelMetadataProvider _modelMetadataProvider;
- public DefaultApplicationModelProvider(IOptions mvcOptionsAccessor)
+ public DefaultApplicationModelProvider(
+ IOptions mvcOptionsAccessor,
+ IModelMetadataProvider modelMetadataProvider)
{
- _globalFilters = mvcOptionsAccessor.Value.Filters;
+ _mvcOptions = mvcOptionsAccessor.Value;
+ _modelMetadataProvider = modelMetadataProvider;
}
///
@@ -37,7 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
throw new ArgumentNullException(nameof(context));
}
- foreach (var filter in _globalFilters)
+ foreach (var filter in _mvcOptions.Filters)
{
context.Result.Filters.Add(filter);
}
@@ -118,9 +121,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
do
{
routeAttributes = currentTypeInfo
- .GetCustomAttributes(inherit: false)
- .OfType()
- .ToArray();
+ .GetCustomAttributes(inherit: false)
+ .OfType()
+ .ToArray();
if (routeAttributes.Length > 0)
{
@@ -213,24 +216,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
throw new ArgumentNullException(nameof(propertyInfo));
}
- // CoreCLR returns IEnumerable from GetCustomAttributes - the OfType
- // is needed to so that the result of ToArray() is object
var attributes = propertyInfo.GetCustomAttributes(inherit: true);
- var propertyModel = new PropertyModel(propertyInfo, attributes);
- var bindingInfo = BindingInfo.GetBindingInfo(attributes);
- if (bindingInfo != null)
- {
- propertyModel.BindingInfo = bindingInfo;
- }
- else if (IsFormFileType(propertyInfo.PropertyType))
- {
- propertyModel.BindingInfo = new BindingInfo
- {
- BindingSource = BindingSource.FormFile,
- };
- }
- propertyModel.PropertyName = propertyInfo.Name;
+
+ var modelMetadata = _modelMetadataProvider.GetMetadataForProperty(propertyInfo.DeclaringType, propertyInfo.Name);
+ var bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata);
+
+ var propertyModel = new PropertyModel(propertyInfo, attributes)
+ {
+ PropertyName = propertyInfo.Name,
+ BindingInfo = bindingInfo,
+ };
return propertyModel;
}
@@ -433,25 +429,25 @@ namespace Microsoft.AspNetCore.Mvc.Internal
throw new ArgumentNullException(nameof(parameterInfo));
}
- // CoreCLR returns IEnumerable from GetCustomAttributes - the OfType
- // is needed to so that the result of ToArray() is object
var attributes = parameterInfo.GetCustomAttributes(inherit: true);
- var parameterModel = new ParameterModel(parameterInfo, attributes);
- var bindingInfo = BindingInfo.GetBindingInfo(attributes);
- if (bindingInfo != null)
+ BindingInfo bindingInfo;
+ if (_mvcOptions.AllowValidatingTopLevelNodes && _modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase)
{
- parameterModel.BindingInfo = bindingInfo;
+ var modelMetadata = modelMetadataProviderBase.GetMetadataForParameter(parameterInfo);
+ bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata);
}
- else if (IsFormFileType(parameterInfo.ParameterType))
+ else
{
- parameterModel.BindingInfo = new BindingInfo
- {
- BindingSource = BindingSource.FormFile,
- };
+ // GetMetadataForParameter should only be used if the user has opted in to the 2.1 behavior.
+ bindingInfo = BindingInfo.GetBindingInfo(attributes);
}
- parameterModel.ParameterName = parameterInfo.Name;
+ var parameterModel = new ParameterModel(parameterInfo, attributes)
+ {
+ ParameterName = parameterInfo.Name,
+ BindingInfo = bindingInfo,
+ };
return parameterModel;
}
@@ -670,12 +666,5 @@ namespace Microsoft.AspNetCore.Mvc.Internal
list.Add(item);
}
}
-
- private static bool IsFormFileType(Type parameterType)
- {
- return parameterType == typeof(IFormFile) ||
- parameterType == typeof(IFormFileCollection) ||
- typeof(IEnumerable).IsAssignableFrom(parameterType);
- }
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs
index d33813cd1e..f03e2ca661 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Collections.Generic;
using System.IO;
using System.Threading;
using Microsoft.AspNetCore.Http;
@@ -76,28 +77,34 @@ namespace Microsoft.AspNetCore.Mvc.Internal
options.ValueProviderFactories.Add(new JQueryFormValueProviderFactory());
// Set up metadata providers
-
- // Don't bind the Type class by default as it's expensive. A user can override this behavior
- // by altering the collection of providers.
- options.ModelMetadataDetailsProviders.Add(new ExcludeBindingMetadataProvider(typeof(Type)));
-
- options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider());
- options.ModelMetadataDetailsProviders.Add(new DefaultValidationMetadataProvider());
-
- options.ModelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(CancellationToken), BindingSource.Special));
- options.ModelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(IFormFile), BindingSource.FormFile));
- options.ModelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(IFormCollection), BindingSource.FormFile));
+ ConfigureAdditionalModelMetadataDetailsProvider(options.ModelMetadataDetailsProviders);
// Set up validators
options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider());
+ }
+
+ internal static void ConfigureAdditionalModelMetadataDetailsProvider(IList modelMetadataDetailsProviders)
+ {
+ // Don't bind the Type class by default as it's expensive. A user can override this behavior
+ // by altering the collection of providers.
+ modelMetadataDetailsProviders.Add(new ExcludeBindingMetadataProvider(typeof(Type)));
+
+ modelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider());
+ modelMetadataDetailsProviders.Add(new DefaultValidationMetadataProvider());
+
+ modelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(CancellationToken), BindingSource.Special));
+ modelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(IFormFile), BindingSource.FormFile));
+ modelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(IFormCollection), BindingSource.FormFile));
+ modelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(IFormFileCollection), BindingSource.FormFile));
// Add types to be excluded from Validation
- options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Type)));
- options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Uri)));
- options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(CancellationToken)));
- options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IFormFile)));
- options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IFormCollection)));
- options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Stream)));
+ modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Type)));
+ modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Uri)));
+ modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(CancellationToken)));
+ modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IFormFile)));
+ modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IFormCollection)));
+ modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IFormFileCollection)));
+ modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Stream)));
}
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/DefaultModelMetadataProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/DefaultModelMetadataProvider.cs
index e71563e60a..5b79143002 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/DefaultModelMetadataProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/DefaultModelMetadataProvider.cs
@@ -5,7 +5,6 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
-using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Options;
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ModelBinderFactory.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ModelBinderFactory.cs
index 82f74e6e58..216fc7c803 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ModelBinderFactory.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ModelBinderFactory.cs
@@ -233,14 +233,18 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
_factory = factory;
Metadata = factoryContext.Metadata;
- BindingInfo = new BindingInfo
+ BindingInfo bindingInfo;
+ if (factoryContext.BindingInfo != null)
{
- BinderModelName = factoryContext.BindingInfo?.BinderModelName ?? Metadata.BinderModelName,
- BinderType = factoryContext.BindingInfo?.BinderType ?? Metadata.BinderType,
- BindingSource = factoryContext.BindingInfo?.BindingSource ?? Metadata.BindingSource,
- PropertyFilterProvider =
- factoryContext.BindingInfo?.PropertyFilterProvider ?? Metadata.PropertyFilterProvider,
- };
+ bindingInfo = new BindingInfo(factoryContext.BindingInfo);
+ }
+ else
+ {
+ bindingInfo = new BindingInfo();
+ }
+
+ bindingInfo.TryApplyBindingInfo(Metadata);
+ BindingInfo = bindingInfo;
MetadataProvider = _factory._metadataProvider;
Visited = new Dictionary();
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ParameterBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ParameterBinder.cs
index 0662d57a79..90a2458f2a 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ParameterBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ParameterBinder.cs
@@ -310,10 +310,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// This is to avoid adding validation errors for an 'empty' prefix when a simple
// type fails to bind. The fix for #7503 uncovered this issue, and was likely the
// original problem being worked around that regressed #7503.
- string modelName = modelBindingContext.ModelName;
+ var modelName = modelBindingContext.ModelName;
if (string.IsNullOrEmpty(modelBindingContext.ModelName) &&
- (parameter.BindingInfo?.BinderModelName ?? metadata.BinderModelName) == null)
+ parameter.BindingInfo?.BinderModelName == null)
{
// If we get here then this is a fallback case. The model name wasn't explicitly set
// and we ended up with an empty prefix.
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.Mvc.Core/Properties/AssemblyInfo.cs
index df50c7d842..abaed09181 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Properties/AssemblyInfo.cs
@@ -5,5 +5,6 @@ using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Mvc.Formatters;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Core.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
+[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.TestCommon, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
[assembly: TypeForwardedTo(typeof(InputFormatterException))]
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageApplicationModelProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageApplicationModelProvider.cs
index 0db3f1e45d..138c14cbe8 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageApplicationModelProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageApplicationModelProvider.cs
@@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.Extensions.Internal;
+using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
@@ -17,6 +18,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private const string ModelPropertyName = "Model";
private readonly PageHandlerPageFilter _pageHandlerPageFilter = new PageHandlerPageFilter();
private readonly PageHandlerResultFilter _pageHandlerResultFilter = new PageHandlerResultFilter();
+ private readonly IModelMetadataProvider _modelMetadataProvider;
+ private readonly MvcOptions _options;
+
+ public DefaultPageApplicationModelProvider(
+ IModelMetadataProvider modelMetadataProvider,
+ IOptions options)
+ {
+ _modelMetadataProvider = modelMetadataProvider;
+ _options = options.Value;
+ }
///
public int Order => -1000;
@@ -217,9 +228,22 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
throw new ArgumentNullException(nameof(parameter));
}
- return new PageParameterModel(parameter, parameter.GetCustomAttributes(inherit: true))
+ var attributes = parameter.GetCustomAttributes(inherit: true);
+
+ BindingInfo bindingInfo;
+ if (_options.AllowValidatingTopLevelNodes && _modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase)
{
- BindingInfo = BindingInfo.GetBindingInfo(parameter.GetCustomAttributes()),
+ var modelMetadata = modelMetadataProviderBase.GetMetadataForParameter(parameter);
+ bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata);
+ }
+ else
+ {
+ bindingInfo = BindingInfo.GetBindingInfo(attributes);
+ }
+
+ return new PageParameterModel(parameter, attributes)
+ {
+ BindingInfo = bindingInfo,
ParameterName = parameter.Name,
};
}
@@ -237,12 +261,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
}
var propertyAttributes = property.GetCustomAttributes(inherit: true);
- var handlerAttributes = property.DeclaringType.GetCustomAttributes(inherit: true);
- // Look for binding info on the handler if nothing is specified on the property.
- // This allows BindProperty attributes on handlers to apply to properties.
- var bindingInfo = BindingInfo.GetBindingInfo(propertyAttributes) ??
- BindingInfo.GetBindingInfo(handlerAttributes);
+ var propertyMetadata = _modelMetadataProvider.GetMetadataForProperty(property.DeclaringType, property.Name);
+ var bindingInfo = BindingInfo.GetBindingInfo(propertyAttributes, propertyMetadata);
+
+ if (bindingInfo == null)
+ {
+ // Look for binding info on the handler if nothing is specified on the property.
+ // This allows BindProperty attributes on handlers to apply to properties.
+ var handlerType = property.DeclaringType;
+ var handlerAttributes = handlerType.GetCustomAttributes(inherit: true);
+ var handlerMetadata = _modelMetadataProvider.GetMetadataForType(property.DeclaringType);
+ bindingInfo = BindingInfo.GetBindingInfo(handlerAttributes, handlerMetadata);
+ }
var model = new PagePropertyModel(property, propertyAttributes)
{
diff --git a/test/Microsoft.AspNetCore.Mvc.Abstractions.Test/Microsoft.AspNetCore.Mvc.Abstractions.Test.csproj b/test/Microsoft.AspNetCore.Mvc.Abstractions.Test/Microsoft.AspNetCore.Mvc.Abstractions.Test.csproj
index 998eb7e89f..1ca6c1dee7 100644
--- a/test/Microsoft.AspNetCore.Mvc.Abstractions.Test/Microsoft.AspNetCore.Mvc.Abstractions.Test.csproj
+++ b/test/Microsoft.AspNetCore.Mvc.Abstractions.Test/Microsoft.AspNetCore.Mvc.Abstractions.Test.csproj
@@ -6,6 +6,7 @@
+
diff --git a/test/Microsoft.AspNetCore.Mvc.Abstractions.Test/ModelBinding/BindingInfoTest.cs b/test/Microsoft.AspNetCore.Mvc.Abstractions.Test/ModelBinding/BindingInfoTest.cs
new file mode 100644
index 0000000000..3c83f948d8
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.Abstractions.Test/ModelBinding/BindingInfoTest.cs
@@ -0,0 +1,197 @@
+// 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;
+using System.Linq;
+using Moq;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Mvc.ModelBinding
+{
+ public class BindingInfoTest
+ {
+ [Fact]
+ public void GetBindingInfo_WithAttributes_ConstructsBindingInfo()
+ {
+ // Arrange
+ var attributes = new object[]
+ {
+ new FromQueryAttribute { Name = "Test" },
+ };
+
+ // Act
+ var bindingInfo = BindingInfo.GetBindingInfo(attributes);
+
+ // Assert
+ Assert.NotNull(bindingInfo);
+ Assert.Same("Test", bindingInfo.BinderModelName);
+ Assert.Same(BindingSource.Query, bindingInfo.BindingSource);
+ }
+
+ [Fact]
+ public void GetBindingInfo_ReadsPropertyPredicateProvider()
+ {
+ // Arrange
+ var bindAttribute = new BindAttribute(include: "SomeProperty");
+ var attributes = new object[]
+ {
+ bindAttribute,
+ };
+
+ // Act
+ var bindingInfo = BindingInfo.GetBindingInfo(attributes);
+
+ // Assert
+ Assert.NotNull(bindingInfo);
+ Assert.Same(bindAttribute, bindingInfo.PropertyFilterProvider);
+ }
+
+ [Fact]
+ public void GetBindingInfo_ReadsRequestPredicateProvider()
+ {
+ // Arrange
+ var attributes = new object[]
+ {
+ new BindPropertyAttribute { Name = "PropertyPrefix", SupportsGet = true, },
+ };
+
+ // Act
+ var bindingInfo = BindingInfo.GetBindingInfo(attributes);
+
+ // Assert
+ Assert.NotNull(bindingInfo);
+ Assert.Same("PropertyPrefix", bindingInfo.BinderModelName);
+ Assert.NotNull(bindingInfo.RequestPredicate);
+ }
+
+ [Fact]
+ public void GetBindingInfo_ReturnsNull_IfNoBindingAttributesArePresent()
+ {
+ // Arrange
+ var attributes = new object[] { new ControllerAttribute(), new BindNeverAttribute(), };
+
+ // Act
+ var bindingInfo = BindingInfo.GetBindingInfo(attributes);
+
+ // Assert
+ Assert.Null(bindingInfo);
+ }
+
+ [Fact]
+ public void GetBindingInfo_WithAttributesAndModelMetadata_UsesValuesFromBindingInfo_IfAttributesPresent()
+ {
+ // Arrange
+ var attributes = new object[]
+ {
+ new ModelBinderAttribute { BinderType = typeof(object), Name = "Test" },
+ };
+ var modelType = typeof(Guid);
+ var provider = new TestModelMetadataProvider();
+ provider.ForType(modelType).BindingDetails(metadata =>
+ {
+ metadata.BindingSource = BindingSource.Special;
+ metadata.BinderType = typeof(string);
+ metadata.BinderModelName = "Different";
+ });
+ var modelMetadata = provider.GetMetadataForType(modelType);
+
+ // Act
+ var bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata);
+
+ // Assert
+ Assert.NotNull(bindingInfo);
+ Assert.Same(typeof(object), bindingInfo.BinderType);
+ Assert.Same("Test", bindingInfo.BinderModelName);
+ }
+
+ [Fact]
+ public void GetBindingInfo_WithAttributesAndModelMetadata_UsesBinderNameFromModelMetadata_WhenNotFoundViaAttributes()
+ {
+ // Arrange
+ var attributes = new object[] { new ModelBinderAttribute(typeof(object)), new ControllerAttribute(), new BindNeverAttribute(), };
+ var modelType = typeof(Guid);
+ var provider = new TestModelMetadataProvider();
+ provider.ForType(modelType).BindingDetails(metadata =>
+ {
+ metadata.BindingSource = BindingSource.Special;
+ metadata.BinderType = typeof(string);
+ metadata.BinderModelName = "Different";
+ });
+ var modelMetadata = provider.GetMetadataForType(modelType);
+
+ // Act
+ var bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata);
+
+ // Assert
+ Assert.NotNull(bindingInfo);
+ Assert.Same(typeof(object), bindingInfo.BinderType);
+ Assert.Same("Different", bindingInfo.BinderModelName);
+ Assert.Same(BindingSource.Custom, bindingInfo.BindingSource);
+ }
+
+ [Fact]
+ public void GetBindingInfo_WithAttributesAndModelMetadata_UsesModelBinderFromModelMetadata_WhenNotFoundViaAttributes()
+ {
+ // Arrange
+ var attributes = new object[] { new ControllerAttribute(), new BindNeverAttribute(), };
+ var modelType = typeof(Guid);
+ var provider = new TestModelMetadataProvider();
+ provider.ForType(modelType).BindingDetails(metadata =>
+ {
+ metadata.BinderType = typeof(string);
+ });
+ var modelMetadata = provider.GetMetadataForType(modelType);
+
+ // Act
+ var bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata);
+
+ // Assert
+ Assert.NotNull(bindingInfo);
+ Assert.Same(typeof(string), bindingInfo.BinderType);
+ }
+
+ [Fact]
+ public void GetBindingInfo_WithAttributesAndModelMetadata_UsesBinderSourceFromModelMetadata_WhenNotFoundViaAttributes()
+ {
+ // Arrange
+ var attributes = new object[] { new BindPropertyAttribute(), new ControllerAttribute(), new BindNeverAttribute(), };
+ var modelType = typeof(Guid);
+ var provider = new TestModelMetadataProvider();
+ provider.ForType(modelType).BindingDetails(metadata =>
+ {
+ metadata.BindingSource = BindingSource.Services;
+ });
+ var modelMetadata = provider.GetMetadataForType(modelType);
+
+ // Act
+ var bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata);
+
+ // Assert
+ Assert.NotNull(bindingInfo);
+ Assert.Same(BindingSource.Services, bindingInfo.BindingSource);
+ }
+
+ [Fact]
+ public void GetBindingInfo_WithAttributesAndModelMetadata_UsesPropertyPredicateProviderFromModelMetadata_WhenNotFoundViaAttributes()
+ {
+ // Arrange
+ var attributes = new object[] { new ModelBinderAttribute(typeof(object)), new ControllerAttribute(), new BindNeverAttribute(), };
+ var modelAttributes = new ModelAttributes(Enumerable.Empty(), null, null);
+ var propertyFilterProvider = Mock.Of();
+ var modelType = typeof(Guid);
+ var provider = new TestModelMetadataProvider();
+ provider.ForType(modelType).BindingDetails(metadata =>
+ {
+ metadata.PropertyFilterProvider = propertyFilterProvider;
+ });
+ var modelMetadata = provider.GetMetadataForType(modelType);
+
+ // Act
+ var bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata);
+
+ // Assert
+ Assert.NotNull(bindingInfo);
+ Assert.Same(propertyFilterProvider, bindingInfo.PropertyFilterProvider);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ActionSelectorTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ActionSelectorTest.cs
index 6076c380dd..d58a50f6b9 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ActionSelectorTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ActionSelectorTest.cs
@@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Internal;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
@@ -790,7 +791,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
var manager = GetApplicationManager(controllerTypes);
- var modelProvider = new DefaultApplicationModelProvider(options);
+ var modelProvider = new DefaultApplicationModelProvider(options, TestModelMetadataProvider.CreateDefaultProvider());
var provider = new ControllerActionDescriptorProvider(
manager,
@@ -964,8 +965,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
foreach (var item in context.Results)
{
- var marker = item.Metadata as BooleanConstraintMarker;
- if (marker != null)
+ if (item.Metadata is BooleanConstraintMarker marker)
{
Assert.Null(item.Constraint);
item.Constraint = new BooleanConstraint() { Pass = marker.Pass };
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ApiBehaviorApplicationModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ApiBehaviorApplicationModelProviderTest.cs
index 445706ec8a..1fae855ed9 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ApiBehaviorApplicationModelProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ApiBehaviorApplicationModelProviderTest.cs
@@ -389,14 +389,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{
// Arrange
var actionName = nameof(ParameterBindingController.ComplexTypeModelWithCancellationToken);
- var actionModel = GetActionModel(typeof(ParameterBindingController), actionName);
- // Go through MvcOptions and MvcCoreMvcOptionsSetup to make
- // sure we pick up the proper ModelMetadataDetailsProviders from the options.
- var options = new MvcOptions();
- new MvcCoreMvcOptionsSetup(new TestHttpRequestStreamReaderFactory()).Configure(options);
- var metadataProvider = TestModelMetadataProvider.CreateProvider(options.ModelMetadataDetailsProviders);
- var provider = GetProvider(modelMetadataProvider: metadataProvider);
+ // Use the default set of ModelMetadataProviders so we get metadata details for CancellationToken.
+ var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
+ var context = GetContext(typeof(ParameterBindingController), modelMetadataProvider);
+ var controllerModel = Assert.Single(context.Result.Controllers);
+ var actionModel = Assert.Single(controllerModel.Actions, m => m.ActionName == actionName);
+
+ var provider = GetProvider();
// Act
provider.InferParameterBindingSources(actionModel);
@@ -529,16 +529,21 @@ namespace Microsoft.AspNetCore.Mvc.Internal
};
var optionsAccessor = Options.Create(options);
- modelMetadataProvider = modelMetadataProvider ?? new TestModelMetadataProvider();
var loggerFactory = NullLoggerFactory.Instance;
-
+ modelMetadataProvider = modelMetadataProvider ?? new EmptyModelMetadataProvider();
return new ApiBehaviorApplicationModelProvider(optionsAccessor, modelMetadataProvider, loggerFactory);
}
- private static ApplicationModelProviderContext GetContext(Type type)
+ private static ApplicationModelProviderContext GetContext(
+ Type type,
+ IModelMetadataProvider modelMetadataProvider = null)
{
var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() });
- new DefaultApplicationModelProvider(Options.Create(new MvcOptions())).OnProvidersExecuting(context);
+ var mvcOptions = Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = true });
+ modelMetadataProvider = modelMetadataProvider ?? new EmptyModelMetadataProvider();
+ var provider = new DefaultApplicationModelProvider(mvcOptions, modelMetadataProvider);
+ provider.OnProvidersExecuting(context);
+
return context;
}
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AuthorizationApplicationModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AuthorizationApplicationModelProviderTest.cs
index a3ff38646f..5015aaa56a 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AuthorizationApplicationModelProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AuthorizationApplicationModelProviderTest.cs
@@ -1,7 +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;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Authorization;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
@@ -22,10 +23,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{
// Arrange
var provider = new AuthorizationApplicationModelProvider(new DefaultAuthorizationPolicyProvider(Options.Create(new AuthorizationOptions())));
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- var context = new ApplicationModelProviderContext(new[] { typeof(AccountController).GetTypeInfo() });
- defaultProvider.OnProvidersExecuting(context);
+ var controllerType = typeof(AccountController);
+ var context = CreateProviderContext(controllerType);
// Act
provider.OnProvidersExecuting(context);
@@ -44,10 +43,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
options.Value.AddPolicy("Derived", policy => policy.RequireClaim("Derived"));
var provider = new AuthorizationApplicationModelProvider(new DefaultAuthorizationPolicyProvider(options));
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- var context = new ApplicationModelProviderContext(new[] { typeof(DerivedController).GetTypeInfo() });
- defaultProvider.OnProvidersExecuting(context);
+ var context = CreateProviderContext(typeof(DerivedController));
// Act
provider.OnProvidersExecuting(context);
@@ -71,10 +67,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{
// Arrange
var provider = new AuthorizationApplicationModelProvider(new DefaultAuthorizationPolicyProvider(Options.Create(new AuthorizationOptions())));
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- var context = new ApplicationModelProviderContext(new[] { typeof(AnonymousController).GetTypeInfo() });
- defaultProvider.OnProvidersExecuting(context);
+ var context = CreateProviderContext(typeof(AnonymousController));
// Act
provider.OnProvidersExecuting(context);
@@ -100,10 +93,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var policyProvider = new DefaultAuthorizationPolicyProvider(authOptions);
var provider = new AuthorizationApplicationModelProvider(policyProvider);
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
+ var context = CreateProviderContext(typeof(BaseController));
// Act
- var action = GetBaseControllerActionModel(provider, defaultProvider);
+ var action = GetBaseControllerActionModel(provider);
// Assert
var authorizationFilter = Assert.IsType(Assert.Single(action.Filters));
@@ -128,10 +121,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
.Verifiable();
var provider = new AuthorizationApplicationModelProvider(authorizationPolicyProviderMock.Object);
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
// Act
- var action = GetBaseControllerActionModel(provider, defaultProvider);
+ var action = GetBaseControllerActionModel(provider);
// Assert
var actionFilter = Assert.IsType(Assert.Single(action.Filters));
@@ -148,10 +140,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultAuthorizationPolicyProvider(
Options.Create(new AuthorizationOptions())
));
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- var context = new ApplicationModelProviderContext(new[] { typeof(NoAuthController).GetTypeInfo() });
- defaultProvider.OnProvidersExecuting(context);
+ var context = CreateProviderContext(typeof(NoAuthController));
// Act
provider.OnProvidersExecuting(context);
@@ -163,16 +152,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Assert.Empty(action.Filters);
}
- private ActionModel GetBaseControllerActionModel(
- IApplicationModelProvider authorizationApplicationModelProvider,
- IApplicationModelProvider applicationModelProvider)
+ private ActionModel GetBaseControllerActionModel(AuthorizationApplicationModelProvider authorizationApplicationModelProvider)
{
- var context = new ApplicationModelProviderContext(new[] { typeof(BaseController).GetTypeInfo() });
- applicationModelProvider.OnProvidersExecuting(context);
- var authorizeData = new List
- {
- new AuthorizeAttribute("POLICY")
- };
+ var context = CreateProviderContext(typeof(BaseController));
authorizationApplicationModelProvider.OnProvidersExecuting(context);
@@ -183,6 +165,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
return action;
}
+ private static ApplicationModelProviderContext CreateProviderContext(Type controllerType)
+ {
+ var defaultProvider = new DefaultApplicationModelProvider(
+ Options.Create(new MvcOptions()),
+ TestModelMetadataProvider.CreateDefaultProvider());
+
+ var context = new ApplicationModelProviderContext(new[] { controllerType.GetTypeInfo() });
+ defaultProvider.OnProvidersExecuting(context);
+ return context;
+ }
+
private class BaseController
{
[Authorize(Policy = "Base")]
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs
index 7e3ba9bd38..0afae5ae78 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs
@@ -709,7 +709,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var manager = GetApplicationManager(new[] { controllerTypeInfo });
var options = Options.Create(new MvcOptions());
options.Value.Conventions.Add(new TestRoutingConvention());
- var modelProvider = new DefaultApplicationModelProvider(options);
+ var modelProvider = new DefaultApplicationModelProvider(options, TestModelMetadataProvider.CreateDefaultProvider());
var provider = new ControllerActionDescriptorProvider(
manager,
new[] { modelProvider },
@@ -1397,7 +1397,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var manager = GetApplicationManager(new[] { controllerTypeInfo });
- var modelProvider = new DefaultApplicationModelProvider(options);
+ var modelProvider = new DefaultApplicationModelProvider(options, TestModelMetadataProvider.CreateDefaultProvider());
var provider = new ControllerActionDescriptorProvider(
manager,
@@ -1413,7 +1413,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var options = Options.Create(new MvcOptions());
var manager = GetApplicationManager(controllerTypeInfos);
- var modelProvider = new DefaultApplicationModelProvider(options);
+ var modelProvider = new DefaultApplicationModelProvider(options, TestModelMetadataProvider.CreateDefaultProvider());
var provider = new ControllerActionDescriptorProvider(
manager,
@@ -1432,7 +1432,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var manager = GetApplicationManager(new[] { controllerTypeInfo });
- var modelProvider = new DefaultApplicationModelProvider(options);
+ var modelProvider = new DefaultApplicationModelProvider(options, TestModelMetadataProvider.CreateDefaultProvider());
var provider = new ControllerActionDescriptorProvider(
manager,
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultApplicationModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultApplicationModelProviderTest.cs
index 5605b698ac..90a038b4c7 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultApplicationModelProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultApplicationModelProviderTest.cs
@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.Options;
using Xunit;
@@ -74,6 +75,49 @@ namespace Microsoft.AspNetCore.Mvc.Internal
});
}
+ [Fact]
+ public void OnProvidersExecuting_ReadsBindingSourceForPropertiesFromModelMetadata()
+ {
+ // Arrange
+ var detailsProvider = new BindingSourceMetadataProvider(typeof(string), BindingSource.Services);
+ var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(new[] { detailsProvider });
+ var typeInfo = typeof(ModelBinderController).GetTypeInfo();
+ var provider = new TestApplicationModelProvider(Options.Create(new MvcOptions()), modelMetadataProvider);
+
+ var context = new ApplicationModelProviderContext(new[] { typeInfo });
+
+ // Act
+ provider.OnProvidersExecuting(context);
+
+ // Assert
+ var controllerModel = Assert.Single(context.Result.Controllers);
+ Assert.Collection(
+ controllerModel.ControllerProperties.OrderBy(p => p.PropertyName),
+ property =>
+ {
+ Assert.Equal(nameof(ModelBinderController.Bound), property.PropertyName);
+ Assert.Equal(BindingSource.Query, property.BindingInfo.BindingSource);
+ Assert.Same(controllerModel, property.Controller);
+
+ var attribute = Assert.Single(property.Attributes);
+ Assert.IsType(attribute);
+ },
+ property =>
+ {
+ Assert.Equal(nameof(ModelBinderController.FormFile), property.PropertyName);
+ Assert.Equal(BindingSource.FormFile, property.BindingInfo.BindingSource);
+ Assert.Same(controllerModel, property.Controller);
+
+ Assert.Empty(property.Attributes);
+ },
+ property =>
+ {
+ Assert.Equal(nameof(ModelBinderController.Unbound), property.PropertyName);
+ Assert.Equal(BindingSource.Services, property.BindingInfo.BindingSource);
+ Assert.Same(controllerModel, property.Controller);
+ });
+ }
+
[Fact]
public void OnProvidersExecuting_AddsBindingSources_ForActionParameters()
{
@@ -88,7 +132,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
var controllerModel = Assert.Single(context.Result.Controllers);
- var action = Assert.Single(controllerModel.Actions);
+ var action = Assert.Single(controllerModel.Actions, a => a.ActionMethod.Name == nameof(ModelBinderController.PostAction));
Assert.Collection(
action.Parameters,
parameter =>
@@ -116,6 +160,107 @@ namespace Microsoft.AspNetCore.Mvc.Internal
});
}
+ [Fact]
+ public void OnProvidersExecuting_AddsBindingSources_ForActionParameters_WithLegacyValidationBehavior()
+ {
+ // Arrange
+ var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
+ var options = Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = false });
+ var builder = new TestApplicationModelProvider(options, modelMetadataProvider);
+ var typeInfo = typeof(ModelBinderController).GetTypeInfo();
+
+ var context = new ApplicationModelProviderContext(new[] { typeInfo });
+
+ // Act
+ builder.OnProvidersExecuting(context);
+
+ // Assert
+ var controllerModel = Assert.Single(context.Result.Controllers);
+ var action = Assert.Single(controllerModel.Actions, a => a.ActionMethod.Name == nameof(ModelBinderController.PostAction));
+ Assert.Collection(
+ action.Parameters,
+ parameter =>
+ {
+ Assert.Equal("fromQuery", parameter.ParameterName);
+ Assert.Equal(BindingSource.Query, parameter.BindingInfo.BindingSource);
+ Assert.Same(action, parameter.Action);
+
+ var attribute = Assert.Single(parameter.Attributes);
+ Assert.IsType(attribute);
+ },
+ parameter =>
+ {
+ Assert.Equal("formFileCollection", parameter.ParameterName);
+ // BindingSource for IFormFileCollection comes from ModelMetadata which we are not using here.
+ Assert.Null(parameter.BindingInfo);
+ Assert.Same(action, parameter.Action);
+
+ Assert.Empty(parameter.Attributes);
+ },
+ parameter =>
+ {
+ Assert.Equal("unbound", parameter.ParameterName);
+ Assert.Null(parameter.BindingInfo);
+ Assert.Same(action, parameter.Action);
+ });
+ }
+
+ [Fact]
+ public void OnProvidersExecuting_AddsBindingSources_ForActionParameters_ReadFromModelMetadata()
+ {
+ // Arrange
+ var options = new MvcOptions { AllowValidatingTopLevelNodes = true };
+ var detailsProvider = new BindingSourceMetadataProvider(typeof(Guid), BindingSource.Special);
+ var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(new[] { detailsProvider });
+
+ var provider = new TestApplicationModelProvider(Options.Create(options), modelMetadataProvider);
+ var typeInfo = typeof(ModelBinderController).GetTypeInfo();
+
+ var context = new ApplicationModelProviderContext(new[] { typeInfo });
+
+ // Act
+ provider.OnProvidersExecuting(context);
+
+ // Assert
+ var controllerModel = Assert.Single(context.Result.Controllers);
+ var action = Assert.Single(controllerModel.Actions, a => a.ActionName == nameof(ModelBinderController.PostAction1));
+ Assert.Collection(
+ action.Parameters,
+ parameter =>
+ {
+ Assert.Equal("guid", parameter.ParameterName);
+ Assert.Equal(BindingSource.Special, parameter.BindingInfo.BindingSource);
+ });
+ }
+
+ [Fact]
+ public void OnProvidersExecuting_UsesBindingSourceSpecifiedOnParameter()
+ {
+ // Arrange
+ var options = new MvcOptions();
+ var detailsProvider = new BindingSourceMetadataProvider(typeof(Guid), BindingSource.Special);
+ var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(new[] { detailsProvider });
+
+ var provider = new TestApplicationModelProvider(Options.Create(options), modelMetadataProvider);
+ var typeInfo = typeof(ModelBinderController).GetTypeInfo();
+
+ var context = new ApplicationModelProviderContext(new[] { typeInfo });
+
+ // Act
+ provider.OnProvidersExecuting(context);
+
+ // Assert
+ var controllerModel = Assert.Single(context.Result.Controllers);
+ var action = Assert.Single(controllerModel.Actions, a => a.ActionName == nameof(ModelBinderController.PostAction2));
+ Assert.Collection(
+ action.Parameters,
+ parameter =>
+ {
+ Assert.Equal("fromQuery", parameter.ParameterName);
+ Assert.Equal(BindingSource.Query, parameter.BindingInfo.BindingSource);
+ });
+ }
+
// This class has a filter attribute, but doesn't implement any filter interfaces,
// so ControllerFilter is not present.
[Fact]
@@ -1363,6 +1508,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public IFormFile FormFile { get; set; }
public IActionResult PostAction([FromQuery] string fromQuery, IFormFileCollection formFileCollection, string unbound) => null;
+
+ public IActionResult PostAction1(Guid guid) => null;
+
+ public IActionResult PostAction2([FromQuery] Guid fromQuery) => null;
}
public class SomeFiltersController : IAsyncActionFilter, IResultFilter
@@ -1404,13 +1553,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private class TestApplicationModelProvider : DefaultApplicationModelProvider
{
public TestApplicationModelProvider()
- : this(Options.Create(new MvcOptions()))
+ : this(Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = true }), TestModelMetadataProvider.CreateDefaultProvider())
{
}
public TestApplicationModelProvider(
- IOptions options)
- : base(options)
+ IOptions options,
+ IModelMetadataProvider modelMetadataProvider)
+ : base(options, modelMetadataProvider)
{
}
diff --git a/test/Microsoft.AspNetCore.Mvc.Cors.Test/Internal/CorsApplicationModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Cors.Test/Internal/CorsApplicationModelProviderTest.cs
index fb3eecb51a..3b9ab90213 100644
--- a/test/Microsoft.AspNetCore.Mvc.Cors.Test/Internal/CorsApplicationModelProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Cors.Test/Internal/CorsApplicationModelProviderTest.cs
@@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Internal;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
@@ -23,10 +24,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{
// Arrange
var corsProvider = new CorsApplicationModelProvider();
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- var context = new ApplicationModelProviderContext(new [] { typeof(CorsController).GetTypeInfo() });
- defaultProvider.OnProvidersExecuting(context);
+ var context = GetProviderContext(typeof(CorsController));
// Act
corsProvider.OnProvidersExecuting(context);
@@ -45,10 +43,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{
// Arrange
var corsProvider = new CorsApplicationModelProvider();
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- var context = new ApplicationModelProviderContext(new[] { typeof(DisableCorsController).GetTypeInfo() });
- defaultProvider.OnProvidersExecuting(context);
+ var context = GetProviderContext(typeof(DisableCorsController));
// Act
corsProvider.OnProvidersExecuting(context);
@@ -67,10 +62,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{
// Arrange
var corsProvider = new CorsApplicationModelProvider();
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- var context = new ApplicationModelProviderContext(new[] { typeof(CustomCorsFilterController).GetTypeInfo() });
- defaultProvider.OnProvidersExecuting(context);
+ var context = GetProviderContext(typeof(CustomCorsFilterController));
// Act
corsProvider.OnProvidersExecuting(context);
@@ -88,10 +80,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{
// Arrange
var corsProvider = new CorsApplicationModelProvider();
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- var context = new ApplicationModelProviderContext(new[] { typeof(EnableCorsController).GetTypeInfo() });
- defaultProvider.OnProvidersExecuting(context);
+ var context = GetProviderContext(typeof(EnableCorsController));
// Act
corsProvider.OnProvidersExecuting(context);
@@ -110,10 +99,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{
// Arrange
var corsProvider = new CorsApplicationModelProvider();
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- var context = new ApplicationModelProviderContext(new[] { typeof(DisableCorsActionController).GetTypeInfo() });
- defaultProvider.OnProvidersExecuting(context);
+ var context = GetProviderContext(typeof(DisableCorsActionController));
// Act
corsProvider.OnProvidersExecuting(context);
@@ -132,10 +118,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{
// Arrange
var corsProvider = new CorsApplicationModelProvider();
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- var context = new ApplicationModelProviderContext(new[] { typeof(CustomCorsFilterOnActionController).GetTypeInfo() });
- defaultProvider.OnProvidersExecuting(context);
+ var context = GetProviderContext(typeof(CustomCorsFilterOnActionController));
// Act
corsProvider.OnProvidersExecuting(context);
@@ -153,12 +136,10 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{
// Arrange
var corsProvider = new CorsApplicationModelProvider();
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
+ var context = GetProviderContext(typeof(RegularController));
- var context = new ApplicationModelProviderContext(new[] { typeof(RegularController).GetTypeInfo() });
context.Result.Filters.Add(
new CorsAuthorizationFilter(Mock.Of(), Mock.Of(), Mock.Of()));
- defaultProvider.OnProvidersExecuting(context);
// Act
corsProvider.OnProvidersExecuting(context);
@@ -176,11 +157,8 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{
// Arrange
var corsProvider = new CorsApplicationModelProvider();
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- var context = new ApplicationModelProviderContext(new[] { typeof(RegularController).GetTypeInfo() });
+ var context = GetProviderContext(typeof(RegularController));
context.Result.Filters.Add(new DisableCorsAuthorizationFilter());
- defaultProvider.OnProvidersExecuting(context);
// Act
corsProvider.OnProvidersExecuting(context);
@@ -198,11 +176,8 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{
// Arrange
var corsProvider = new CorsApplicationModelProvider();
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- var context = new ApplicationModelProviderContext(new[] { typeof(RegularController).GetTypeInfo() });
+ var context = GetProviderContext(typeof(RegularController));
context.Result.Filters.Add(new CustomCorsFilterAttribute());
- defaultProvider.OnProvidersExecuting(context);
// Act
corsProvider.OnProvidersExecuting(context);
@@ -220,10 +195,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{
// Arrange
var corsProvider = new CorsApplicationModelProvider();
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- var context = new ApplicationModelProviderContext(new[] { typeof(RegularController).GetTypeInfo() });
- defaultProvider.OnProvidersExecuting(context);
+ var context = GetProviderContext(typeof(RegularController));
// Act
corsProvider.OnProvidersExecuting(context);
@@ -236,6 +208,17 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
Assert.IsNotType(constraint);
}
+ private static ApplicationModelProviderContext GetProviderContext(Type controllerType)
+ {
+ var context = new ApplicationModelProviderContext(new[] { controllerType.GetTypeInfo() });
+ var provider = new DefaultApplicationModelProvider(
+ Options.Create(new MvcOptions()),
+ TestModelMetadataProvider.CreateDefaultProvider());
+ provider.OnProvidersExecuting(context);
+
+ return context;
+ }
+
private class EnableCorsController
{
[EnableCors("policy")]
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ApiBehaviorTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ApiBehaviorTest.cs
index 78e1d637c6..3b5d399c2b 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ApiBehaviorTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ApiBehaviorTest.cs
@@ -88,7 +88,14 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
}
[Fact]
- public async Task ActionsWithApiBehavior_InferFromBodyParameters()
+ public Task ActionsWithApiBehavior_InferFromBodyParameters()
+ => ActionsWithApiBehaviorInferFromBodyParameters("ActionWithInferredFromBodyParameter");
+
+ [Fact]
+ public Task ActionsWithApiBehavior_InferFromBodyParameters_DoNotConsiderCancellationTokenSourceParameter()
+ => ActionsWithApiBehaviorInferFromBodyParameters("ActionWithInferredFromBodyParameterAndCancellationToken");
+
+ private async Task ActionsWithApiBehaviorInferFromBodyParameters(string action)
{
// Arrange
var input = new Contact
@@ -98,7 +105,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
};
// Act
- var response = await Client.PostAsJsonAsync("/contact/ActionWithInferredFromBodyParameter", input);
+ var response = await Client.PostAsJsonAsync($"/contact/{action}", input);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/AuthorizeFilterIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/AuthorizeFilterIntegrationTest.cs
index 31dcf75a53..f19a67d45b 100644
--- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/AuthorizeFilterIntegrationTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/AuthorizeFilterIntegrationTest.cs
@@ -13,11 +13,12 @@ using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Internal;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
using Microsoft.Extensions.ObjectPool;
+using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.IntegrationTests
@@ -31,13 +32,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
public async Task AuthorizeFilter_CalledTwiceWithNonDefaultProvider()
{
// Arrange
- var applicationModelProviderContext = new ApplicationModelProviderContext(
- new[] { typeof(AuthorizeController).GetTypeInfo() });
+ var applicationModelProviderContext = GetProviderContext(typeof(AuthorizeController));
var policyProvider = new TestAuthorizationPolicyProvider();
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
-
- defaultProvider.OnProvidersExecuting(applicationModelProviderContext);
var controller = Assert.Single(applicationModelProviderContext.Result.Controllers);
var action = Assert.Single(controller.Actions);
@@ -64,6 +61,17 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
return httpContext;
}
+ private static ApplicationModelProviderContext GetProviderContext(Type controllerType)
+ {
+ var context = new ApplicationModelProviderContext(new[] { controllerType.GetTypeInfo() });
+ var provider = new DefaultApplicationModelProvider(
+ Options.Create(new MvcOptions()),
+ TestModelMetadataProvider.CreateDefaultProvider());
+ provider.OnProvidersExecuting(context);
+
+ return context;
+ }
+
private static IServiceProvider GetServices()
{
var serviceCollection = new ServiceCollection();
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/AuthorizationPageApplicationModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/AuthorizationPageApplicationModelProviderTest.cs
index ecaad15494..85b51820df 100644
--- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/AuthorizationPageApplicationModelProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/AuthorizationPageApplicationModelProviderTest.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Authorization;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Options;
using Xunit;
@@ -160,7 +161,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private static PageApplicationModelProviderContext GetApplicationProviderContext(TypeInfo typeInfo)
{
- var defaultProvider = new DefaultPageApplicationModelProvider();
+ var defaultProvider = new DefaultPageApplicationModelProvider(
+ TestModelMetadataProvider.CreateDefaultProvider(),
+ Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = true }));
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
defaultProvider.OnProvidersExecuting(context);
return context;
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageApplicationModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageApplicationModelProviderTest.cs
index 196cf7f2eb..99aeded947 100644
--- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageApplicationModelProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageApplicationModelProviderTest.cs
@@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
+using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
@@ -21,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_ThrowsIfPageDoesNotDeriveFromValidBaseType()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(InvalidPageWithWrongBaseClass).GetTypeInfo();
var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@@ -62,7 +63,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_ThrowsIfModelPropertyDoesNotExistOnPage()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageWithoutModelProperty).GetTypeInfo();
var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@@ -85,7 +86,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_ThrowsIfModelPropertyIsNotPublic()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageWithNonVisibleModel).GetTypeInfo();
var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@@ -110,7 +111,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_ThrowsIfModelPropertyIsStatic()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageWithStaticModel).GetTypeInfo();
var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@@ -135,7 +136,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversPropertiesFromPage_IfModelTypeDoesNotHaveAttribute()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageWithModelWithoutPageModelAttribute).GetTypeInfo();
var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@@ -189,7 +190,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversPropertiesFromPageModel_IfModelHasAttribute()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageWithModelWithPageModelAttribute).GetTypeInfo();
var modelType = typeof(ModelWithPageModelAttribute);
var descriptor = new PageActionDescriptor();
@@ -233,7 +234,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversProperties_FromAllSubTypesThatDeclaresBindProperty()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(BindPropertyAttributeOnBaseModelPage).GetTypeInfo();
var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@@ -287,7 +288,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversHandlersFromPage()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageWithModelWithoutHandlers).GetTypeInfo();
var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@@ -329,7 +330,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversPropertiesFromModel()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageWithModel).GetTypeInfo();
var modelType = typeof(TestPageModel);
var descriptor = new PageActionDescriptor();
@@ -363,7 +364,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversBindingInfoFromHandler()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageWithBindPropertyModel).GetTypeInfo();
var modelType = typeof(ModelWithBindProperty);
var descriptor = new PageActionDescriptor();
@@ -410,7 +411,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversHandlersFromModel()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageWithModel).GetTypeInfo();
var modelType = typeof(TestPageModel);
var descriptor = new PageActionDescriptor();
@@ -438,7 +439,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_EmptyPage()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(EmptyPage).GetTypeInfo();
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
@@ -459,7 +460,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_EmptyPageModel()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(EmptyPageWithPageModel).GetTypeInfo();
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
@@ -528,7 +529,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void CreateDescriptor_FindsHandlerMethod_OnModel()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageWithHandlerThatGetsIgnored).GetTypeInfo();
var modelType = typeof(ModelWithHandler);
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
@@ -575,7 +576,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_FindsHandlerMethodOnPage_WhenModelIsNotAnnotatedWithPageModelAttribute()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageWithHandler).GetTypeInfo();
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
@@ -626,7 +627,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerMethods_DiscoversHandlersFromBaseType()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(InheritsMethods).GetTypeInfo();
var baseType = typeof(TestSetPageModel);
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@@ -677,7 +678,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerMethods_IgnoresNonPublicMethods()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(ProtectedModel).GetTypeInfo();
var baseType = typeof(TestSetPageModel);
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@@ -705,7 +706,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerMethods_IgnoreGenericTypeParameters()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(GenericClassModel).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@@ -728,7 +729,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerMethods_IgnoresStaticMethods()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageModelWithStaticHandler).GetTypeInfo();
var expected = typeInfo.GetMethod(nameof(PageModelWithStaticHandler.OnGet), BindingFlags.Public | BindingFlags.Instance);
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@@ -758,7 +759,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerMethods_IgnoresAbstractMethods()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageModelWithAbstractMethod).GetTypeInfo();
var expected = typeInfo.GetMethod(nameof(PageModelWithAbstractMethod.OnGet), BindingFlags.Public | BindingFlags.Instance);
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@@ -786,7 +787,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerMethods_IgnoresMethodWithNonHandlerAttribute()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageWithNonHandlerMethod).GetTypeInfo();
var expected = typeInfo.GetMethod(nameof(PageWithNonHandlerMethod.OnGet), BindingFlags.Public | BindingFlags.Instance);
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@@ -817,7 +818,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void CreateHandlerModel_ParsesMethod()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(PageModelWithHandlerNames).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@@ -851,7 +852,42 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void CreateHandlerMethods_AddsParameterDescriptors()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
+ var typeInfo = typeof(PageWithHandlerParameters).GetTypeInfo();
+ var expected = typeInfo.GetMethod(nameof(PageWithHandlerParameters.OnPost));
+ var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
+
+ // Act
+ provider.PopulateHandlerMethods(pageModel);
+
+ // Assert
+ var handlerMethods = pageModel.HandlerMethods;
+ var handler = Assert.Single(handlerMethods);
+
+ Assert.Collection(
+ handler.Parameters,
+ p =>
+ {
+ Assert.NotNull(p.ParameterInfo);
+ Assert.Equal(typeof(string), p.ParameterInfo.ParameterType);
+ Assert.Equal("name", p.ParameterName);
+ },
+ p =>
+ {
+ Assert.NotNull(p.ParameterInfo);
+ Assert.Equal(typeof(int), p.ParameterInfo.ParameterType);
+ Assert.Equal("id", p.ParameterName);
+ Assert.Equal("personId", p.BindingInfo.BinderModelName);
+ });
+ }
+
+ [Fact]
+ public void CreateHandlerMethods_WithLegacyValidationBehavior_AddsParameterDescriptors()
+ {
+ // Arrange
+ var provider = new DefaultPageApplicationModelProvider(
+ TestModelMetadataProvider.CreateDefaultProvider(),
+ Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = false }));
var typeInfo = typeof(PageWithHandlerParameters).GetTypeInfo();
var expected = typeInfo.GetMethod(nameof(PageWithHandlerParameters.OnPost));
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@@ -895,7 +931,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerProperties_UsesPropertyHelpers_ToFindProperties()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(HidesAProperty).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@@ -928,7 +964,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerProperties_SupportsGet_OnProperty()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(ModelSupportsGetOnProperty).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@@ -1039,7 +1075,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateFilters_AddsIFilterMetadataAttributesToModel()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(FilterModel).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true));
@@ -1063,7 +1099,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateFilters_AddsPageHandlerPageFilter_IfPageImplementsIAsyncPageFilter()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(ModelImplementingAsyncPageFilter).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true));
@@ -1093,7 +1129,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateFilters_AddsPageHandlerPageFilter_IfPageImplementsIPageFilter()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(ModelImplementingPageFilter).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true));
@@ -1128,7 +1164,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateFilters_AddsPageHandlerPageFilter_ForModelDerivingFromTypeImplementingPageFilter()
{
// Arrange
- var provider = new DefaultPageApplicationModelProvider();
+ var provider = CreateProvider();
var typeInfo = typeof(DerivedFromPageModel).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true));
@@ -1144,5 +1180,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
[ServiceFilter(typeof(IServiceProvider))]
private class DerivedFromPageModel : PageModel { }
+
+ private static DefaultPageApplicationModelProvider CreateProvider()
+ {
+ return new DefaultPageApplicationModelProvider(
+ TestModelMetadataProvider.CreateDefaultProvider(),
+ Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = true }));
+ }
}
}
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/ResponseCacheFilterApplicationModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/ResponseCacheFilterApplicationModelProviderTest.cs
index f2ccc1ad94..cf6acab03f 100644
--- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/ResponseCacheFilterApplicationModelProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/ResponseCacheFilterApplicationModelProviderTest.cs
@@ -6,6 +6,7 @@ using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
@@ -136,7 +137,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private static PageApplicationModelProviderContext GetApplicationProviderContext(TypeInfo typeInfo)
{
- var defaultProvider = new DefaultPageApplicationModelProvider();
+ var defaultProvider = new DefaultPageApplicationModelProvider(
+ TestModelMetadataProvider.CreateDefaultProvider(),
+ Options.Create(new MvcOptions()));
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
defaultProvider.OnProvidersExecuting(context);
return context;
diff --git a/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs b/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs
index df3b617c95..eb9f5434a3 100644
--- a/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs
@@ -183,6 +183,12 @@ namespace Microsoft.AspNetCore.Mvc
Assert.Equal(BindingSource.FormFile, formCollectionParameter.BindingSource);
},
provider =>
+ {
+ var formFileParameter = Assert.IsType(provider);
+ Assert.Equal(typeof(IFormFileCollection), formFileParameter.Type);
+ Assert.Equal(BindingSource.FormFile, formFileParameter.BindingSource);
+ },
+ provider =>
{
var excludeFilter = Assert.IsType(provider);
Assert.Equal(typeof(Type), excludeFilter.Type);
@@ -208,6 +214,11 @@ namespace Microsoft.AspNetCore.Mvc
Assert.Equal(typeof(IFormCollection), excludeFilter.Type);
},
provider =>
+ {
+ var excludeFilter = Assert.IsType(provider);
+ Assert.Equal(typeof(IFormFileCollection), excludeFilter.Type);
+ },
+ provider =>
{
var excludeFilter = Assert.IsType(provider);
Assert.Equal(typeof(Stream), excludeFilter.Type);
diff --git a/test/Microsoft.AspNetCore.Mvc.TestCommon/TestModelMetadataProvider.cs b/test/Microsoft.AspNetCore.Mvc.TestCommon/TestModelMetadataProvider.cs
index 62952ab4c6..73fe0c4c6b 100644
--- a/test/Microsoft.AspNetCore.Mvc.TestCommon/TestModelMetadataProvider.cs
+++ b/test/Microsoft.AspNetCore.Mvc.TestCommon/TestModelMetadataProvider.cs
@@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// Creates a provider with all the defaults - includes data annotations
public static ModelMetadataProvider CreateDefaultProvider(IStringLocalizerFactory stringLocalizerFactory = null)
{
- var detailsProviders = new IMetadataDetailsProvider[]
+ var detailsProviders = new List
{
new DefaultBindingMetadataProvider(),
new DefaultValidationMetadataProvider(),
@@ -29,6 +29,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
new DataMemberRequiredBindingMetadataProvider(),
};
+ MvcCoreMvcOptionsSetup.ConfigureAdditionalModelMetadataDetailsProvider(detailsProviders);
+
var compositeDetailsProvider = new DefaultCompositeMetadataDetailsProvider(detailsProviders);
return new DefaultModelMetadataProvider(compositeDetailsProvider, Options.Create(new MvcOptions()));
}
@@ -45,6 +47,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
new DataMemberRequiredBindingMetadataProvider(),
};
+ MvcCoreMvcOptionsSetup.ConfigureAdditionalModelMetadataDetailsProvider(detailsProviders);
+
detailsProviders.AddRange(providers);
var compositeDetailsProvider = new DefaultCompositeMetadataDetailsProvider(detailsProviders);
diff --git a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/TempDataApplicationModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/TempDataApplicationModelProviderTest.cs
index 459c398b9d..fb6ec71dc1 100644
--- a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/TempDataApplicationModelProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/TempDataApplicationModelProviderTest.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Internal;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Options;
using Xunit;
@@ -20,7 +21,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
var type = typeof(TestController_NoTempDataProperties);
var options = Options.Create(new MvcViewOptions());
var provider = new TempDataApplicationModelProvider(options);
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
+ var defaultProvider = new DefaultApplicationModelProvider(
+ Options.Create(new MvcOptions()),
+ TestModelMetadataProvider.CreateDefaultProvider());
var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
@@ -41,7 +44,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
var options = Options.Create(new MvcViewOptions());
var provider = new TempDataApplicationModelProvider(options);
var expected = $"The '{type.FullName}.Test' property with TempDataAttribute is invalid. A property using TempDataAttribute must have a public getter and setter.";
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
+ var defaultProvider = new DefaultApplicationModelProvider(
+ Options.Create(new MvcOptions()),
+ TestModelMetadataProvider.CreateDefaultProvider());
var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
@@ -58,7 +63,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
var type = typeof(TestController_NullableNonPrimitiveTempDataProperty);
var options = Options.Create(new MvcViewOptions());
var provider = new TempDataApplicationModelProvider(options);
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
+ var defaultProvider = new DefaultApplicationModelProvider(
+ Options.Create(new MvcOptions()),
+ TestModelMetadataProvider.CreateDefaultProvider());
var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
@@ -78,7 +85,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
var expected = typeof(TestController_OneTempDataProperty).GetProperty(nameof(TestController_OneTempDataProperty.Test2));
var options = Options.Create(new MvcViewOptions());
var provider = new TempDataApplicationModelProvider(options);
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
+ var defaultProvider = new DefaultApplicationModelProvider(
+ Options.Create(new MvcOptions()),
+ TestModelMetadataProvider.CreateDefaultProvider());
var context = new ApplicationModelProviderContext(new[] { typeof(TestController_OneTempDataProperty).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
@@ -102,7 +111,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
var expected = typeof(TestController_OneTempDataProperty).GetProperty(nameof(TestController_OneTempDataProperty.Test2));
var options = Options.Create(new MvcViewOptions { SuppressTempDataAttributePrefix = true });
var provider = new TempDataApplicationModelProvider(options);
- var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
+ var defaultProvider = new DefaultApplicationModelProvider(
+ Options.Create(new MvcOptions()),
+ TestModelMetadataProvider.CreateDefaultProvider());
var context = new ApplicationModelProviderContext(new[] { typeof(TestController_OneTempDataProperty).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
diff --git a/test/Microsoft.AspNetCore.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs b/test/Microsoft.AspNetCore.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs
index 315a4568bc..fea8184296 100644
--- a/test/Microsoft.AspNetCore.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs
@@ -379,22 +379,18 @@ namespace System.Web.Http
var setup = new WebApiCompatShimOptionsSetup();
setup.Configure(options);
- var optionsAccessor = new Mock>();
- optionsAccessor
- .SetupGet(o => o.Value)
- .Returns(options);
-
var authorizationOptionsAccessor = new Mock>();
authorizationOptionsAccessor
.SetupGet(o => o.Value)
.Returns(new AuthorizationOptions());
- var modelProvider = new DefaultApplicationModelProvider(optionsAccessor.Object);
+ var optionsAccessor = Options.Create(options);
+ var modelProvider = new DefaultApplicationModelProvider(optionsAccessor, TestModelMetadataProvider.CreateDefaultProvider());
var provider = new ControllerActionDescriptorProvider(
manager,
new[] { modelProvider },
- optionsAccessor.Object);
+ optionsAccessor);
return provider;
}
diff --git a/test/WebSites/BasicWebSite/Controllers/ContactApiController.cs b/test/WebSites/BasicWebSite/Controllers/ContactApiController.cs
index d2141e6dd5..e4c443954c 100644
--- a/test/WebSites/BasicWebSite/Controllers/ContactApiController.cs
+++ b/test/WebSites/BasicWebSite/Controllers/ContactApiController.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using BasicWebSite.Models;
using Microsoft.AspNetCore.Mvc;
@@ -51,6 +52,10 @@ namespace BasicWebSite
[HttpPost("ActionWithInferredFromBodyParameter")]
public ActionResult ActionWithInferredFromBodyParameter(Contact contact) => contact;
+ [HttpPost(nameof(ActionWithInferredFromBodyParameterAndCancellationToken))]
+ public ActionResult ActionWithInferredFromBodyParameterAndCancellationToken(Contact contact, CancellationToken cts)
+ => contact;
+
[HttpPost("ActionWithInferredRouteAndQueryParameters/{name}/{id}")]
public ActionResult ActionWithInferredRouteAndQueryParameter(int id, string name, string email)
{
@@ -68,4 +73,4 @@ namespace BasicWebSite
return contact;
}
}
-}
\ No newline at end of file
+}