Merge pull request #7590 from aspnet/release/2.1

Release/2.1
This commit is contained in:
Pranav K 2018-04-02 09:49:14 -07:00 committed by GitHub
commit c7559e1ff4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 787 additions and 219 deletions

View File

@ -65,6 +65,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// <summary> /// <summary>
/// Constructs a new instance of <see cref="BindingInfo"/> from the given <paramref name="attributes"/>. /// Constructs a new instance of <see cref="BindingInfo"/> from the given <paramref name="attributes"/>.
/// <para>
/// This overload does not account for <see cref="BindingInfo"/> specified via <see cref="ModelMetadata"/>. Consider using
/// <see cref="GetBindingInfo(IEnumerable{object}, ModelMetadata)"/> overload, or <see cref="TryApplyBindingInfo(ModelMetadata)"/>
/// on the result of this method to to get a more accurate <see cref="BindingInfo"/> instance.
/// </para>
/// </summary> /// </summary>
/// <param name="attributes">A collection of attributes which are used to construct <see cref="BindingInfo"/> /// <param name="attributes">A collection of attributes which are used to construct <see cref="BindingInfo"/>
/// </param> /// </param>
@ -134,6 +139,81 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
return isBindingInfoPresent ? bindingInfo : null; return isBindingInfoPresent ? bindingInfo : null;
} }
/// <summary>
/// Constructs a new instance of <see cref="BindingInfo"/> from the given <paramref name="attributes"/> and <paramref name="modelMetadata"/>.
/// </summary>
/// <param name="attributes">A collection of attributes which are used to construct <see cref="BindingInfo"/>.</param>
/// <param name="modelMetadata">The <see cref="ModelMetadata"/>.</param>
/// <returns>A new instance of <see cref="BindingInfo"/> if any binding metadata was discovered; otherwise or <see langword="null"/>.</returns>
public static BindingInfo GetBindingInfo(IEnumerable<object> 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;
}
/// <summary>
/// Applies binding metadata from the specified <paramref name="modelMetadata"/>.
/// <para>
/// Uses values from <paramref name="modelMetadata"/> if no value is already available.
/// </para>
/// </summary>
/// <param name="modelMetadata">The <see cref="ModelMetadata"/>.</param>
/// <returns><see langword="true"/> if any binding metadata from <paramref name="modelMetadata"/> was applied;
/// <see langword="false"/> otherwise.</returns>
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 class CompositePropertyFilterProvider : IPropertyFilterProvider
{ {
private readonly IEnumerable<IPropertyFilterProvider> _providers; private readonly IEnumerable<IPropertyFilterProvider> _providers;

View File

@ -97,7 +97,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
isFromRequest: false); isFromRequest: false);
/// <summary> /// <summary>
/// A <see cref="BindingSource"/> for <see cref="IFormFile"/> and <see cref="IFormCollection"/>. /// A <see cref="BindingSource"/> for <see cref="IFormFile"/>, <see cref="IFormCollection"/>, and <see cref="IFormFileCollection"/>.
/// </summary> /// </summary>
public static readonly BindingSource FormFile = new BindingSource( public static readonly BindingSource FormFile = new BindingSource(
"FormFile", "FormFile",

View File

@ -618,8 +618,8 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
return new ApiParameterDescriptionContext return new ApiParameterDescriptionContext
{ {
ModelMetadata = metadata, ModelMetadata = metadata,
BinderModelName = bindingInfo?.BinderModelName ?? metadata.BinderModelName, BinderModelName = bindingInfo?.BinderModelName,
BindingSource = bindingInfo?.BindingSource ?? metadata.BindingSource, BindingSource = bindingInfo?.BindingSource,
PropertyName = propertyName ?? metadata.Name, PropertyName = propertyName ?? metadata.Name,
}; };
} }
@ -716,9 +716,11 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
{ {
var propertyMetadata = modelMetadata.Properties[i]; var propertyMetadata = modelMetadata.Properties[i];
var key = new PropertyKey(propertyMetadata, source); var key = new PropertyKey(propertyMetadata, source);
var bindingInfo = BindingInfo.GetBindingInfo(Enumerable.Empty<object>(), propertyMetadata);
var propertyContext = ApiParameterDescriptionContext.GetContext( var propertyContext = ApiParameterDescriptionContext.GetContext(
propertyMetadata, propertyMetadata,
bindingInfo: null, bindingInfo: bindingInfo,
propertyName: null); propertyName: null);
if (Visited.Add(key)) if (Visited.Add(key))

View File

@ -139,7 +139,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
actionModel.Filters.Add(_modelStateInvalidFilter); actionModel.Filters.Add(_modelStateInvalidFilter);
} }
private void InferParameterBindingSources(ActionModel actionModel) // Internal for unit testing
internal void InferParameterBindingSources(ActionModel actionModel)
{ {
if (_modelMetadataProvider == null || _apiBehaviorOptions.SuppressInferBindingSourcesForParameters) if (_modelMetadataProvider == null || _apiBehaviorOptions.SuppressInferBindingSourcesForParameters)
{ {

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ActionConstraints; using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
@ -19,11 +18,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{ {
public class DefaultApplicationModelProvider : IApplicationModelProvider public class DefaultApplicationModelProvider : IApplicationModelProvider
{ {
private readonly ICollection<IFilterMetadata> _globalFilters; private readonly MvcOptions _mvcOptions;
private readonly IModelMetadataProvider _modelMetadataProvider;
public DefaultApplicationModelProvider(IOptions<MvcOptions> mvcOptionsAccessor) public DefaultApplicationModelProvider(
IOptions<MvcOptions> mvcOptionsAccessor,
IModelMetadataProvider modelMetadataProvider)
{ {
_globalFilters = mvcOptionsAccessor.Value.Filters; _mvcOptions = mvcOptionsAccessor.Value;
_modelMetadataProvider = modelMetadataProvider;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -37,7 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
foreach (var filter in _globalFilters) foreach (var filter in _mvcOptions.Filters)
{ {
context.Result.Filters.Add(filter); context.Result.Filters.Add(filter);
} }
@ -118,9 +121,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
do do
{ {
routeAttributes = currentTypeInfo routeAttributes = currentTypeInfo
.GetCustomAttributes(inherit: false) .GetCustomAttributes(inherit: false)
.OfType<IRouteTemplateProvider>() .OfType<IRouteTemplateProvider>()
.ToArray(); .ToArray();
if (routeAttributes.Length > 0) if (routeAttributes.Length > 0)
{ {
@ -213,24 +216,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
throw new ArgumentNullException(nameof(propertyInfo)); throw new ArgumentNullException(nameof(propertyInfo));
} }
// CoreCLR returns IEnumerable<Attribute> from GetCustomAttributes - the OfType<object>
// is needed to so that the result of ToArray() is object
var attributes = propertyInfo.GetCustomAttributes(inherit: true); 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; return propertyModel;
} }
@ -433,25 +429,25 @@ namespace Microsoft.AspNetCore.Mvc.Internal
throw new ArgumentNullException(nameof(parameterInfo)); throw new ArgumentNullException(nameof(parameterInfo));
} }
// CoreCLR returns IEnumerable<Attribute> from GetCustomAttributes - the OfType<object>
// is needed to so that the result of ToArray() is object
var attributes = parameterInfo.GetCustomAttributes(inherit: true); var attributes = parameterInfo.GetCustomAttributes(inherit: true);
var parameterModel = new ParameterModel(parameterInfo, attributes);
var bindingInfo = BindingInfo.GetBindingInfo(attributes); BindingInfo bindingInfo;
if (bindingInfo != null) 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 // GetMetadataForParameter should only be used if the user has opted in to the 2.1 behavior.
{ bindingInfo = BindingInfo.GetBindingInfo(attributes);
BindingSource = BindingSource.FormFile,
};
} }
parameterModel.ParameterName = parameterInfo.Name; var parameterModel = new ParameterModel(parameterInfo, attributes)
{
ParameterName = parameterInfo.Name,
BindingInfo = bindingInfo,
};
return parameterModel; return parameterModel;
} }
@ -670,12 +666,5 @@ namespace Microsoft.AspNetCore.Mvc.Internal
list.Add(item); list.Add(item);
} }
} }
private static bool IsFormFileType(Type parameterType)
{
return parameterType == typeof(IFormFile) ||
parameterType == typeof(IFormFileCollection) ||
typeof(IEnumerable<IFormFile>).IsAssignableFrom(parameterType);
}
} }
} }

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -76,28 +77,34 @@ namespace Microsoft.AspNetCore.Mvc.Internal
options.ValueProviderFactories.Add(new JQueryFormValueProviderFactory()); options.ValueProviderFactories.Add(new JQueryFormValueProviderFactory());
// Set up metadata providers // Set up metadata providers
ConfigureAdditionalModelMetadataDetailsProvider(options.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.
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));
// Set up validators // Set up validators
options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider()); options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider());
}
internal static void ConfigureAdditionalModelMetadataDetailsProvider(IList<IMetadataDetailsProvider> 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 // Add types to be excluded from Validation
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Type))); modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Type)));
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Uri))); modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Uri)));
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(CancellationToken))); modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(CancellationToken)));
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IFormFile))); modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IFormFile)));
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IFormCollection))); modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IFormCollection)));
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Stream))); modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IFormFileCollection)));
modelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Stream)));
} }
} }
} }

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.Extensions.Internal; using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;

View File

@ -233,14 +233,18 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{ {
_factory = factory; _factory = factory;
Metadata = factoryContext.Metadata; Metadata = factoryContext.Metadata;
BindingInfo = new BindingInfo BindingInfo bindingInfo;
if (factoryContext.BindingInfo != null)
{ {
BinderModelName = factoryContext.BindingInfo?.BinderModelName ?? Metadata.BinderModelName, bindingInfo = new BindingInfo(factoryContext.BindingInfo);
BinderType = factoryContext.BindingInfo?.BinderType ?? Metadata.BinderType, }
BindingSource = factoryContext.BindingInfo?.BindingSource ?? Metadata.BindingSource, else
PropertyFilterProvider = {
factoryContext.BindingInfo?.PropertyFilterProvider ?? Metadata.PropertyFilterProvider, bindingInfo = new BindingInfo();
}; }
bindingInfo.TryApplyBindingInfo(Metadata);
BindingInfo = bindingInfo;
MetadataProvider = _factory._metadataProvider; MetadataProvider = _factory._metadataProvider;
Visited = new Dictionary<Key, IModelBinder>(); Visited = new Dictionary<Key, IModelBinder>();

View File

@ -310,10 +310,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// This is to avoid adding validation errors for an 'empty' prefix when a simple // 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 // type fails to bind. The fix for #7503 uncovered this issue, and was likely the
// original problem being worked around that regressed #7503. // original problem being worked around that regressed #7503.
string modelName = modelBindingContext.ModelName; var modelName = modelBindingContext.ModelName;
if (string.IsNullOrEmpty(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 // 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. // and we ended up with an empty prefix.

View File

@ -5,5 +5,6 @@ using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.Formatters;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Core.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Core.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.TestCommon, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
[assembly: TypeForwardedTo(typeof(InputFormatterException))] [assembly: TypeForwardedTo(typeof(InputFormatterException))]

View File

@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.Extensions.Internal; using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{ {
@ -17,6 +18,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private const string ModelPropertyName = "Model"; private const string ModelPropertyName = "Model";
private readonly PageHandlerPageFilter _pageHandlerPageFilter = new PageHandlerPageFilter(); private readonly PageHandlerPageFilter _pageHandlerPageFilter = new PageHandlerPageFilter();
private readonly PageHandlerResultFilter _pageHandlerResultFilter = new PageHandlerResultFilter(); private readonly PageHandlerResultFilter _pageHandlerResultFilter = new PageHandlerResultFilter();
private readonly IModelMetadataProvider _modelMetadataProvider;
private readonly MvcOptions _options;
public DefaultPageApplicationModelProvider(
IModelMetadataProvider modelMetadataProvider,
IOptions<MvcOptions> options)
{
_modelMetadataProvider = modelMetadataProvider;
_options = options.Value;
}
/// <inheritdoc /> /// <inheritdoc />
public int Order => -1000; public int Order => -1000;
@ -217,9 +228,22 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
throw new ArgumentNullException(nameof(parameter)); 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, ParameterName = parameter.Name,
}; };
} }
@ -237,12 +261,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
} }
var propertyAttributes = property.GetCustomAttributes(inherit: true); 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. var propertyMetadata = _modelMetadataProvider.GetMetadataForProperty(property.DeclaringType, property.Name);
// This allows BindProperty attributes on handlers to apply to properties. var bindingInfo = BindingInfo.GetBindingInfo(propertyAttributes, propertyMetadata);
var bindingInfo = BindingInfo.GetBindingInfo(propertyAttributes) ??
BindingInfo.GetBindingInfo(handlerAttributes); 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) var model = new PagePropertyModel(property, propertyAttributes)
{ {

View File

@ -6,6 +6,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc\Microsoft.AspNetCore.Mvc.csproj" /> <ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc\Microsoft.AspNetCore.Mvc.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.TestCommon\Microsoft.AspNetCore.Mvc.TestCommon.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" /> <PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
</ItemGroup> </ItemGroup>

View File

@ -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<object>(), null, null);
var propertyFilterProvider = Mock.Of<IPropertyFilterProvider>();
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);
}
}
}

View File

@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -790,7 +791,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
var manager = GetApplicationManager(controllerTypes); var manager = GetApplicationManager(controllerTypes);
var modelProvider = new DefaultApplicationModelProvider(options); var modelProvider = new DefaultApplicationModelProvider(options, TestModelMetadataProvider.CreateDefaultProvider());
var provider = new ControllerActionDescriptorProvider( var provider = new ControllerActionDescriptorProvider(
manager, manager,
@ -964,8 +965,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
{ {
foreach (var item in context.Results) foreach (var item in context.Results)
{ {
var marker = item.Metadata as BooleanConstraintMarker; if (item.Metadata is BooleanConstraintMarker marker)
if (marker != null)
{ {
Assert.Null(item.Constraint); Assert.Null(item.Constraint);
item.Constraint = new BooleanConstraint() { Pass = marker.Pass }; item.Constraint = new BooleanConstraint() { Pass = marker.Pass };

View File

@ -5,6 +5,7 @@ using System;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Infrastructure;
@ -383,6 +384,31 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Assert.Same(BindingSource.Body, result); Assert.Same(BindingSource.Body, result);
} }
[Fact]
public void InferParameterBindingSources_SetsCorrectBindingSourceForComplexTypesWithCancellationToken()
{
// Arrange
var actionName = nameof(ParameterBindingController.ComplexTypeModelWithCancellationToken);
// 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);
// Assert
var model = GetParameterModel<TestModel>(actionModel);
Assert.Same(BindingSource.Body, model.BindingInfo.BindingSource);
var cancellationToken = GetParameterModel<CancellationToken>(actionModel);
Assert.Same(BindingSource.Special, cancellationToken.BindingInfo.BindingSource);
}
[Fact] [Fact]
public void InferBindingSourceForParameter_ReturnsBodyForSimpleTypes() public void InferBindingSourceForParameter_ReturnsBodyForSimpleTypes()
{ {
@ -503,16 +529,21 @@ namespace Microsoft.AspNetCore.Mvc.Internal
}; };
var optionsAccessor = Options.Create(options); var optionsAccessor = Options.Create(options);
modelMetadataProvider = modelMetadataProvider ?? new TestModelMetadataProvider();
var loggerFactory = NullLoggerFactory.Instance; var loggerFactory = NullLoggerFactory.Instance;
modelMetadataProvider = modelMetadataProvider ?? new EmptyModelMetadataProvider();
return new ApiBehaviorApplicationModelProvider(optionsAccessor, modelMetadataProvider, loggerFactory); 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() }); 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; return context;
} }
@ -535,6 +566,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
return Assert.Single(action.Parameters); return Assert.Single(action.Parameters);
} }
private static ParameterModel GetParameterModel<T>(ActionModel action)
{
return Assert.Single(action.Parameters.Where(x => typeof(T).IsAssignableFrom(x.ParameterType)));
}
[ApiController] [ApiController]
[Route("TestApi")] [Route("TestApi")]
private class TestApiController : Controller private class TestApiController : Controller
@ -612,6 +648,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
[HttpPost] [HttpPost]
[Consumes("application/json")] [Consumes("application/json")]
public IActionResult ActionWithConsumesAttribute([FromForm] string parameter) => null; public IActionResult ActionWithConsumesAttribute([FromForm] string parameter) => null;
[HttpPut("cancellation")]
public IActionResult ComplexTypeModelWithCancellationToken(TestModel model, CancellationToken cancellationToken) => null;
} }
[ApiController] [ApiController]

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure; using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Moq; using Moq;
using Xunit; using Xunit;
@ -22,10 +23,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{ {
// Arrange // Arrange
var provider = new AuthorizationApplicationModelProvider(new DefaultAuthorizationPolicyProvider(Options.Create(new AuthorizationOptions()))); var provider = new AuthorizationApplicationModelProvider(new DefaultAuthorizationPolicyProvider(Options.Create(new AuthorizationOptions())));
var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions())); var controllerType = typeof(AccountController);
var context = CreateProviderContext(controllerType);
var context = new ApplicationModelProviderContext(new[] { typeof(AccountController).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
// Act // Act
provider.OnProvidersExecuting(context); provider.OnProvidersExecuting(context);
@ -44,10 +43,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
options.Value.AddPolicy("Derived", policy => policy.RequireClaim("Derived")); options.Value.AddPolicy("Derived", policy => policy.RequireClaim("Derived"));
var provider = new AuthorizationApplicationModelProvider(new DefaultAuthorizationPolicyProvider(options)); var provider = new AuthorizationApplicationModelProvider(new DefaultAuthorizationPolicyProvider(options));
var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions())); var context = CreateProviderContext(typeof(DerivedController));
var context = new ApplicationModelProviderContext(new[] { typeof(DerivedController).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
// Act // Act
provider.OnProvidersExecuting(context); provider.OnProvidersExecuting(context);
@ -71,10 +67,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{ {
// Arrange // Arrange
var provider = new AuthorizationApplicationModelProvider(new DefaultAuthorizationPolicyProvider(Options.Create(new AuthorizationOptions()))); var provider = new AuthorizationApplicationModelProvider(new DefaultAuthorizationPolicyProvider(Options.Create(new AuthorizationOptions())));
var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions())); var context = CreateProviderContext(typeof(AnonymousController));
var context = new ApplicationModelProviderContext(new[] { typeof(AnonymousController).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
// Act // Act
provider.OnProvidersExecuting(context); provider.OnProvidersExecuting(context);
@ -100,10 +93,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var policyProvider = new DefaultAuthorizationPolicyProvider(authOptions); var policyProvider = new DefaultAuthorizationPolicyProvider(authOptions);
var provider = new AuthorizationApplicationModelProvider(policyProvider); var provider = new AuthorizationApplicationModelProvider(policyProvider);
var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions())); var context = CreateProviderContext(typeof(BaseController));
// Act // Act
var action = GetBaseControllerActionModel(provider, defaultProvider); var action = GetBaseControllerActionModel(provider);
// Assert // Assert
var authorizationFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(action.Filters)); var authorizationFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(action.Filters));
@ -128,10 +121,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
.Verifiable(); .Verifiable();
var provider = new AuthorizationApplicationModelProvider(authorizationPolicyProviderMock.Object); var provider = new AuthorizationApplicationModelProvider(authorizationPolicyProviderMock.Object);
var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
// Act // Act
var action = GetBaseControllerActionModel(provider, defaultProvider); var action = GetBaseControllerActionModel(provider);
// Assert // Assert
var actionFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(action.Filters)); var actionFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(action.Filters));
@ -148,10 +140,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultAuthorizationPolicyProvider( new DefaultAuthorizationPolicyProvider(
Options.Create(new AuthorizationOptions()) Options.Create(new AuthorizationOptions())
)); ));
var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions())); var context = CreateProviderContext(typeof(NoAuthController));
var context = new ApplicationModelProviderContext(new[] { typeof(NoAuthController).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
// Act // Act
provider.OnProvidersExecuting(context); provider.OnProvidersExecuting(context);
@ -163,16 +152,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Assert.Empty(action.Filters); Assert.Empty(action.Filters);
} }
private ActionModel GetBaseControllerActionModel( private ActionModel GetBaseControllerActionModel(AuthorizationApplicationModelProvider authorizationApplicationModelProvider)
IApplicationModelProvider authorizationApplicationModelProvider,
IApplicationModelProvider applicationModelProvider)
{ {
var context = new ApplicationModelProviderContext(new[] { typeof(BaseController).GetTypeInfo() }); var context = CreateProviderContext(typeof(BaseController));
applicationModelProvider.OnProvidersExecuting(context);
var authorizeData = new List<IAuthorizeData>
{
new AuthorizeAttribute("POLICY")
};
authorizationApplicationModelProvider.OnProvidersExecuting(context); authorizationApplicationModelProvider.OnProvidersExecuting(context);
@ -183,6 +165,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
return action; 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 private class BaseController
{ {
[Authorize(Policy = "Base")] [Authorize(Policy = "Base")]

View File

@ -709,7 +709,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var manager = GetApplicationManager(new[] { controllerTypeInfo }); var manager = GetApplicationManager(new[] { controllerTypeInfo });
var options = Options.Create(new MvcOptions()); var options = Options.Create(new MvcOptions());
options.Value.Conventions.Add(new TestRoutingConvention()); options.Value.Conventions.Add(new TestRoutingConvention());
var modelProvider = new DefaultApplicationModelProvider(options); var modelProvider = new DefaultApplicationModelProvider(options, TestModelMetadataProvider.CreateDefaultProvider());
var provider = new ControllerActionDescriptorProvider( var provider = new ControllerActionDescriptorProvider(
manager, manager,
new[] { modelProvider }, new[] { modelProvider },
@ -1397,7 +1397,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var manager = GetApplicationManager(new[] { controllerTypeInfo }); var manager = GetApplicationManager(new[] { controllerTypeInfo });
var modelProvider = new DefaultApplicationModelProvider(options); var modelProvider = new DefaultApplicationModelProvider(options, TestModelMetadataProvider.CreateDefaultProvider());
var provider = new ControllerActionDescriptorProvider( var provider = new ControllerActionDescriptorProvider(
manager, manager,
@ -1413,7 +1413,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var options = Options.Create(new MvcOptions()); var options = Options.Create(new MvcOptions());
var manager = GetApplicationManager(controllerTypeInfos); var manager = GetApplicationManager(controllerTypeInfos);
var modelProvider = new DefaultApplicationModelProvider(options); var modelProvider = new DefaultApplicationModelProvider(options, TestModelMetadataProvider.CreateDefaultProvider());
var provider = new ControllerActionDescriptorProvider( var provider = new ControllerActionDescriptorProvider(
manager, manager,
@ -1432,7 +1432,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var manager = GetApplicationManager(new[] { controllerTypeInfo }); var manager = GetApplicationManager(new[] { controllerTypeInfo });
var modelProvider = new DefaultApplicationModelProvider(options); var modelProvider = new DefaultApplicationModelProvider(options, TestModelMetadataProvider.CreateDefaultProvider());
var provider = new ControllerActionDescriptorProvider( var provider = new ControllerActionDescriptorProvider(
manager, manager,

View File

@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Xunit; 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<FromQueryAttribute>(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] [Fact]
public void OnProvidersExecuting_AddsBindingSources_ForActionParameters() public void OnProvidersExecuting_AddsBindingSources_ForActionParameters()
{ {
@ -88,7 +132,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert // Assert
var controllerModel = Assert.Single(context.Result.Controllers); 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( Assert.Collection(
action.Parameters, action.Parameters,
parameter => 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<FromQueryAttribute>(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, // This class has a filter attribute, but doesn't implement any filter interfaces,
// so ControllerFilter is not present. // so ControllerFilter is not present.
[Fact] [Fact]
@ -1363,6 +1508,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public IFormFile FormFile { get; set; } public IFormFile FormFile { get; set; }
public IActionResult PostAction([FromQuery] string fromQuery, IFormFileCollection formFileCollection, string unbound) => null; 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 public class SomeFiltersController : IAsyncActionFilter, IResultFilter
@ -1404,13 +1553,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private class TestApplicationModelProvider : DefaultApplicationModelProvider private class TestApplicationModelProvider : DefaultApplicationModelProvider
{ {
public TestApplicationModelProvider() public TestApplicationModelProvider()
: this(Options.Create(new MvcOptions())) : this(Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = true }), TestModelMetadataProvider.CreateDefaultProvider())
{ {
} }
public TestApplicationModelProvider( public TestApplicationModelProvider(
IOptions<MvcOptions> options) IOptions<MvcOptions> options,
: base(options) IModelMetadataProvider modelMetadataProvider)
: base(options, modelMetadataProvider)
{ {
} }

View File

@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Moq; using Moq;
@ -23,10 +24,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{ {
// Arrange // Arrange
var corsProvider = new CorsApplicationModelProvider(); var corsProvider = new CorsApplicationModelProvider();
var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions())); var context = GetProviderContext(typeof(CorsController));
var context = new ApplicationModelProviderContext(new [] { typeof(CorsController).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
// Act // Act
corsProvider.OnProvidersExecuting(context); corsProvider.OnProvidersExecuting(context);
@ -45,10 +43,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{ {
// Arrange // Arrange
var corsProvider = new CorsApplicationModelProvider(); var corsProvider = new CorsApplicationModelProvider();
var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions())); var context = GetProviderContext(typeof(DisableCorsController));
var context = new ApplicationModelProviderContext(new[] { typeof(DisableCorsController).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
// Act // Act
corsProvider.OnProvidersExecuting(context); corsProvider.OnProvidersExecuting(context);
@ -67,10 +62,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{ {
// Arrange // Arrange
var corsProvider = new CorsApplicationModelProvider(); var corsProvider = new CorsApplicationModelProvider();
var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions())); var context = GetProviderContext(typeof(CustomCorsFilterController));
var context = new ApplicationModelProviderContext(new[] { typeof(CustomCorsFilterController).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
// Act // Act
corsProvider.OnProvidersExecuting(context); corsProvider.OnProvidersExecuting(context);
@ -88,10 +80,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{ {
// Arrange // Arrange
var corsProvider = new CorsApplicationModelProvider(); var corsProvider = new CorsApplicationModelProvider();
var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions())); var context = GetProviderContext(typeof(EnableCorsController));
var context = new ApplicationModelProviderContext(new[] { typeof(EnableCorsController).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
// Act // Act
corsProvider.OnProvidersExecuting(context); corsProvider.OnProvidersExecuting(context);
@ -110,10 +99,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{ {
// Arrange // Arrange
var corsProvider = new CorsApplicationModelProvider(); var corsProvider = new CorsApplicationModelProvider();
var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions())); var context = GetProviderContext(typeof(DisableCorsActionController));
var context = new ApplicationModelProviderContext(new[] { typeof(DisableCorsActionController).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
// Act // Act
corsProvider.OnProvidersExecuting(context); corsProvider.OnProvidersExecuting(context);
@ -132,10 +118,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{ {
// Arrange // Arrange
var corsProvider = new CorsApplicationModelProvider(); var corsProvider = new CorsApplicationModelProvider();
var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions())); var context = GetProviderContext(typeof(CustomCorsFilterOnActionController));
var context = new ApplicationModelProviderContext(new[] { typeof(CustomCorsFilterOnActionController).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context);
// Act // Act
corsProvider.OnProvidersExecuting(context); corsProvider.OnProvidersExecuting(context);
@ -153,12 +136,10 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{ {
// Arrange // Arrange
var corsProvider = new CorsApplicationModelProvider(); 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( context.Result.Filters.Add(
new CorsAuthorizationFilter(Mock.Of<ICorsService>(), Mock.Of<ICorsPolicyProvider>(), Mock.Of<ILoggerFactory>())); new CorsAuthorizationFilter(Mock.Of<ICorsService>(), Mock.Of<ICorsPolicyProvider>(), Mock.Of<ILoggerFactory>()));
defaultProvider.OnProvidersExecuting(context);
// Act // Act
corsProvider.OnProvidersExecuting(context); corsProvider.OnProvidersExecuting(context);
@ -176,11 +157,8 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{ {
// Arrange // Arrange
var corsProvider = new CorsApplicationModelProvider(); 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 DisableCorsAuthorizationFilter()); context.Result.Filters.Add(new DisableCorsAuthorizationFilter());
defaultProvider.OnProvidersExecuting(context);
// Act // Act
corsProvider.OnProvidersExecuting(context); corsProvider.OnProvidersExecuting(context);
@ -198,11 +176,8 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{ {
// Arrange // Arrange
var corsProvider = new CorsApplicationModelProvider(); 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 CustomCorsFilterAttribute()); context.Result.Filters.Add(new CustomCorsFilterAttribute());
defaultProvider.OnProvidersExecuting(context);
// Act // Act
corsProvider.OnProvidersExecuting(context); corsProvider.OnProvidersExecuting(context);
@ -220,10 +195,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
{ {
// Arrange // Arrange
var corsProvider = new CorsApplicationModelProvider(); 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() });
defaultProvider.OnProvidersExecuting(context);
// Act // Act
corsProvider.OnProvidersExecuting(context); corsProvider.OnProvidersExecuting(context);
@ -236,6 +208,17 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal
Assert.IsNotType<CorsHttpMethodActionConstraint>(constraint); Assert.IsNotType<CorsHttpMethodActionConstraint>(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 private class EnableCorsController
{ {
[EnableCors("policy")] [EnableCors("policy")]

View File

@ -88,7 +88,14 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
} }
[Fact] [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 // Arrange
var input = new Contact var input = new Contact
@ -98,7 +105,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
}; };
// Act // Act
var response = await Client.PostAsJsonAsync("/contact/ActionWithInferredFromBodyParameter", input); var response = await Client.PostAsJsonAsync($"/contact/{action}", input);
// Assert // Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);

View File

@ -13,11 +13,12 @@ using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Mvc.IntegrationTests namespace Microsoft.AspNetCore.Mvc.IntegrationTests
@ -31,13 +32,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
public async Task AuthorizeFilter_CalledTwiceWithNonDefaultProvider() public async Task AuthorizeFilter_CalledTwiceWithNonDefaultProvider()
{ {
// Arrange // Arrange
var applicationModelProviderContext = new ApplicationModelProviderContext( var applicationModelProviderContext = GetProviderContext(typeof(AuthorizeController));
new[] { typeof(AuthorizeController).GetTypeInfo() });
var policyProvider = new TestAuthorizationPolicyProvider(); var policyProvider = new TestAuthorizationPolicyProvider();
var defaultProvider = new DefaultApplicationModelProvider(Options.Create(new MvcOptions()));
defaultProvider.OnProvidersExecuting(applicationModelProviderContext);
var controller = Assert.Single(applicationModelProviderContext.Result.Controllers); var controller = Assert.Single(applicationModelProviderContext.Result.Controllers);
var action = Assert.Single(controller.Actions); var action = Assert.Single(controller.Actions);
@ -64,6 +61,17 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
return httpContext; 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() private static IServiceProvider GetServices()
{ {
var serviceCollection = new ServiceCollection(); var serviceCollection = new ServiceCollection();

View File

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Xunit; using Xunit;
@ -160,7 +161,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private static PageApplicationModelProviderContext GetApplicationProviderContext(TypeInfo typeInfo) 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); var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
defaultProvider.OnProvidersExecuting(context); defaultProvider.OnProvidersExecuting(context);
return context; return context;

View File

@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.Extensions.Options;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
@ -21,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_ThrowsIfPageDoesNotDeriveFromValidBaseType() public void OnProvidersExecuting_ThrowsIfPageDoesNotDeriveFromValidBaseType()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(InvalidPageWithWrongBaseClass).GetTypeInfo(); var typeInfo = typeof(InvalidPageWithWrongBaseClass).GetTypeInfo();
var descriptor = new PageActionDescriptor(); var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo); var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@ -62,7 +63,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_ThrowsIfModelPropertyDoesNotExistOnPage() public void OnProvidersExecuting_ThrowsIfModelPropertyDoesNotExistOnPage()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageWithoutModelProperty).GetTypeInfo(); var typeInfo = typeof(PageWithoutModelProperty).GetTypeInfo();
var descriptor = new PageActionDescriptor(); var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo); var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@ -85,7 +86,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_ThrowsIfModelPropertyIsNotPublic() public void OnProvidersExecuting_ThrowsIfModelPropertyIsNotPublic()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageWithNonVisibleModel).GetTypeInfo(); var typeInfo = typeof(PageWithNonVisibleModel).GetTypeInfo();
var descriptor = new PageActionDescriptor(); var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo); var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@ -110,7 +111,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_ThrowsIfModelPropertyIsStatic() public void OnProvidersExecuting_ThrowsIfModelPropertyIsStatic()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageWithStaticModel).GetTypeInfo(); var typeInfo = typeof(PageWithStaticModel).GetTypeInfo();
var descriptor = new PageActionDescriptor(); var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo); var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@ -135,7 +136,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversPropertiesFromPage_IfModelTypeDoesNotHaveAttribute() public void OnProvidersExecuting_DiscoversPropertiesFromPage_IfModelTypeDoesNotHaveAttribute()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageWithModelWithoutPageModelAttribute).GetTypeInfo(); var typeInfo = typeof(PageWithModelWithoutPageModelAttribute).GetTypeInfo();
var descriptor = new PageActionDescriptor(); var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo); var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@ -189,7 +190,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversPropertiesFromPageModel_IfModelHasAttribute() public void OnProvidersExecuting_DiscoversPropertiesFromPageModel_IfModelHasAttribute()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageWithModelWithPageModelAttribute).GetTypeInfo(); var typeInfo = typeof(PageWithModelWithPageModelAttribute).GetTypeInfo();
var modelType = typeof(ModelWithPageModelAttribute); var modelType = typeof(ModelWithPageModelAttribute);
var descriptor = new PageActionDescriptor(); var descriptor = new PageActionDescriptor();
@ -233,7 +234,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversProperties_FromAllSubTypesThatDeclaresBindProperty() public void OnProvidersExecuting_DiscoversProperties_FromAllSubTypesThatDeclaresBindProperty()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(BindPropertyAttributeOnBaseModelPage).GetTypeInfo(); var typeInfo = typeof(BindPropertyAttributeOnBaseModelPage).GetTypeInfo();
var descriptor = new PageActionDescriptor(); var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo); var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@ -287,7 +288,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversHandlersFromPage() public void OnProvidersExecuting_DiscoversHandlersFromPage()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageWithModelWithoutHandlers).GetTypeInfo(); var typeInfo = typeof(PageWithModelWithoutHandlers).GetTypeInfo();
var descriptor = new PageActionDescriptor(); var descriptor = new PageActionDescriptor();
var context = new PageApplicationModelProviderContext(descriptor, typeInfo); var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
@ -329,7 +330,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversPropertiesFromModel() public void OnProvidersExecuting_DiscoversPropertiesFromModel()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageWithModel).GetTypeInfo(); var typeInfo = typeof(PageWithModel).GetTypeInfo();
var modelType = typeof(TestPageModel); var modelType = typeof(TestPageModel);
var descriptor = new PageActionDescriptor(); var descriptor = new PageActionDescriptor();
@ -363,7 +364,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversBindingInfoFromHandler() public void OnProvidersExecuting_DiscoversBindingInfoFromHandler()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageWithBindPropertyModel).GetTypeInfo(); var typeInfo = typeof(PageWithBindPropertyModel).GetTypeInfo();
var modelType = typeof(ModelWithBindProperty); var modelType = typeof(ModelWithBindProperty);
var descriptor = new PageActionDescriptor(); var descriptor = new PageActionDescriptor();
@ -410,7 +411,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_DiscoversHandlersFromModel() public void OnProvidersExecuting_DiscoversHandlersFromModel()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageWithModel).GetTypeInfo(); var typeInfo = typeof(PageWithModel).GetTypeInfo();
var modelType = typeof(TestPageModel); var modelType = typeof(TestPageModel);
var descriptor = new PageActionDescriptor(); var descriptor = new PageActionDescriptor();
@ -438,7 +439,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_EmptyPage() public void OnProvidersExecuting_EmptyPage()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(EmptyPage).GetTypeInfo(); var typeInfo = typeof(EmptyPage).GetTypeInfo();
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo); var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
@ -459,7 +460,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_EmptyPageModel() public void OnProvidersExecuting_EmptyPageModel()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(EmptyPageWithPageModel).GetTypeInfo(); var typeInfo = typeof(EmptyPageWithPageModel).GetTypeInfo();
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo); var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
@ -528,7 +529,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void CreateDescriptor_FindsHandlerMethod_OnModel() public void CreateDescriptor_FindsHandlerMethod_OnModel()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageWithHandlerThatGetsIgnored).GetTypeInfo(); var typeInfo = typeof(PageWithHandlerThatGetsIgnored).GetTypeInfo();
var modelType = typeof(ModelWithHandler); var modelType = typeof(ModelWithHandler);
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo); var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
@ -575,7 +576,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnProvidersExecuting_FindsHandlerMethodOnPage_WhenModelIsNotAnnotatedWithPageModelAttribute() public void OnProvidersExecuting_FindsHandlerMethodOnPage_WhenModelIsNotAnnotatedWithPageModelAttribute()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageWithHandler).GetTypeInfo(); var typeInfo = typeof(PageWithHandler).GetTypeInfo();
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo); var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
@ -626,7 +627,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerMethods_DiscoversHandlersFromBaseType() public void PopulateHandlerMethods_DiscoversHandlersFromBaseType()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(InheritsMethods).GetTypeInfo(); var typeInfo = typeof(InheritsMethods).GetTypeInfo();
var baseType = typeof(TestSetPageModel); var baseType = typeof(TestSetPageModel);
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]); var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@ -677,7 +678,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerMethods_IgnoresNonPublicMethods() public void PopulateHandlerMethods_IgnoresNonPublicMethods()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(ProtectedModel).GetTypeInfo(); var typeInfo = typeof(ProtectedModel).GetTypeInfo();
var baseType = typeof(TestSetPageModel); var baseType = typeof(TestSetPageModel);
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]); var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@ -705,7 +706,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerMethods_IgnoreGenericTypeParameters() public void PopulateHandlerMethods_IgnoreGenericTypeParameters()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(GenericClassModel).GetTypeInfo(); var typeInfo = typeof(GenericClassModel).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]); var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@ -728,7 +729,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerMethods_IgnoresStaticMethods() public void PopulateHandlerMethods_IgnoresStaticMethods()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageModelWithStaticHandler).GetTypeInfo(); var typeInfo = typeof(PageModelWithStaticHandler).GetTypeInfo();
var expected = typeInfo.GetMethod(nameof(PageModelWithStaticHandler.OnGet), BindingFlags.Public | BindingFlags.Instance); var expected = typeInfo.GetMethod(nameof(PageModelWithStaticHandler.OnGet), BindingFlags.Public | BindingFlags.Instance);
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]); var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@ -758,7 +759,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerMethods_IgnoresAbstractMethods() public void PopulateHandlerMethods_IgnoresAbstractMethods()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageModelWithAbstractMethod).GetTypeInfo(); var typeInfo = typeof(PageModelWithAbstractMethod).GetTypeInfo();
var expected = typeInfo.GetMethod(nameof(PageModelWithAbstractMethod.OnGet), BindingFlags.Public | BindingFlags.Instance); var expected = typeInfo.GetMethod(nameof(PageModelWithAbstractMethod.OnGet), BindingFlags.Public | BindingFlags.Instance);
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]); var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@ -786,7 +787,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateHandlerMethods_IgnoresMethodWithNonHandlerAttribute() public void PopulateHandlerMethods_IgnoresMethodWithNonHandlerAttribute()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageWithNonHandlerMethod).GetTypeInfo(); var typeInfo = typeof(PageWithNonHandlerMethod).GetTypeInfo();
var expected = typeInfo.GetMethod(nameof(PageWithNonHandlerMethod.OnGet), BindingFlags.Public | BindingFlags.Instance); var expected = typeInfo.GetMethod(nameof(PageWithNonHandlerMethod.OnGet), BindingFlags.Public | BindingFlags.Instance);
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]); var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@ -817,7 +818,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void CreateHandlerModel_ParsesMethod() public void CreateHandlerModel_ParsesMethod()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(PageModelWithHandlerNames).GetTypeInfo(); var typeInfo = typeof(PageModelWithHandlerNames).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]); var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@ -851,7 +852,42 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void CreateHandlerMethods_AddsParameterDescriptors() public void CreateHandlerMethods_AddsParameterDescriptors()
{ {
// Arrange // 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 typeInfo = typeof(PageWithHandlerParameters).GetTypeInfo();
var expected = typeInfo.GetMethod(nameof(PageWithHandlerParameters.OnPost)); var expected = typeInfo.GetMethod(nameof(PageWithHandlerParameters.OnPost));
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]); 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() public void PopulateHandlerProperties_UsesPropertyHelpers_ToFindProperties()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(HidesAProperty).GetTypeInfo(); var typeInfo = typeof(HidesAProperty).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]); 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() public void PopulateHandlerProperties_SupportsGet_OnProperty()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(ModelSupportsGetOnProperty).GetTypeInfo(); var typeInfo = typeof(ModelSupportsGetOnProperty).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]); var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, new object[0]);
@ -1039,7 +1075,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void PopulateFilters_AddsIFilterMetadataAttributesToModel() public void PopulateFilters_AddsIFilterMetadataAttributesToModel()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(FilterModel).GetTypeInfo(); var typeInfo = typeof(FilterModel).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true)); 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() public void PopulateFilters_AddsPageHandlerPageFilter_IfPageImplementsIAsyncPageFilter()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(ModelImplementingAsyncPageFilter).GetTypeInfo(); var typeInfo = typeof(ModelImplementingAsyncPageFilter).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true)); 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() public void PopulateFilters_AddsPageHandlerPageFilter_IfPageImplementsIPageFilter()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(ModelImplementingPageFilter).GetTypeInfo(); var typeInfo = typeof(ModelImplementingPageFilter).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true)); 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() public void PopulateFilters_AddsPageHandlerPageFilter_ForModelDerivingFromTypeImplementingPageFilter()
{ {
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider(); var provider = CreateProvider();
var typeInfo = typeof(DerivedFromPageModel).GetTypeInfo(); var typeInfo = typeof(DerivedFromPageModel).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true)); var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true));
@ -1144,5 +1180,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
[ServiceFilter(typeof(IServiceProvider))] [ServiceFilter(typeof(IServiceProvider))]
private class DerivedFromPageModel : PageModel { } private class DerivedFromPageModel : PageModel { }
private static DefaultPageApplicationModelProvider CreateProvider()
{
return new DefaultPageApplicationModelProvider(
TestModelMetadataProvider.CreateDefaultProvider(),
Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = true }));
}
} }
} }

View File

@ -6,6 +6,7 @@ using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Moq; using Moq;
@ -136,7 +137,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private static PageApplicationModelProviderContext GetApplicationProviderContext(TypeInfo typeInfo) 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); var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
defaultProvider.OnProvidersExecuting(context); defaultProvider.OnProvidersExecuting(context);
return context; return context;

View File

@ -183,6 +183,12 @@ namespace Microsoft.AspNetCore.Mvc
Assert.Equal(BindingSource.FormFile, formCollectionParameter.BindingSource); Assert.Equal(BindingSource.FormFile, formCollectionParameter.BindingSource);
}, },
provider => provider =>
{
var formFileParameter = Assert.IsType<BindingSourceMetadataProvider>(provider);
Assert.Equal(typeof(IFormFileCollection), formFileParameter.Type);
Assert.Equal(BindingSource.FormFile, formFileParameter.BindingSource);
},
provider =>
{ {
var excludeFilter = Assert.IsType<SuppressChildValidationMetadataProvider>(provider); var excludeFilter = Assert.IsType<SuppressChildValidationMetadataProvider>(provider);
Assert.Equal(typeof(Type), excludeFilter.Type); Assert.Equal(typeof(Type), excludeFilter.Type);
@ -208,6 +214,11 @@ namespace Microsoft.AspNetCore.Mvc
Assert.Equal(typeof(IFormCollection), excludeFilter.Type); Assert.Equal(typeof(IFormCollection), excludeFilter.Type);
}, },
provider => provider =>
{
var excludeFilter = Assert.IsType<SuppressChildValidationMetadataProvider>(provider);
Assert.Equal(typeof(IFormFileCollection), excludeFilter.Type);
},
provider =>
{ {
var excludeFilter = Assert.IsType<SuppressChildValidationMetadataProvider>(provider); var excludeFilter = Assert.IsType<SuppressChildValidationMetadataProvider>(provider);
Assert.Equal(typeof(Stream), excludeFilter.Type); Assert.Equal(typeof(Stream), excludeFilter.Type);

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// Creates a provider with all the defaults - includes data annotations // Creates a provider with all the defaults - includes data annotations
public static ModelMetadataProvider CreateDefaultProvider(IStringLocalizerFactory stringLocalizerFactory = null) public static ModelMetadataProvider CreateDefaultProvider(IStringLocalizerFactory stringLocalizerFactory = null)
{ {
var detailsProviders = new IMetadataDetailsProvider[] var detailsProviders = new List<IMetadataDetailsProvider>
{ {
new DefaultBindingMetadataProvider(), new DefaultBindingMetadataProvider(),
new DefaultValidationMetadataProvider(), new DefaultValidationMetadataProvider(),
@ -29,6 +29,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
new DataMemberRequiredBindingMetadataProvider(), new DataMemberRequiredBindingMetadataProvider(),
}; };
MvcCoreMvcOptionsSetup.ConfigureAdditionalModelMetadataDetailsProvider(detailsProviders);
var compositeDetailsProvider = new DefaultCompositeMetadataDetailsProvider(detailsProviders); var compositeDetailsProvider = new DefaultCompositeMetadataDetailsProvider(detailsProviders);
return new DefaultModelMetadataProvider(compositeDetailsProvider, Options.Create(new MvcOptions())); return new DefaultModelMetadataProvider(compositeDetailsProvider, Options.Create(new MvcOptions()));
} }
@ -45,6 +47,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
new DataMemberRequiredBindingMetadataProvider(), new DataMemberRequiredBindingMetadataProvider(),
}; };
MvcCoreMvcOptionsSetup.ConfigureAdditionalModelMetadataDetailsProvider(detailsProviders);
detailsProviders.AddRange(providers); detailsProviders.AddRange(providers);
var compositeDetailsProvider = new DefaultCompositeMetadataDetailsProvider(detailsProviders); var compositeDetailsProvider = new DefaultCompositeMetadataDetailsProvider(detailsProviders);

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Xunit; using Xunit;
@ -20,7 +21,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
var type = typeof(TestController_NoTempDataProperties); var type = typeof(TestController_NoTempDataProperties);
var options = Options.Create(new MvcViewOptions()); var options = Options.Create(new MvcViewOptions());
var provider = new TempDataApplicationModelProvider(options); 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() }); var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context); defaultProvider.OnProvidersExecuting(context);
@ -41,7 +44,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
var options = Options.Create(new MvcViewOptions()); var options = Options.Create(new MvcViewOptions());
var provider = new TempDataApplicationModelProvider(options); 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 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() }); var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context); defaultProvider.OnProvidersExecuting(context);
@ -58,7 +63,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
var type = typeof(TestController_NullableNonPrimitiveTempDataProperty); var type = typeof(TestController_NullableNonPrimitiveTempDataProperty);
var options = Options.Create(new MvcViewOptions()); var options = Options.Create(new MvcViewOptions());
var provider = new TempDataApplicationModelProvider(options); 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() }); var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context); defaultProvider.OnProvidersExecuting(context);
@ -78,7 +85,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
var expected = typeof(TestController_OneTempDataProperty).GetProperty(nameof(TestController_OneTempDataProperty.Test2)); var expected = typeof(TestController_OneTempDataProperty).GetProperty(nameof(TestController_OneTempDataProperty.Test2));
var options = Options.Create(new MvcViewOptions()); var options = Options.Create(new MvcViewOptions());
var provider = new TempDataApplicationModelProvider(options); 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() }); var context = new ApplicationModelProviderContext(new[] { typeof(TestController_OneTempDataProperty).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context); defaultProvider.OnProvidersExecuting(context);
@ -102,7 +111,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
var expected = typeof(TestController_OneTempDataProperty).GetProperty(nameof(TestController_OneTempDataProperty.Test2)); var expected = typeof(TestController_OneTempDataProperty).GetProperty(nameof(TestController_OneTempDataProperty.Test2));
var options = Options.Create(new MvcViewOptions { SuppressTempDataAttributePrefix = true }); var options = Options.Create(new MvcViewOptions { SuppressTempDataAttributePrefix = true });
var provider = new TempDataApplicationModelProvider(options); 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() }); var context = new ApplicationModelProviderContext(new[] { typeof(TestController_OneTempDataProperty).GetTypeInfo() });
defaultProvider.OnProvidersExecuting(context); defaultProvider.OnProvidersExecuting(context);

View File

@ -379,22 +379,18 @@ namespace System.Web.Http
var setup = new WebApiCompatShimOptionsSetup(); var setup = new WebApiCompatShimOptionsSetup();
setup.Configure(options); setup.Configure(options);
var optionsAccessor = new Mock<IOptions<MvcOptions>>();
optionsAccessor
.SetupGet(o => o.Value)
.Returns(options);
var authorizationOptionsAccessor = new Mock<IOptions<AuthorizationOptions>>(); var authorizationOptionsAccessor = new Mock<IOptions<AuthorizationOptions>>();
authorizationOptionsAccessor authorizationOptionsAccessor
.SetupGet(o => o.Value) .SetupGet(o => o.Value)
.Returns(new AuthorizationOptions()); .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( var provider = new ControllerActionDescriptorProvider(
manager, manager,
new[] { modelProvider }, new[] { modelProvider },
optionsAccessor.Object); optionsAccessor);
return provider; return provider;
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BasicWebSite.Models; using BasicWebSite.Models;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -51,6 +52,10 @@ namespace BasicWebSite
[HttpPost("ActionWithInferredFromBodyParameter")] [HttpPost("ActionWithInferredFromBodyParameter")]
public ActionResult<Contact> ActionWithInferredFromBodyParameter(Contact contact) => contact; public ActionResult<Contact> ActionWithInferredFromBodyParameter(Contact contact) => contact;
[HttpPost(nameof(ActionWithInferredFromBodyParameterAndCancellationToken))]
public ActionResult<Contact> ActionWithInferredFromBodyParameterAndCancellationToken(Contact contact, CancellationToken cts)
=> contact;
[HttpPost("ActionWithInferredRouteAndQueryParameters/{name}/{id}")] [HttpPost("ActionWithInferredRouteAndQueryParameters/{name}/{id}")]
public ActionResult<Contact> ActionWithInferredRouteAndQueryParameter(int id, string name, string email) public ActionResult<Contact> ActionWithInferredRouteAndQueryParameter(int id, string name, string email)
{ {
@ -68,4 +73,4 @@ namespace BasicWebSite
return contact; return contact;
} }
} }
} }