Remove many `CompatibilitySwitch<T>` properties (#4628)

- #7156 2 of 3
- will leave the `IEnumerable<ICompatibilitySwitch>` implementations to avoid `breakingchanges.netcore.json` churn
- will leave one `ConfigureCompatibilityOptions<MvcOptions>` subclass: `MvcOptionsConfigureCompatibilityOptions`
- a few options remain as regular properties:
  - `ApiBehaviorOptions.SuppressMapClientErrors` (default `false`)
  - `MvcOptions.EnableEndpointRouting` (default `true`)
  - `MvcOptions.MaxValidationDepth` (default `32`)
  - `MvcJsonOptions.AllowInputFormatterExceptionMessages` (default `true`)

nits:
- move `IsEffectivePolicy(...)` check earlier in `AuthorizeFilter`
- correct a typo or two
This commit is contained in:
Doug Bunting 2018-12-16 19:33:27 -08:00 committed by GitHub
parent 826f950530
commit 708dc5cb5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 244 additions and 1147 deletions

View File

@ -24,7 +24,6 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
public class DefaultApiDescriptionProvider : IApiDescriptionProvider public class DefaultApiDescriptionProvider : IApiDescriptionProvider
{ {
private readonly MvcOptions _mvcOptions; private readonly MvcOptions _mvcOptions;
private readonly IActionResultTypeMapper _mapper;
private readonly ApiResponseTypeProvider _responseTypeProvider; private readonly ApiResponseTypeProvider _responseTypeProvider;
private readonly RouteOptions _routeOptions; private readonly RouteOptions _routeOptions;
private readonly IInlineConstraintResolver _constraintResolver; private readonly IInlineConstraintResolver _constraintResolver;
@ -53,7 +52,8 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
/// <param name="constraintResolver">The <see cref="IInlineConstraintResolver"/> used for resolving inline /// <param name="constraintResolver">The <see cref="IInlineConstraintResolver"/> used for resolving inline
/// constraints.</param> /// constraints.</param>
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param> /// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="mapper"> The <see cref="IActionResultTypeMapper"/>.</param> /// <param name="mapper">The <see cref="IActionResultTypeMapper"/>.</param>
/// <remarks>The <paramref name="mapper"/> parameter is currently unused.</remarks>
[Obsolete("This constructor is obsolete and will be removed in a future release.")] [Obsolete("This constructor is obsolete and will be removed in a future release.")]
public DefaultApiDescriptionProvider( public DefaultApiDescriptionProvider(
IOptions<MvcOptions> optionsAccessor, IOptions<MvcOptions> optionsAccessor,
@ -64,7 +64,6 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
_mvcOptions = optionsAccessor.Value; _mvcOptions = optionsAccessor.Value;
_constraintResolver = constraintResolver; _constraintResolver = constraintResolver;
_modelMetadataProvider = modelMetadataProvider; _modelMetadataProvider = modelMetadataProvider;
_mapper = mapper;
_responseTypeProvider = new ApiResponseTypeProvider(modelMetadataProvider, mapper, _mvcOptions); _responseTypeProvider = new ApiResponseTypeProvider(modelMetadataProvider, mapper, _mvcOptions);
} }
@ -75,8 +74,9 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
/// <param name="constraintResolver">The <see cref="IInlineConstraintResolver"/> used for resolving inline /// <param name="constraintResolver">The <see cref="IInlineConstraintResolver"/> used for resolving inline
/// constraints.</param> /// constraints.</param>
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param> /// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="mapper"> The <see cref="IActionResultTypeMapper"/>.</param> /// <param name="mapper">The <see cref="IActionResultTypeMapper"/>.</param>
/// <param name="routeOptions">The accessor for <see cref="RouteOptions"/>.</param> /// <param name="routeOptions">The accessor for <see cref="RouteOptions"/>.</param>
/// <remarks>The <paramref name="mapper"/> parameter is currently unused.</remarks>
public DefaultApiDescriptionProvider( public DefaultApiDescriptionProvider(
IOptions<MvcOptions> optionsAccessor, IOptions<MvcOptions> optionsAccessor,
IInlineConstraintResolver constraintResolver, IInlineConstraintResolver constraintResolver,
@ -87,7 +87,6 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
_mvcOptions = optionsAccessor.Value; _mvcOptions = optionsAccessor.Value;
_constraintResolver = constraintResolver; _constraintResolver = constraintResolver;
_modelMetadataProvider = modelMetadataProvider; _modelMetadataProvider = modelMetadataProvider;
_mapper = mapper;
_responseTypeProvider = new ApiResponseTypeProvider(modelMetadataProvider, mapper, _mvcOptions); _responseTypeProvider = new ApiResponseTypeProvider(modelMetadataProvider, mapper, _mvcOptions);
_routeOptions = routeOptions.Value; _routeOptions = routeOptions.Value;
} }
@ -202,8 +201,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
var visitor = new PseudoModelBindingVisitor(context, actionParameter); var visitor = new PseudoModelBindingVisitor(context, actionParameter);
ModelMetadata metadata; ModelMetadata metadata;
if (_mvcOptions.AllowValidatingTopLevelNodes && if (actionParameter is ControllerParameterDescriptor controllerParameterDescriptor &&
actionParameter is ControllerParameterDescriptor controllerParameterDescriptor &&
_modelMetadataProvider is ModelMetadataProvider provider) _modelMetadataProvider is ModelMetadataProvider provider)
{ {
// The default model metadata provider derives from ModelMetadataProvider // The default model metadata provider derives from ModelMetadataProvider

View File

@ -15,29 +15,9 @@ namespace Microsoft.AspNetCore.Mvc
/// </summary> /// </summary>
public class ApiBehaviorOptions : IEnumerable<ICompatibilitySwitch> public class ApiBehaviorOptions : IEnumerable<ICompatibilitySwitch>
{ {
private readonly CompatibilitySwitch<bool> _suppressMapClientErrors; private readonly ICompatibilitySwitch[] _switches = Array.Empty<ICompatibilitySwitch>();
private readonly CompatibilitySwitch<bool> _suppressUseValidationProblemDetailsForInvalidModelStateResponses;
private readonly CompatibilitySwitch<bool> _allowInferringBindingSourceForCollectionTypesAsFromQuery;
private readonly ICompatibilitySwitch[] _switches;
private Func<ActionContext, IActionResult> _invalidModelStateResponseFactory; private Func<ActionContext, IActionResult> _invalidModelStateResponseFactory;
/// <summary>
/// Creates a new instance of <see cref="ApiBehaviorOptions"/>.
/// </summary>
public ApiBehaviorOptions()
{
_suppressMapClientErrors = new CompatibilitySwitch<bool>(nameof(SuppressMapClientErrors));
_suppressUseValidationProblemDetailsForInvalidModelStateResponses = new CompatibilitySwitch<bool>(nameof(SuppressUseValidationProblemDetailsForInvalidModelStateResponses));
_allowInferringBindingSourceForCollectionTypesAsFromQuery = new CompatibilitySwitch<bool>(nameof(AllowInferringBindingSourceForCollectionTypesAsFromQuery));
_switches = new[]
{
_suppressMapClientErrors,
_suppressUseValidationProblemDetailsForInvalidModelStateResponses,
_allowInferringBindingSourceForCollectionTypesAsFromQuery
};
}
/// <summary> /// <summary>
/// Delegate invoked on actions annotated with <see cref="ApiControllerAttribute"/> to convert invalid /// Delegate invoked on actions annotated with <see cref="ApiControllerAttribute"/> to convert invalid
/// <see cref="ModelStateDictionary"/> into an <see cref="IActionResult"/> /// <see cref="ModelStateDictionary"/> into an <see cref="IActionResult"/>
@ -92,47 +72,7 @@ namespace Microsoft.AspNetCore.Mvc
/// <value> /// <value>
/// The default value is <see langword="false"/>. /// The default value is <see langword="false"/>.
/// </value> /// </value>
public bool SuppressMapClientErrors public bool SuppressMapClientErrors { get; set; }
{
// Note: When compatibility switches are removed in 3.0, this property should be retained as a regular boolean property.
get => _suppressMapClientErrors.Value;
set => _suppressMapClientErrors.Value = value;
}
/// <summary>
/// Gets or sets a value that determines if controllers annotated with <see cref="ApiControllerAttribute"/> respond using
/// <see cref="ValidationProblemDetails"/> in <see cref="InvalidModelStateResponseFactory"/>.
/// <para>
/// When <see langword="true"/>, <see cref="SuppressModelStateInvalidFilter"/> returns errors in <see cref="ModelStateDictionary"/>
/// as a <see cref="ValidationProblemDetails"/>. Otherwise, <see cref="SuppressModelStateInvalidFilter"/> returns the errors
/// in the format determined by <see cref="SerializableError"/>.
/// </para>
/// </summary>
/// <value>
/// The default value is <see langword="false"/>.
/// </value>
public bool SuppressUseValidationProblemDetailsForInvalidModelStateResponses
{
get => _suppressUseValidationProblemDetailsForInvalidModelStateResponses.Value;
set => _suppressUseValidationProblemDetailsForInvalidModelStateResponses.Value = value;
}
/// <summary>
/// Gets or sets a value that determines if <see cref="BindingInfo.BindingSource"/> for collection types
/// (<see cref="ModelMetadata.IsCollectionType"/>).
/// <para>
/// When <see langword="true" />, the binding source for collection types is inferred as <see cref="BindingSource.Query"/>.
/// Otherwise <see cref="BindingSource.Body"/> is inferred.
/// </para>
/// </summary>
/// <value>
/// The default value is <see langword="false"/>.
/// </value>
public bool AllowInferringBindingSourceForCollectionTypesAsFromQuery
{
get => _allowInferringBindingSourceForCollectionTypesAsFromQuery.Value;
set => _allowInferringBindingSourceForCollectionTypesAsFromQuery.Value = value;
}
/// <summary> /// <summary>
/// Gets a map of HTTP status codes to <see cref="ClientErrorData"/>. Configured values /// Gets a map of HTTP status codes to <see cref="ClientErrorData"/>. Configured values
@ -142,8 +82,7 @@ namespace Microsoft.AspNetCore.Mvc
/// Use of this feature can be disabled by resetting <see cref="SuppressMapClientErrors"/>. /// Use of this feature can be disabled by resetting <see cref="SuppressMapClientErrors"/>.
/// </para> /// </para>
/// </summary> /// </summary>
public IDictionary<int, ClientErrorData> ClientErrorMapping { get; } = public IDictionary<int, ClientErrorData> ClientErrorMapping { get; } = new Dictionary<int, ClientErrorData>();
new Dictionary<int, ClientErrorData>();
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator() IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator()
{ {

View File

@ -49,12 +49,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
if (!options.SuppressInferBindingSourcesForParameters) if (!options.SuppressInferBindingSourcesForParameters)
{ {
var convention = new InferParameterBindingInfoConvention(modelMetadataProvider) ActionModelConventions.Add(new InferParameterBindingInfoConvention(modelMetadataProvider));
{
AllowInferringBindingSourceForCollectionTypesAsFromQuery = options.AllowInferringBindingSourceForCollectionTypesAsFromQuery,
};
ActionModelConventions.Add(convention);
} }
} }

View File

@ -454,7 +454,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
var attributes = parameterInfo.GetCustomAttributes(inherit: true); var attributes = parameterInfo.GetCustomAttributes(inherit: true);
BindingInfo bindingInfo; BindingInfo bindingInfo;
if (_mvcOptions.AllowValidatingTopLevelNodes && _modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase) if (_modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase)
{ {
var modelMetadata = modelMetadataProviderBase.GetMetadataForParameter(parameterInfo); var modelMetadata = modelMetadataProviderBase.GetMetadataForParameter(parameterInfo);
bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata); bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata);

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
/// An <see cref="IActionModelConvention"/> that infers <see cref="BindingInfo.BindingSource"/> for parameters. /// An <see cref="IActionModelConvention"/> that infers <see cref="BindingInfo.BindingSource"/> for parameters.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The goal of this covention is to make intuitive and easy to document <see cref="BindingSource"/> inferences. The rules are: /// The goal of this convention is to make intuitive and easy to document <see cref="BindingSource"/> inferences. The rules are:
/// <list type="number"> /// <list type="number">
/// <item>A previously specified <see cref="BindingInfo.BindingSource" /> is never overwritten.</item> /// <item>A previously specified <see cref="BindingInfo.BindingSource" /> is never overwritten.</item>
/// <item>A complex type parameter (<see cref="ModelMetadata.IsComplexType"/>) is assigned <see cref="BindingSource.Body"/>.</item> /// <item>A complex type parameter (<see cref="ModelMetadata.IsComplexType"/>) is assigned <see cref="BindingSource.Body"/>.</item>
@ -31,8 +31,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
_modelMetadataProvider = modelMetadataProvider ?? throw new ArgumentNullException(nameof(modelMetadataProvider)); _modelMetadataProvider = modelMetadataProvider ?? throw new ArgumentNullException(nameof(modelMetadataProvider));
} }
internal bool AllowInferringBindingSourceForCollectionTypesAsFromQuery { get; set; }
protected virtual bool ShouldApply(ActionModel action) => true; protected virtual bool ShouldApply(ActionModel action) => true;
public void Apply(ActionModel action) public void Apply(ActionModel action)
@ -118,13 +116,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
private bool IsComplexTypeParameter(ParameterModel parameter) private bool IsComplexTypeParameter(ParameterModel parameter)
{ {
// No need for information from attributes on the parameter. Just use its type. // No need for information from attributes on the parameter. Just use its type.
var metadata = _modelMetadataProvider var metadata = _modelMetadataProvider.GetMetadataForType(parameter.ParameterInfo.ParameterType);
.GetMetadataForType(parameter.ParameterInfo.ParameterType);
if (AllowInferringBindingSourceForCollectionTypesAsFromQuery && metadata.IsCollectionType)
{
return false;
}
return metadata.IsComplexType; return metadata.IsComplexType;
} }

View File

@ -12,7 +12,6 @@ using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Core; using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.Authorization namespace Microsoft.AspNetCore.Mvc.Authorization
{ {
@ -23,7 +22,6 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
/// </summary> /// </summary>
public class AuthorizeFilter : IAsyncAuthorizationFilter, IFilterFactory public class AuthorizeFilter : IAsyncAuthorizationFilter, IFilterFactory
{ {
private MvcOptions _mvcOptions;
private AuthorizationPolicy _effectivePolicy; private AuthorizationPolicy _effectivePolicy;
/// <summary> /// <summary>
@ -136,38 +134,25 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
var effectivePolicy = await ComputePolicyAsync(); var effectivePolicy = await ComputePolicyAsync();
var canCache = PolicyProvider == null; var canCache = PolicyProvider == null;
if (_mvcOptions == null) // Combine all authorize filters into single effective policy that's only run on the closest filter
var builder = new AuthorizationPolicyBuilder(effectivePolicy);
for (var i = 0; i < context.Filters.Count; i++)
{ {
_mvcOptions = context.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>().Value; if (ReferenceEquals(this, context.Filters[i]))
}
if (_mvcOptions.AllowCombiningAuthorizeFilters)
{
if (!context.IsEffectivePolicy(this))
{ {
return null; continue;
} }
// Combine all authorize filters into single effective policy that's only run on the closest filter if (context.Filters[i] is AuthorizeFilter authorizeFilter)
var builder = new AuthorizationPolicyBuilder(effectivePolicy);
for (var i = 0; i < context.Filters.Count; i++)
{ {
if (ReferenceEquals(this, context.Filters[i])) // Combine using the explicit policy, or the dynamic policy provider
{ builder.Combine(await authorizeFilter.ComputePolicyAsync());
continue; canCache = canCache && authorizeFilter.PolicyProvider == null;
}
if (context.Filters[i] is AuthorizeFilter authorizeFilter)
{
// Combine using the explicit policy, or the dynamic policy provider
builder.Combine(await authorizeFilter.ComputePolicyAsync());
canCache = canCache && authorizeFilter.PolicyProvider == null;
}
} }
effectivePolicy = builder?.Build() ?? effectivePolicy;
} }
effectivePolicy = builder?.Build() ?? effectivePolicy;
// We can cache the effective policy when there is no custom policy provider // We can cache the effective policy when there is no custom policy provider
if (canCache) if (canCache)
{ {
@ -185,6 +170,11 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
if (!context.IsEffectivePolicy(this))
{
return;
}
var effectivePolicy = await GetEffectivePolicyAsync(context); var effectivePolicy = await GetEffectivePolicyAsync(context);
if (effectivePolicy == null) if (effectivePolicy == null)
{ {

View File

@ -54,8 +54,6 @@ namespace Microsoft.AspNetCore.Mvc
/// <remarks> /// <remarks>
/// ASP.NET Core MVC 2.1 introduced compatibility switches for the following: /// ASP.NET Core MVC 2.1 introduced compatibility switches for the following:
/// <list type="bullet"> /// <list type="bullet">
/// <item><description><see cref="MvcOptions.AllowBindingHeaderValuesToNonStringModelTypes"/></description></item>
/// <item><description><see cref="MvcOptions.InputFormatterExceptionPolicy"/></description></item>
/// <item><description><see cref="MvcOptions.InputFormatterExceptionPolicy"/></description></item> /// <item><description><see cref="MvcOptions.InputFormatterExceptionPolicy"/></description></item>
/// <item><description><see cref="MvcOptions.SuppressBindingUndefinedValueToEnumType"/></description></item> /// <item><description><see cref="MvcOptions.SuppressBindingUndefinedValueToEnumType"/></description></item>
/// <item><description><c>MvcJsonOptions.AllowInputFormatterExceptionMessages</c></description></item> /// <item><description><c>MvcJsonOptions.AllowInputFormatterExceptionMessages</c></description></item>
@ -75,7 +73,6 @@ namespace Microsoft.AspNetCore.Mvc
/// ASP.NET Core MVC 2.2 introduced compatibility switches for the following: /// ASP.NET Core MVC 2.2 introduced compatibility switches for the following:
/// <list type="bullet"> /// <list type="bullet">
/// <item><description><c>ApiBehaviorOptions.SuppressMapClientErrors</c></description></item> /// <item><description><c>ApiBehaviorOptions.SuppressMapClientErrors</c></description></item>
/// <item><description><c>ApiBehaviorOptions.SuppressUseValidationProblemDetailsForInvalidModelStateResponses</c></description></item>
/// <item><description><c>MvcDataAnnotationsLocalizationOptions.AllowDataAnnotationsLocalizationForEnumDisplayAttributes</c></description></item> /// <item><description><c>MvcDataAnnotationsLocalizationOptions.AllowDataAnnotationsLocalizationForEnumDisplayAttributes</c></description></item>
/// <item><description><see cref="MvcOptions.EnableEndpointRouting" /></description></item> /// <item><description><see cref="MvcOptions.EnableEndpointRouting" /></description></item>
/// <item><description><see cref="MvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent"/></description></item> /// <item><description><see cref="MvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent"/></description></item>

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Microsoft.AspNetCore.Mvc.Controllers namespace Microsoft.AspNetCore.Mvc.Controllers
@ -134,8 +133,7 @@ namespace Microsoft.AspNetCore.Mvc.Controllers
var parameter = parameters[i]; var parameter = parameters[i];
ModelMetadata metadata; ModelMetadata metadata;
if (mvcOptions.AllowValidatingTopLevelNodes && if (modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase &&
modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase &&
parameter is ControllerParameterDescriptor controllerParameterDescriptor) parameter is ControllerParameterDescriptor controllerParameterDescriptor)
{ {
// The default model metadata provider derives from ModelMetadataProvider // The default model metadata provider derives from ModelMetadataProvider

View File

@ -2,37 +2,21 @@
// 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 Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Core; using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
{ {
internal class ApiBehaviorOptionsSetup : internal class ApiBehaviorOptionsSetup :
ConfigureCompatibilityOptions<ApiBehaviorOptions>, IConfigureOptions<ApiBehaviorOptions>,
IConfigureOptions<ApiBehaviorOptions> IPostConfigureOptions<ApiBehaviorOptions>
{ {
internal static readonly Func<ActionContext, IActionResult> DefaultFactory = DefaultInvalidModelStateResponse; internal static readonly Func<ActionContext, IActionResult> DefaultFactory = DefaultInvalidModelStateResponse;
internal static readonly Func<ActionContext, IActionResult> ProblemDetailsFactory = ProblemDetailsInvalidModelStateResponse; internal static readonly Func<ActionContext, IActionResult> ProblemDetailsFactory =
ProblemDetailsInvalidModelStateResponse;
public ApiBehaviorOptionsSetup(
ILoggerFactory loggerFactory,
IOptions<MvcCompatibilityOptions> compatibilityOptions)
: base(loggerFactory, compatibilityOptions)
{
}
protected override IReadOnlyDictionary<string, object> DefaultValues
{
get
{
return new Dictionary<string, object>();
}
}
public void Configure(ApiBehaviorOptions options) public void Configure(ApiBehaviorOptions options)
{ {
@ -45,11 +29,8 @@ namespace Microsoft.Extensions.DependencyInjection
ConfigureClientErrorMapping(options); ConfigureClientErrorMapping(options);
} }
public override void PostConfigure(string name, ApiBehaviorOptions options) public void PostConfigure(string name, ApiBehaviorOptions options)
{ {
// Let compatibility switches do their thing.
base.PostConfigure(name, options);
// We want to use problem details factory only if // We want to use problem details factory only if
// (a) it has not been opted out of (SuppressMapClientErrors = true) // (a) it has not been opted out of (SuppressMapClientErrors = true)
// (b) a different factory was configured // (b) a different factory was configured

View File

@ -49,20 +49,22 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
if (context.ActionContext.ActionDescriptor is ControllerActionDescriptor) if (context.ActionContext.ActionDescriptor is ControllerActionDescriptor)
{ {
var controllerContext = new ControllerContext(context.ActionContext); var controllerContext = new ControllerContext(context.ActionContext)
// PERF: These are rarely going to be changed, so let's go copy-on-write. {
controllerContext.ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories); // PERF: These are rarely going to be changed, so let's go copy-on-write.
ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories)
};
controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors; controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors;
var cacheResult = _controllerActionInvokerCache.GetCachedResult(controllerContext); var (cacheEntry, filters) = _controllerActionInvokerCache.GetCachedResult(controllerContext);
var invoker = new ControllerActionInvoker( var invoker = new ControllerActionInvoker(
_logger, _logger,
_diagnosticListener, _diagnosticListener,
_mapper, _mapper,
controllerContext, controllerContext,
cacheResult.cacheEntry, cacheEntry,
cacheResult.filters); filters);
context.Result = invoker; context.Result = invoker;
} }

View File

@ -23,16 +23,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
{ {
return new Dictionary<string, object> return new Dictionary<string, object>
{ {
[nameof(MvcOptions.AllowCombiningAuthorizeFilters)] = true,
[nameof(MvcOptions.AllowBindingHeaderValuesToNonStringModelTypes)] = true,
[nameof(MvcOptions.AllowValidatingTopLevelNodes)] = true,
[nameof(MvcOptions.InputFormatterExceptionPolicy)] = InputFormatterExceptionPolicy.MalformedInputExceptions, [nameof(MvcOptions.InputFormatterExceptionPolicy)] = InputFormatterExceptionPolicy.MalformedInputExceptions,
[nameof(MvcOptions.SuppressBindingUndefinedValueToEnumType)] = true, [nameof(MvcOptions.SuppressBindingUndefinedValueToEnumType)] = true,
[nameof(MvcOptions.EnableEndpointRouting)] = true,
// Matches JsonSerializerSettingsProvider.DefaultMaxDepth
[nameof(MvcOptions.MaxValidationDepth)] = 32,
[nameof(MvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent)] = true, [nameof(MvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent)] = true,
}; };
} }

View File

@ -38,10 +38,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
/// The <see cref="IModelBinder"/> for binding <typeparamref name="TElement"/>. /// The <see cref="IModelBinder"/> for binding <typeparamref name="TElement"/>.
/// </param> /// </param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
/// <remarks>
/// The binder will not add an error for an unbound top-level model even if
/// <see cref="ModelMetadata.IsBindingRequired"/> is <see langword="true"/>.
/// </remarks>
public ArrayModelBinder(IModelBinder elementBinder, ILoggerFactory loggerFactory) public ArrayModelBinder(IModelBinder elementBinder, ILoggerFactory loggerFactory)
: base(elementBinder, loggerFactory) : base(elementBinder, loggerFactory)
{ {
@ -59,11 +55,16 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
/// <see cref="ModelMetadata.IsBindingRequired"/> is <see langword="true"/> for a top-level model, the binder /// <see cref="ModelMetadata.IsBindingRequired"/> is <see langword="true"/> for a top-level model, the binder
/// adds a <see cref="ModelStateDictionary"/> error when the model is not bound. /// adds a <see cref="ModelStateDictionary"/> error when the model is not bound.
/// </param> /// </param>
/// <remarks>
/// The <paramref name="allowValidatingTopLevelNodes"/> parameter is currently ignored.
/// <see cref="CollectionModelBinder{TElement}.AllowValidatingTopLevelNodes"/> is always <see langword="true"/>
/// in <see cref="ArrayModelBinder{TElement}"/>.
/// </remarks>
public ArrayModelBinder( public ArrayModelBinder(
IModelBinder elementBinder, IModelBinder elementBinder,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
bool allowValidatingTopLevelNodes) bool allowValidatingTopLevelNodes)
: base(elementBinder, loggerFactory, allowValidatingTopLevelNodes) : base(elementBinder, loggerFactory, allowValidatingTopLevelNodes: true)
{ {
} }

View File

@ -4,7 +4,6 @@
using System; using System;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{ {
@ -28,12 +27,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
var binderType = typeof(ArrayModelBinder<>).MakeGenericType(elementType); var binderType = typeof(ArrayModelBinder<>).MakeGenericType(elementType);
var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>(); var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
var mvcOptions = context.Services.GetRequiredService<IOptions<MvcOptions>>().Value;
return (IModelBinder)Activator.CreateInstance( return (IModelBinder)Activator.CreateInstance(
binderType, binderType,
elementBinder, elementBinder,
loggerFactory, loggerFactory,
mvcOptions.AllowValidatingTopLevelNodes); true /* allowValidatingTopLevelNodes */);
} }
return null; return null;

View File

@ -42,12 +42,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
/// </summary> /// </summary>
/// <param name="elementBinder">The <see cref="IModelBinder"/> for binding elements.</param> /// <param name="elementBinder">The <see cref="IModelBinder"/> for binding elements.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
/// <remarks>
/// The binder will not add an error for an unbound top-level model even if
/// <see cref="ModelMetadata.IsBindingRequired"/> is <see langword="true"/>.
/// </remarks>
public CollectionModelBinder(IModelBinder elementBinder, ILoggerFactory loggerFactory) public CollectionModelBinder(IModelBinder elementBinder, ILoggerFactory loggerFactory)
: this(elementBinder, loggerFactory, allowValidatingTopLevelNodes: false) : this(elementBinder, loggerFactory, allowValidatingTopLevelNodes: true)
{ {
} }
@ -196,16 +192,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
/// </summary> /// </summary>
/// <param name="bindingContext">The <see cref="ModelBindingContext"/>.</param> /// <param name="bindingContext">The <see cref="ModelBindingContext"/>.</param>
/// <remarks> /// <remarks>
/// <para>
/// This method should be called only when <see cref="MvcOptions.AllowValidatingTopLevelNodes" /> is
/// <see langword="true" /> and a top-level model was not bound.
/// </para>
/// <para>
/// For back-compatibility reasons, <see cref="ModelBindingContext.Result" /> must have /// For back-compatibility reasons, <see cref="ModelBindingContext.Result" /> must have
/// <see cref="ModelBindingResult.IsModelSet" /> equal to <see langword="true" /> when a /// <see cref="ModelBindingResult.IsModelSet" /> equal to <see langword="true" /> when a
/// top-level model is not bound. Therefore, ParameterBinder can not detect a /// top-level model is not bound. Therefore, ParameterBinder can not detect a
/// <see cref="ModelMetadata.IsBindingRequired" /> failure for collections. Add the error here. /// <see cref="ModelMetadata.IsBindingRequired" /> failure for collections. Add the error here.
/// </para>
/// </remarks> /// </remarks>
protected void AddErrorIfBindingRequired(ModelBindingContext bindingContext) protected void AddErrorIfBindingRequired(ModelBindingContext bindingContext)
{ {

View File

@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
binderType, binderType,
elementBinder, elementBinder,
loggerFactory, loggerFactory,
mvcOptions.AllowValidatingTopLevelNodes); true /* allowValidatingTopLevelNodes */);
} }
// If the model type is IEnumerable<> then we need to know if we can assign a List<> to it, since // If the model type is IEnumerable<> then we need to know if we can assign a List<> to it, since
@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
binderType, binderType,
elementBinder, elementBinder,
loggerFactory, loggerFactory,
mvcOptions.AllowValidatingTopLevelNodes); true /* allowValidatingTopLevelNodes */);
} }
} }

View File

@ -44,14 +44,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
/// The <see cref="IDictionary{TKey, TValue}"/> of binders to use for binding properties. /// The <see cref="IDictionary{TKey, TValue}"/> of binders to use for binding properties.
/// </param> /// </param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
/// <remarks>
/// The binder will not add an error for an unbound top-level model even if
/// <see cref="ModelMetadata.IsBindingRequired"/> is <see langword="true"/>.
/// </remarks>
public ComplexTypeModelBinder( public ComplexTypeModelBinder(
IDictionary<ModelMetadata, IModelBinder> propertyBinders, IDictionary<ModelMetadata, IModelBinder> propertyBinders,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
: this(propertyBinders, loggerFactory, allowValidatingTopLevelNodes: false) : this(propertyBinders, loggerFactory, allowValidatingTopLevelNodes: true)
{ {
} }
@ -67,6 +63,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
/// <see cref="ModelMetadata.IsBindingRequired"/> is <see langword="true"/> for a top-level model, the binder /// <see cref="ModelMetadata.IsBindingRequired"/> is <see langword="true"/> for a top-level model, the binder
/// adds a <see cref="ModelStateDictionary"/> error when the model is not bound. /// adds a <see cref="ModelStateDictionary"/> error when the model is not bound.
/// </param> /// </param>
/// <remarks>The <paramref name="allowValidatingTopLevelNodes"/> parameter is currently ignored.</remarks>
public ComplexTypeModelBinder( public ComplexTypeModelBinder(
IDictionary<ModelMetadata, IModelBinder> propertyBinders, IDictionary<ModelMetadata, IModelBinder> propertyBinders,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
@ -84,13 +81,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
_propertyBinders = propertyBinders; _propertyBinders = propertyBinders;
_logger = loggerFactory.CreateLogger<ComplexTypeModelBinder>(); _logger = loggerFactory.CreateLogger<ComplexTypeModelBinder>();
AllowValidatingTopLevelNodes = allowValidatingTopLevelNodes;
} }
// Internal for testing.
internal bool AllowValidatingTopLevelNodes { get; }
/// <inheritdoc />
public Task BindModelAsync(ModelBindingContext bindingContext) public Task BindModelAsync(ModelBindingContext bindingContext)
{ {
if (bindingContext == null) if (bindingContext == null)
@ -172,8 +164,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
// 1. The top-level model has no public settable properties. // 1. The top-level model has no public settable properties.
// 2. All properties in a [BindRequired] model have [BindNever] or are otherwise excluded from binding. // 2. All properties in a [BindRequired] model have [BindNever] or are otherwise excluded from binding.
// 3. No data exists for any property. // 3. No data exists for any property.
if (AllowValidatingTopLevelNodes && if (!attemptedPropertyBinding &&
!attemptedPropertyBinding &&
bindingContext.IsTopLevelObject && bindingContext.IsTopLevelObject &&
modelMetadata.IsBindingRequired) modelMetadata.IsBindingRequired)
{ {

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{ {
@ -32,11 +31,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
} }
var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>(); var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
var mvcOptions = context.Services.GetRequiredService<IOptions<MvcOptions>>().Value;
return new ComplexTypeModelBinder( return new ComplexTypeModelBinder(
propertyBinders, propertyBinders,
loggerFactory, loggerFactory,
mvcOptions.AllowValidatingTopLevelNodes); allowValidatingTopLevelNodes: true);
} }
return null; return null;

View File

@ -41,10 +41,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
/// <param name="keyBinder">The <see cref="IModelBinder"/> for <typeparamref name="TKey"/>.</param> /// <param name="keyBinder">The <see cref="IModelBinder"/> for <typeparamref name="TKey"/>.</param>
/// <param name="valueBinder">The <see cref="IModelBinder"/> for <typeparamref name="TValue"/>.</param> /// <param name="valueBinder">The <see cref="IModelBinder"/> for <typeparamref name="TValue"/>.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
/// <remarks>
/// The binder will not add an error for an unbound top-level model even if
/// <see cref="ModelMetadata.IsBindingRequired"/> is <see langword="true"/>.
/// </remarks>
public DictionaryModelBinder(IModelBinder keyBinder, IModelBinder valueBinder, ILoggerFactory loggerFactory) public DictionaryModelBinder(IModelBinder keyBinder, IModelBinder valueBinder, ILoggerFactory loggerFactory)
: base(new KeyValuePairModelBinder<TKey, TValue>(keyBinder, valueBinder, loggerFactory), loggerFactory) : base(new KeyValuePairModelBinder<TKey, TValue>(keyBinder, valueBinder, loggerFactory), loggerFactory)
{ {
@ -67,6 +63,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
/// <see cref="ModelMetadata.IsBindingRequired"/> is <see langword="true"/> for a top-level model, the binder /// <see cref="ModelMetadata.IsBindingRequired"/> is <see langword="true"/> for a top-level model, the binder
/// adds a <see cref="ModelStateDictionary"/> error when the model is not bound. /// adds a <see cref="ModelStateDictionary"/> error when the model is not bound.
/// </param> /// </param>
/// <remarks>
/// The <paramref name="allowValidatingTopLevelNodes"/> parameter is currently ignored.
/// <see cref="CollectionModelBinder{TElement}.AllowValidatingTopLevelNodes"/> is always
/// <see langword="false"/> in <see cref="DictionaryModelBinder{TKey, TValue}"/>. This class ignores that
/// property and unconditionally checks for unbound top-level models with
/// <see cref="ModelMetadata.IsBindingRequired"/>.
/// </remarks>
public DictionaryModelBinder( public DictionaryModelBinder(
IModelBinder keyBinder, IModelBinder keyBinder,
IModelBinder valueBinder, IModelBinder valueBinder,
@ -84,12 +87,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
} }
_valueBinder = valueBinder; _valueBinder = valueBinder;
AllowValidatingTopLevelNodes = allowValidatingTopLevelNodes;
} }
// Internal for testing.
internal new bool AllowValidatingTopLevelNodes { get; }
/// <inheritdoc /> /// <inheritdoc />
public override async Task BindModelAsync(ModelBindingContext bindingContext) public override async Task BindModelAsync(ModelBindingContext bindingContext)
{ {
@ -121,7 +120,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{ {
// No IEnumerableValueProvider available for the fallback approach. For example the user may have // No IEnumerableValueProvider available for the fallback approach. For example the user may have
// replaced the ValueProvider with something other than a CompositeValueProvider. // replaced the ValueProvider with something other than a CompositeValueProvider.
if (AllowValidatingTopLevelNodes && bindingContext.IsTopLevelObject) if (bindingContext.IsTopLevelObject)
{ {
AddErrorIfBindingRequired(bindingContext); AddErrorIfBindingRequired(bindingContext);
} }
@ -135,7 +134,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
if (keys.Count == 0) if (keys.Count == 0)
{ {
// No entries with the expected keys. // No entries with the expected keys.
if (AllowValidatingTopLevelNodes && bindingContext.IsTopLevelObject) if (bindingContext.IsTopLevelObject)
{ {
AddErrorIfBindingRequired(bindingContext); AddErrorIfBindingRequired(bindingContext);
} }

View File

@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
keyBinder, keyBinder,
valueBinder, valueBinder,
loggerFactory, loggerFactory,
mvcOptions.AllowValidatingTopLevelNodes); true /* allowValidatingTopLevelNodes */);
} }
return null; return null;

View File

@ -4,7 +4,6 @@
using System; using System;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{ {
@ -32,23 +31,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>(); var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger<HeaderModelBinderProvider>(); var logger = loggerFactory.CreateLogger<HeaderModelBinderProvider>();
var options = context.Services.GetRequiredService<IOptions<MvcOptions>>().Value;
if (!options.AllowBindingHeaderValuesToNonStringModelTypes)
{
if (modelMetadata.ModelType == typeof(string) ||
modelMetadata.ElementType == typeof(string))
{
return new HeaderModelBinder(loggerFactory);
}
else
{
logger.CannotCreateHeaderModelBinderCompatVersion_2_0(modelMetadata.ModelType);
}
return null;
}
if (!IsSimpleType(modelMetadata)) if (!IsSimpleType(modelMetadata))
{ {
logger.CannotCreateHeaderModelBinder(modelMetadata.ModelType); logger.CannotCreateHeaderModelBinder(modelMetadata.ModelType);

View File

@ -19,7 +19,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{ {
private readonly IModelMetadataProvider _modelMetadataProvider; private readonly IModelMetadataProvider _modelMetadataProvider;
private readonly IModelBinderFactory _modelBinderFactory; private readonly IModelBinderFactory _modelBinderFactory;
private readonly MvcOptions _mvcOptions;
private readonly IObjectModelValidator _objectModelValidator; private readonly IObjectModelValidator _objectModelValidator;
/// <summary> /// <summary>
@ -55,6 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// <param name="validator">The <see cref="IObjectModelValidator"/>.</param> /// <param name="validator">The <see cref="IObjectModelValidator"/>.</param>
/// <param name="mvcOptions">The <see cref="MvcOptions"/> accessor.</param> /// <param name="mvcOptions">The <see cref="MvcOptions"/> accessor.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
/// <remarks>The <paramref name="mvcOptions"/> parameter is currently unused.</remarks>
public ParameterBinder( public ParameterBinder(
IModelMetadataProvider modelMetadataProvider, IModelMetadataProvider modelMetadataProvider,
IModelBinderFactory modelBinderFactory, IModelBinderFactory modelBinderFactory,
@ -90,7 +90,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
_modelMetadataProvider = modelMetadataProvider; _modelMetadataProvider = modelMetadataProvider;
_modelBinderFactory = modelBinderFactory; _modelBinderFactory = modelBinderFactory;
_objectModelValidator = validator; _objectModelValidator = validator;
_mvcOptions = mvcOptions.Value;
Logger = loggerFactory.CreateLogger(GetType()); Logger = loggerFactory.CreateLogger(GetType());
} }
@ -260,8 +259,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var modelBindingResult = modelBindingContext.Result; var modelBindingResult = modelBindingContext.Result;
if (_mvcOptions.AllowValidatingTopLevelNodes && if (_objectModelValidator is ObjectModelValidator baseObjectValidator)
_objectModelValidator is ObjectModelValidator baseObjectValidator)
{ {
Logger.AttemptingToValidateParameterOrProperty(parameter, metadata); Logger.AttemptingToValidateParameterOrProperty(parameter, metadata);

View File

@ -39,10 +39,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
validatorProvider, validatorProvider,
validatorCache, validatorCache,
metadataProvider, metadataProvider,
validationState); validationState)
{
visitor.MaxValidationDepth = _mvcOptions.MaxValidationDepth; MaxValidationDepth = _mvcOptions.MaxValidationDepth,
visitor.AllowShortCircuitingValidationWhenNoValidatorsArePresent = _mvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent; AllowShortCircuitingValidationWhenNoValidatorsArePresent =
_mvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent,
};
return visitor; return visitor;
} }

View File

@ -4,14 +4,11 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
@ -22,18 +19,14 @@ namespace Microsoft.AspNetCore.Mvc
/// </summary> /// </summary>
public class MvcOptions : IEnumerable<ICompatibilitySwitch> public class MvcOptions : IEnumerable<ICompatibilitySwitch>
{ {
private int _maxModelStateErrors = ModelStateDictionary.DefaultMaxAllowedErrors;
// See CompatibilitySwitch.cs for guide on how to implement these. // See CompatibilitySwitch.cs for guide on how to implement these.
private readonly CompatibilitySwitch<bool> _allowBindingHeaderValuesToNonStringModelTypes;
private readonly CompatibilitySwitch<bool> _allowCombiningAuthorizeFilters;
private readonly CompatibilitySwitch<bool> _allowValidatingTopLevelNodes;
private readonly CompatibilitySwitch<InputFormatterExceptionPolicy> _inputFormatterExceptionPolicy; private readonly CompatibilitySwitch<InputFormatterExceptionPolicy> _inputFormatterExceptionPolicy;
private readonly CompatibilitySwitch<bool> _suppressBindingUndefinedValueToEnumType; private readonly CompatibilitySwitch<bool> _suppressBindingUndefinedValueToEnumType;
private readonly CompatibilitySwitch<bool> _enableEndpointRouting;
private readonly NullableCompatibilitySwitch<int> _maxValidationDepth;
private readonly CompatibilitySwitch<bool> _allowShortCircuitingValidationWhenNoValidatorsArePresent; private readonly CompatibilitySwitch<bool> _allowShortCircuitingValidationWhenNoValidatorsArePresent;
private readonly ICompatibilitySwitch[] _switches; private readonly ICompatibilitySwitch[] _switches;
private int _maxModelStateErrors = ModelStateDictionary.DefaultMaxAllowedErrors;
private int? _maxValidationDepth = 32;
/// <summary> /// <summary>
/// Creates a new instance of <see cref="MvcOptions"/>. /// Creates a new instance of <see cref="MvcOptions"/>.
@ -52,24 +45,14 @@ namespace Microsoft.AspNetCore.Mvc
ModelValidatorProviders = new List<IModelValidatorProvider>(); ModelValidatorProviders = new List<IModelValidatorProvider>();
ValueProviderFactories = new List<IValueProviderFactory>(); ValueProviderFactories = new List<IValueProviderFactory>();
_allowCombiningAuthorizeFilters = new CompatibilitySwitch<bool>(nameof(AllowCombiningAuthorizeFilters));
_allowBindingHeaderValuesToNonStringModelTypes = new CompatibilitySwitch<bool>(nameof(AllowBindingHeaderValuesToNonStringModelTypes));
_allowValidatingTopLevelNodes = new CompatibilitySwitch<bool>(nameof(AllowValidatingTopLevelNodes));
_inputFormatterExceptionPolicy = new CompatibilitySwitch<InputFormatterExceptionPolicy>(nameof(InputFormatterExceptionPolicy), InputFormatterExceptionPolicy.AllExceptions); _inputFormatterExceptionPolicy = new CompatibilitySwitch<InputFormatterExceptionPolicy>(nameof(InputFormatterExceptionPolicy), InputFormatterExceptionPolicy.AllExceptions);
_suppressBindingUndefinedValueToEnumType = new CompatibilitySwitch<bool>(nameof(SuppressBindingUndefinedValueToEnumType)); _suppressBindingUndefinedValueToEnumType = new CompatibilitySwitch<bool>(nameof(SuppressBindingUndefinedValueToEnumType));
_enableEndpointRouting = new CompatibilitySwitch<bool>(nameof(EnableEndpointRouting));
_maxValidationDepth = new NullableCompatibilitySwitch<int>(nameof(MaxValidationDepth));
_allowShortCircuitingValidationWhenNoValidatorsArePresent = new CompatibilitySwitch<bool>(nameof(AllowShortCircuitingValidationWhenNoValidatorsArePresent)); _allowShortCircuitingValidationWhenNoValidatorsArePresent = new CompatibilitySwitch<bool>(nameof(AllowShortCircuitingValidationWhenNoValidatorsArePresent));
_switches = new ICompatibilitySwitch[] _switches = new ICompatibilitySwitch[]
{ {
_allowCombiningAuthorizeFilters,
_allowBindingHeaderValuesToNonStringModelTypes,
_allowValidatingTopLevelNodes,
_inputFormatterExceptionPolicy, _inputFormatterExceptionPolicy,
_suppressBindingUndefinedValueToEnumType, _suppressBindingUndefinedValueToEnumType,
_enableEndpointRouting,
_maxValidationDepth,
_allowShortCircuitingValidationWhenNoValidatorsArePresent, _allowShortCircuitingValidationWhenNoValidatorsArePresent,
}; };
} }
@ -82,11 +65,7 @@ namespace Microsoft.AspNetCore.Mvc
/// <value> /// <value>
/// The default value is <see langword="true"/>. /// The default value is <see langword="true"/>.
/// </value> /// </value>
public bool EnableEndpointRouting public bool EnableEndpointRouting { get; set; } = true;
{
get => _enableEndpointRouting.Value;
set => _enableEndpointRouting.Value = value;
}
/// <summary> /// <summary>
/// Gets or sets the flag which decides whether body model binding (for example, on an /// Gets or sets the flag which decides whether body model binding (for example, on an
@ -100,58 +79,6 @@ namespace Microsoft.AspNetCore.Mvc
/// </example> /// </example>
public bool AllowEmptyInputInBodyModelBinding { get; set; } public bool AllowEmptyInputInBodyModelBinding { get; set; }
/// <summary>
/// Gets or sets a value that determines if policies on instances of <see cref="AuthorizeFilter" />
/// will be combined into a single effective policy.
/// </summary>
/// <value>
/// The default value is <see langword="true"/>.
/// </value>
/// <remarks>
/// Authorization policies are designed such that multiple authorization policies applied to an endpoint
/// should be combined and executed a single policy. The <see cref="AuthorizeFilter"/> (commonly applied
/// by <see cref="AuthorizeAttribute"/>) can be applied globally, to controllers, and to actions - which
/// specifies multiple authorization policies for an action. In all ASP.NET Core releases prior to 2.1
/// these multiple policies would not combine as intended. This compatibility switch configures whether the
/// old (unintended) behavior or the new combining behavior will be used when multiple authorization policies
/// are applied.
/// </remarks>
public bool AllowCombiningAuthorizeFilters
{
get => _allowCombiningAuthorizeFilters.Value;
set => _allowCombiningAuthorizeFilters.Value = value;
}
/// <summary>
/// Gets or sets a value that determines if <see cref="HeaderModelBinder"/> should bind to types other than
/// <see cref="string"/> or a collection of <see cref="string"/>. If set to <c>true</c>,
/// <see cref="HeaderModelBinder"/> would bind to simple types (like <see cref="string"/>, <see cref="int"/>,
/// <see cref="Enum"/>, <see cref="bool"/> etc.) or a collection of simple types.
/// </summary>
/// <value>
/// The default value is <see langword="true"/>.
/// </value>
public bool AllowBindingHeaderValuesToNonStringModelTypes
{
get => _allowBindingHeaderValuesToNonStringModelTypes.Value;
set => _allowBindingHeaderValuesToNonStringModelTypes.Value = value;
}
/// <summary>
/// Gets or sets a value that determines if model bound action parameters, controller properties, page handler
/// parameters, or page model properties are validated (in addition to validating their elements or
/// properties). If set to <see langword="true"/>, <see cref="BindRequiredAttribute"/> and
/// <c>ValidationAttribute</c>s on these top-level nodes are checked. Otherwise, such attributes are ignored.
/// </summary>
/// <value>
/// The default value is <see langword="true"/>.
/// </value>
public bool AllowValidatingTopLevelNodes
{
get => _allowValidatingTopLevelNodes.Value;
set => _allowValidatingTopLevelNodes.Value = value;
}
/// <summary> /// <summary>
/// Gets a Dictionary of CacheProfile Names, <see cref="CacheProfile"/> which are pre-defined settings for /// Gets a Dictionary of CacheProfile Names, <see cref="CacheProfile"/> which are pre-defined settings for
/// response caching. /// response caching.
@ -311,7 +238,7 @@ namespace Microsoft.AspNetCore.Mvc
/// </value> /// </value>
public int? MaxValidationDepth public int? MaxValidationDepth
{ {
get => _maxValidationDepth.Value; get => _maxValidationDepth;
set set
{ {
if (value != null && value <= 0) if (value != null && value <= 0)
@ -319,7 +246,7 @@ namespace Microsoft.AspNetCore.Mvc
throw new ArgumentOutOfRangeException(nameof(value)); throw new ArgumentOutOfRangeException(nameof(value));
} }
_maxValidationDepth.Value = value; _maxValidationDepth = value;
} }
} }

View File

@ -72,5 +72,35 @@
"TypeId": "public abstract class Microsoft.AspNetCore.Mvc.ModelBinding.ObjectModelValidator : Microsoft.AspNetCore.Mvc.ModelBinding.Validation.IObjectModelValidator", "TypeId": "public abstract class Microsoft.AspNetCore.Mvc.ModelBinding.ObjectModelValidator : Microsoft.AspNetCore.Mvc.ModelBinding.Validation.IObjectModelValidator",
"MemberId": "public abstract Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor GetValidationVisitor(Microsoft.AspNetCore.Mvc.ActionContext actionContext, Microsoft.AspNetCore.Mvc.ModelBinding.Validation.IModelValidatorProvider validatorProvider, Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidatorCache validatorCache, Microsoft.AspNetCore.Mvc.ModelBinding.IModelMetadataProvider metadataProvider, Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationStateDictionary validationState)", "MemberId": "public abstract Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor GetValidationVisitor(Microsoft.AspNetCore.Mvc.ActionContext actionContext, Microsoft.AspNetCore.Mvc.ModelBinding.Validation.IModelValidatorProvider validatorProvider, Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidatorCache validatorCache, Microsoft.AspNetCore.Mvc.ModelBinding.IModelMetadataProvider metadataProvider, Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationStateDictionary validationState)",
"Kind": "Addition" "Kind": "Addition"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.MvcOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Boolean get_AllowBindingHeaderValuesToNonStringModelTypes()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.MvcOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Boolean get_AllowCombiningAuthorizeFilters()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.MvcOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Boolean get_AllowValidatingTopLevelNodes()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.MvcOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Void set_AllowBindingHeaderValuesToNonStringModelTypes(System.Boolean value)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.MvcOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Void set_AllowCombiningAuthorizeFilters(System.Boolean value)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.MvcOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Void set_AllowValidatingTopLevelNodes(System.Boolean value)",
"Kind": "Removal"
} }
] ]

View File

@ -75,8 +75,6 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcJsonMvcOptionsSetup>()); ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcJsonMvcOptionsSetup>());
services.TryAddEnumerable(
ServiceDescriptor.Transient<IPostConfigureOptions<MvcJsonOptions>, MvcJsonOptionsConfigureCompatibilityOptions>());
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Transient<IApiDescriptionProvider, JsonPatchOperationsArrayProvider>()); ServiceDescriptor.Transient<IApiDescriptionProvider, JsonPatchOperationsArrayProvider>());
services.TryAddSingleton<JsonResultExecutor>(); services.TryAddSingleton<JsonResultExecutor>();

View File

@ -1,32 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
{
internal class MvcJsonOptionsConfigureCompatibilityOptions : ConfigureCompatibilityOptions<MvcJsonOptions>
{
public MvcJsonOptionsConfigureCompatibilityOptions(
ILoggerFactory loggerFactory,
IOptions<MvcCompatibilityOptions> compatibilityOptions)
: base(loggerFactory, compatibilityOptions)
{
}
protected override IReadOnlyDictionary<string, object> DefaultValues
{
get
{
return new Dictionary<string, object>
{
[nameof(MvcJsonOptions.AllowInputFormatterExceptionMessages)] = true,
};
}
}
}
}

View File

@ -1,6 +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;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.Formatters;
@ -15,21 +16,7 @@ namespace Microsoft.AspNetCore.Mvc
/// </summary> /// </summary>
public class MvcJsonOptions : IEnumerable<ICompatibilitySwitch> public class MvcJsonOptions : IEnumerable<ICompatibilitySwitch>
{ {
private readonly CompatibilitySwitch<bool> _allowInputFormatterExceptionMessages; private readonly ICompatibilitySwitch[] _switches = Array.Empty<ICompatibilitySwitch>();
private readonly ICompatibilitySwitch[] _switches;
/// <summary>
/// Creates a new instance of <see cref="MvcJsonOptions"/>.
/// </summary>
public MvcJsonOptions()
{
_allowInputFormatterExceptionMessages = new CompatibilitySwitch<bool>(nameof(AllowInputFormatterExceptionMessages));
_switches = new ICompatibilitySwitch[]
{
_allowInputFormatterExceptionMessages,
};
}
/// <summary> /// <summary>
/// Gets or sets a flag to determine whether error messages from JSON deserialization by the /// Gets or sets a flag to determine whether error messages from JSON deserialization by the
@ -44,11 +31,7 @@ namespace Microsoft.AspNetCore.Mvc
/// or using <see cref="BadRequestObjectResult"/>. In effect, this setting controls whether clients can receive /// or using <see cref="BadRequestObjectResult"/>. In effect, this setting controls whether clients can receive
/// detailed error messages about submitted JSON data. /// detailed error messages about submitted JSON data.
/// </remarks> /// </remarks>
public bool AllowInputFormatterExceptionMessages public bool AllowInputFormatterExceptionMessages { get; set; } = true;
{
get => _allowInputFormatterExceptionMessages.Value;
set => _allowInputFormatterExceptionMessages.Value = value;
}
/// <summary> /// <summary>
/// Gets the <see cref="JsonSerializerSettings"/> that are used by this application. /// Gets the <see cref="JsonSerializerSettings"/> that are used by this application.

View File

@ -64,7 +64,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
/// This type will never be used for deserialization, but we are required to implement the add /// This type will never be used for deserialization, but we are required to implement the add
/// method so that the type can be serialized. This will never be called. /// method so that the type can be serialized. This will never be called.
/// </summary> /// </summary>
/// <param name="item">The item to add. Unused.</param> /// <param name="item">The item to add.</param>
/// <exception cref="NotImplementedException">Thrown unconditionally.</exception>
public void Add(object item) public void Add(object item)
{ {
throw new NotImplementedException(); throw new NotImplementedException();

View File

@ -21,7 +21,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
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 IModelMetadataProvider _modelMetadataProvider;
private readonly MvcOptions _mvcOptions;
private readonly RazorPagesOptions _razorPagesOptions; private readonly RazorPagesOptions _razorPagesOptions;
private readonly Func<ActionContext, bool> _supportsAllRequests; private readonly Func<ActionContext, bool> _supportsAllRequests;
private readonly Func<ActionContext, bool> _supportsNonGetRequests; private readonly Func<ActionContext, bool> _supportsNonGetRequests;
@ -29,11 +28,9 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
public DefaultPageApplicationModelProvider( public DefaultPageApplicationModelProvider(
IModelMetadataProvider modelMetadataProvider, IModelMetadataProvider modelMetadataProvider,
IOptions<MvcOptions> options,
IOptions<RazorPagesOptions> razorPagesOptions) IOptions<RazorPagesOptions> razorPagesOptions)
{ {
_modelMetadataProvider = modelMetadataProvider; _modelMetadataProvider = modelMetadataProvider;
_mvcOptions = options.Value;
_razorPagesOptions = razorPagesOptions.Value; _razorPagesOptions = razorPagesOptions.Value;
_supportsAllRequests = _ => true; _supportsAllRequests = _ => true;
@ -248,7 +245,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
var attributes = parameter.GetCustomAttributes(inherit: true); var attributes = parameter.GetCustomAttributes(inherit: true);
BindingInfo bindingInfo; BindingInfo bindingInfo;
if (_mvcOptions.AllowValidatingTopLevelNodes && _modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase) if (_modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase)
{ {
var modelMetadata = modelMetadataProviderBase.GetMetadataForParameter(parameter); var modelMetadata = modelMetadataProviderBase.GetMetadataForParameter(parameter);
bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata); bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata);

View File

@ -102,8 +102,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{ {
var parameter = handler.Parameters[i]; var parameter = handler.Parameters[i];
ModelMetadata metadata; ModelMetadata metadata;
if (mvcOptions.AllowValidatingTopLevelNodes && if (modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase)
modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase)
{ {
// The default model metadata provider derives from ModelMetadataProvider // The default model metadata provider derives from ModelMetadataProvider
// and can therefore supply information about attributes applied to parameters. // and can therefore supply information about attributes applied to parameters.

View File

@ -1180,25 +1180,6 @@ namespace Microsoft.AspNetCore.Mvc.Description
Assert.True(parameter.ModelMetadata.IsBindingRequired); Assert.True(parameter.ModelMetadata.IsBindingRequired);
} }
[Fact]
public void GetApiDescription_ParameterDescription_IsRequiredNotSet_IfNotValidatingTopLevelNodes()
{
// Arrange
var action = CreateActionDescriptor(nameof(RequiredParameter));
// Act
var descriptions = GetApiDescriptions(action, allowValidatingTopLevelNodes: false);
// Assert
var description = Assert.Single(descriptions);
var parameter = Assert.Single(description.ParameterDescriptions);
Assert.Equal("name", parameter.Name);
Assert.Same(BindingSource.ModelBinding, parameter.Source);
Assert.Equal(typeof(string), parameter.Type);
Assert.False(parameter.ModelMetadata.IsRequired);
Assert.False(parameter.ModelMetadata.IsBindingRequired);
}
[Fact] [Fact]
public void GetApiDescription_ParameterDescription_SourceFromRouteData() public void GetApiDescription_ParameterDescription_SourceFromRouteData()
{ {
@ -1816,15 +1797,11 @@ namespace Microsoft.AspNetCore.Mvc.Description
ActionDescriptor action, ActionDescriptor action,
List<MockInputFormatter> inputFormatters = null, List<MockInputFormatter> inputFormatters = null,
List<MockOutputFormatter> outputFormatters = null, List<MockOutputFormatter> outputFormatters = null,
bool allowValidatingTopLevelNodes = true,
RouteOptions routeOptions = null) RouteOptions routeOptions = null)
{ {
var context = new ApiDescriptionProviderContext(new ActionDescriptor[] { action }); var context = new ApiDescriptionProviderContext(new ActionDescriptor[] { action });
var options = new MvcOptions var options = new MvcOptions();
{
AllowValidatingTopLevelNodes = allowValidatingTopLevelNodes,
};
foreach (var formatter in inputFormatters ?? CreateInputFormatters()) foreach (var formatter in inputFormatters ?? CreateInputFormatters())
{ {
options.InputFormatters.Add(formatter); options.InputFormatters.Add(formatter);

View File

@ -169,19 +169,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
loggerFactory); loggerFactory);
} }
private static ApplicationModelProviderContext GetContext(
Type type,
IModelMetadataProvider modelMetadataProvider = null)
{
var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() });
var mvcOptions = Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = true });
modelMetadataProvider = modelMetadataProvider ?? new EmptyModelMetadataProvider();
var provider = new DefaultApplicationModelProvider(mvcOptions, modelMetadataProvider);
provider.OnProvidersExecuting(context);
return context;
}
private class TestApiController : ControllerBase private class TestApiController : ControllerBase
{ {
public IActionResult TestAction(object value) => null; public IActionResult TestAction(object value) => null;

View File

@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
IModelMetadataProvider modelMetadataProvider = null) IModelMetadataProvider modelMetadataProvider = null)
{ {
var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() }); var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() });
var mvcOptions = Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = true }); var mvcOptions = Options.Create(new MvcOptions());
modelMetadataProvider = modelMetadataProvider ?? new EmptyModelMetadataProvider(); modelMetadataProvider = modelMetadataProvider ?? new EmptyModelMetadataProvider();
var convention = new DefaultApplicationModelProvider(mvcOptions, modelMetadataProvider); var convention = new DefaultApplicationModelProvider(mvcOptions, modelMetadataProvider);
convention.OnProvidersExecuting(context); convention.OnProvidersExecuting(context);
@ -68,12 +68,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
return context; return context;
} }
private static ControllerModel GetControllerModel(Type controllerType)
{
var context = GetContext(controllerType);
return Assert.Single(context.Result.Controllers);
}
private static ActionModel GetActionModel(Type controllerType, string actionName) private static ActionModel GetActionModel(Type controllerType, string actionName)
{ {
var context = GetContext(controllerType); var context = GetContext(controllerType);

View File

@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{ {
// Arrange // Arrange
var builder = new TestApplicationModelProvider( var builder = new TestApplicationModelProvider(
new MvcOptions { AllowValidatingTopLevelNodes = true }, new MvcOptions(),
TestModelMetadataProvider.CreateDefaultProvider()); TestModelMetadataProvider.CreateDefaultProvider());
var typeInfo = typeof(ModelBinderController).GetTypeInfo(); var typeInfo = typeof(ModelBinderController).GetTypeInfo();
@ -152,7 +152,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{ {
// Arrange // Arrange
var builder = new TestApplicationModelProvider( var builder = new TestApplicationModelProvider(
new MvcOptions { AllowValidatingTopLevelNodes = true }, new MvcOptions(),
TestModelMetadataProvider.CreateDefaultProvider()); TestModelMetadataProvider.CreateDefaultProvider());
var typeInfo = typeof(ModelBinderController).GetTypeInfo(); var typeInfo = typeof(ModelBinderController).GetTypeInfo();
@ -191,57 +191,12 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
}); });
} }
[Fact]
public void OnProvidersExecuting_AddsBindingSources_ForActionParameters_WithLegacyValidationBehavior()
{
// Arrange
var builder = new TestApplicationModelProvider(
new MvcOptions(),
TestModelMetadataProvider.CreateDefaultProvider());
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] [Fact]
public void OnProvidersExecuting_InfersFormFileSourceForTypesAssignableFromIEnumerableOfFormFiles() public void OnProvidersExecuting_InfersFormFileSourceForTypesAssignableFromIEnumerableOfFormFiles()
{ {
// Arrange // Arrange
var builder = new TestApplicationModelProvider( var builder = new TestApplicationModelProvider(
new MvcOptions { AllowValidatingTopLevelNodes = true }, new MvcOptions(),
TestModelMetadataProvider.CreateDefaultProvider()); TestModelMetadataProvider.CreateDefaultProvider());
var typeInfo = typeof(ModelBinderController).GetTypeInfo(); var typeInfo = typeof(ModelBinderController).GetTypeInfo();
@ -286,7 +241,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
public void OnProvidersExecuting_AddsBindingSources_ForActionParameters_ReadFromModelMetadata() public void OnProvidersExecuting_AddsBindingSources_ForActionParameters_ReadFromModelMetadata()
{ {
// Arrange // Arrange
var options = new MvcOptions { AllowValidatingTopLevelNodes = true }; var options = new MvcOptions();
var detailsProvider = new BindingSourceMetadataProvider(typeof(Guid), BindingSource.Special); var detailsProvider = new BindingSourceMetadataProvider(typeof(Guid), BindingSource.Special);
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(new[] { detailsProvider }); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(new[] { detailsProvider });
@ -1822,9 +1777,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
private class TestApplicationModelProvider : DefaultApplicationModelProvider private class TestApplicationModelProvider : DefaultApplicationModelProvider
{ {
public TestApplicationModelProvider() public TestApplicationModelProvider()
: this( : this(new MvcOptions(), new EmptyModelMetadataProvider())
new MvcOptions { AllowValidatingTopLevelNodes = true },
new EmptyModelMetadataProvider())
{ {
} }

View File

@ -468,22 +468,6 @@ Environment.NewLine + "int b";
Assert.Same(BindingSource.Body, result); Assert.Same(BindingSource.Body, result);
} }
[Fact]
public void InferBindingSourceForParameter_ReturnsQueryForCollectionOfSimpleTypes_WhenAllowInferringBindingSourceForCollectionTypesAsFromQueryIsSet()
{
// Arrange
var actionName = nameof(ParameterBindingController.CollectionOfSimpleTypes);
var parameter = GetParameterModel(typeof(ParameterBindingController), actionName);
var convention = GetConvention();
convention.AllowInferringBindingSourceForCollectionTypesAsFromQuery = true;
// Act
var result = convention.InferBindingSourceForParameter(parameter);
// Assert
Assert.Same(BindingSource.Query, result);
}
[Fact] [Fact]
public void InferBindingSourceForParameter_ReturnsBodyForCollectionOfComplexTypes() public void InferBindingSourceForParameter_ReturnsBodyForCollectionOfComplexTypes()
{ {
@ -499,22 +483,6 @@ Environment.NewLine + "int b";
Assert.Same(BindingSource.Body, result); Assert.Same(BindingSource.Body, result);
} }
[Fact]
public void InferBindingSourceForParameter_ReturnsQueryForCollectionOfComplexTypes_WhenAllowInferringBindingSourceForCollectionTypesAsFromQueryIsSet()
{
// Arrange
var actionName = nameof(ParameterBindingController.CollectionOfComplexTypes);
var parameter = GetParameterModel(typeof(ParameterBindingController), actionName);
var convention = GetConvention();
convention.AllowInferringBindingSourceForCollectionTypesAsFromQuery = true;
// Act
var result = convention.InferBindingSourceForParameter(parameter);
// Assert
Assert.Same(BindingSource.Query, result);
}
[Fact] [Fact]
public void PreservesBindingSourceInference_ForFromQueryParameter_WithDefaultName() public void PreservesBindingSourceInference_ForFromQueryParameter_WithDefaultName()
{ {
@ -782,7 +750,7 @@ Environment.NewLine + "int b";
IModelMetadataProvider modelMetadataProvider = null) IModelMetadataProvider modelMetadataProvider = null)
{ {
var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() }); var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() });
var mvcOptions = Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = true }); var mvcOptions = Options.Create(new MvcOptions());
modelMetadataProvider = modelMetadataProvider ?? new EmptyModelMetadataProvider(); modelMetadataProvider = modelMetadataProvider ?? new EmptyModelMetadataProvider();
var convention = new DefaultApplicationModelProvider(mvcOptions, modelMetadataProvider); var convention = new DefaultApplicationModelProvider(mvcOptions, modelMetadataProvider);
convention.OnProvidersExecuting(context); convention.OnProvidersExecuting(context);

View File

@ -31,6 +31,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
{ {
// Arrange // Arrange
var authorizationContext = GetAuthorizationContext(anonymous: true); var authorizationContext = GetAuthorizationContext(anonymous: true);
// The type 'AuthorizeFilter' is both a filter by itself and also a filter factory. // The type 'AuthorizeFilter' is both a filter by itself and also a filter factory.
// The default filter provider first checks if a type is a filter factory and creates an instance of // The default filter provider first checks if a type is a filter factory and creates an instance of
// this filter. // this filter.
@ -38,6 +39,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
var filterFactory = authorizeFilterFactory as IFilterFactory; var filterFactory = authorizeFilterFactory as IFilterFactory;
var authorizeFilter = (AuthorizeFilter)filterFactory.CreateInstance( var authorizeFilter = (AuthorizeFilter)filterFactory.CreateInstance(
authorizationContext.HttpContext.RequestServices); authorizationContext.HttpContext.RequestServices);
authorizationContext.Filters.Add(authorizeFilter);
// Act // Act
await authorizeFilter.OnAuthorizationAsync(authorizationContext); await authorizeFilter.OnAuthorizationAsync(authorizationContext);
@ -52,6 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
// Arrange // Arrange
var authorizeFilter = new AuthorizeFilter(new[] { new AuthorizeAttribute() }); var authorizeFilter = new AuthorizeFilter(new[] { new AuthorizeAttribute() });
var authorizationContext = GetAuthorizationContext(); var authorizationContext = GetAuthorizationContext();
authorizationContext.Filters.Add(authorizeFilter);
var expected = "An AuthorizationPolicy cannot be created without a valid instance of " + var expected = "An AuthorizationPolicy cannot be created without a valid instance of " +
"IAuthorizationPolicyProvider."; "IAuthorizationPolicyProvider.";
@ -67,6 +70,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
// Arrange // Arrange
var authorizeFilter = new AuthorizeFilter(new[] { new AuthorizeAttribute() }); var authorizeFilter = new AuthorizeFilter(new[] { new AuthorizeAttribute() });
var authorizationContext = GetAuthorizationContext(); var authorizationContext = GetAuthorizationContext();
authorizationContext.Filters.Add(authorizeFilter);
var expected = "An AuthorizationPolicy cannot be created without a valid instance of " + var expected = "An AuthorizationPolicy cannot be created without a valid instance of " +
"IAuthorizationPolicyProvider."; "IAuthorizationPolicyProvider.";
@ -101,6 +105,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
.Callback(() => getPolicyCount++); .Callback(() => getPolicyCount++);
var authorizeFilter = new AuthorizeFilter(policyProvider.Object, new AuthorizeAttribute[] { new AuthorizeAttribute("whatever") }); var authorizeFilter = new AuthorizeFilter(policyProvider.Object, new AuthorizeAttribute[] { new AuthorizeAttribute("whatever") });
var authorizationContext = GetAuthorizationContext(); var authorizationContext = GetAuthorizationContext();
authorizationContext.Filters.Add(authorizeFilter);
// Act & Assert // Act & Assert
await authorizeFilter.OnAuthorizationAsync(authorizationContext); await authorizeFilter.OnAuthorizationAsync(authorizationContext);
@ -153,6 +158,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
// Arrange // Arrange
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()); var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
var authorizationContext = GetAuthorizationContext(anonymous: true); var authorizationContext = GetAuthorizationContext(anonymous: true);
authorizationContext.Filters.Add(authorizeFilter);
// Act // Act
await authorizeFilter.OnAuthorizationAsync(authorizationContext); await authorizeFilter.OnAuthorizationAsync(authorizationContext);
@ -223,8 +229,10 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
private class TestPolicyProvider : IAuthorizationPolicyProvider private class TestPolicyProvider : IAuthorizationPolicyProvider
{ {
private AuthorizationPolicy _true = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build(); private readonly AuthorizationPolicy _true =
private AuthorizationPolicy _false = new AuthorizationPolicyBuilder().RequireAssertion(_ => false).Build(); new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build();
private readonly AuthorizationPolicy _false =
new AuthorizationPolicyBuilder().RequireAssertion(_ => false).Build();
public int GetPolicyCalls = 0; public int GetPolicyCalls = 0;
@ -250,7 +258,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
new AuthorizeAttribute { Policy = "true"}, new AuthorizeAttribute { Policy = "true"},
new AuthorizeAttribute { Policy = "false"} new AuthorizeAttribute { Policy = "false"}
}); });
var authorizationContext = GetAuthorizationContext(anonymous: false, registerServices: s => s.Configure<MvcOptions>(o => o.AllowCombiningAuthorizeFilters = true)); var authorizationContext = GetAuthorizationContext(anonymous: false);
// Effective policy should fail, if both are combined // Effective policy should fail, if both are combined
authorizationContext.Filters.Add(authorizeFilter); authorizationContext.Filters.Add(authorizeFilter);
var secondFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => true).Build()); var secondFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => true).Build());
@ -274,7 +282,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
new AuthorizeAttribute { Policy = "true"}, new AuthorizeAttribute { Policy = "true"},
new AuthorizeAttribute { Policy = "false"} new AuthorizeAttribute { Policy = "false"}
}); });
var authorizationContext = GetAuthorizationContext(anonymous: false, registerServices: s => s.Configure<MvcOptions>(o => o.AllowCombiningAuthorizeFilters = true)); var authorizationContext = GetAuthorizationContext(anonymous: false);
// Effective policy should fail, if both are combined // Effective policy should fail, if both are combined
authorizationContext.Filters.Add(authorizeFilter); authorizationContext.Filters.Add(authorizeFilter);
var secondFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => true).Build()); var secondFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => true).Build());
@ -304,7 +312,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
{ {
// Arrange // Arrange
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => true).Build()); var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => true).Build());
var authorizationContext = GetAuthorizationContext(anonymous: false, registerServices: s => s.Configure<MvcOptions>(o => o.AllowCombiningAuthorizeFilters = true)); var authorizationContext = GetAuthorizationContext(anonymous: false);
// Effective policy should fail, if both are combined // Effective policy should fail, if both are combined
authorizationContext.Filters.Add(authorizeFilter); authorizationContext.Filters.Add(authorizeFilter);
var secondFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => false).Build()); var secondFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => false).Build());
@ -322,7 +330,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
{ {
// Arrange // Arrange
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => false).Build()); var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => false).Build());
var authorizationContext = GetAuthorizationContext(anonymous: false, registerServices: s => s.Configure<MvcOptions>(o => o.AllowCombiningAuthorizeFilters = true)); var authorizationContext = GetAuthorizationContext(anonymous: false);
// Effective policy should fail, if both are combined // Effective policy should fail, if both are combined
authorizationContext.Filters.Add(authorizeFilter); authorizationContext.Filters.Add(authorizeFilter);
var secondFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => false).Build()); var secondFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => false).Build());
@ -340,7 +348,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
{ {
// Arrange // Arrange
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => true).Build()); var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAssertion(a => true).Build());
var authorizationContext = GetAuthorizationContext(anonymous: false, registerServices: s => s.Configure<MvcOptions>(o => o.AllowCombiningAuthorizeFilters = true)); var authorizationContext = GetAuthorizationContext(anonymous: false);
// Effective policy should fail, if both are combined // Effective policy should fail, if both are combined
authorizationContext.Filters.Add(authorizeFilter); authorizationContext.Filters.Add(authorizeFilter);
authorizationContext.Filters.Add(new DerivedAuthorizeFilter()); authorizationContext.Filters.Add(new DerivedAuthorizeFilter());
@ -381,6 +389,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
// Arrange // Arrange
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireRole("Wut").Build()); var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireRole("Wut").Build());
var authorizationContext = GetAuthorizationContext(); var authorizationContext = GetAuthorizationContext();
authorizationContext.Filters.Add(authorizeFilter);
// Act // Act
await authorizeFilter.OnAuthorizationAsync(authorizationContext); await authorizeFilter.OnAuthorizationAsync(authorizationContext);
@ -397,6 +406,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
.RequireClaim("Permission", "CanViewComment") .RequireClaim("Permission", "CanViewComment")
.Build()); .Build());
var authorizationContext = GetAuthorizationContext(); var authorizationContext = GetAuthorizationContext();
authorizationContext.Filters.Add(authorizeFilter);
// Act // Act
await authorizeFilter.OnAuthorizationAsync(authorizationContext); await authorizeFilter.OnAuthorizationAsync(authorizationContext);
@ -532,10 +542,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
serviceCollection.AddSingleton(auth.Object); serviceCollection.AddSingleton(auth.Object);
serviceCollection.AddAuthorization(); serviceCollection.AddAuthorization();
serviceCollection.AddAuthorizationPolicyEvaluator(); serviceCollection.AddAuthorizationPolicyEvaluator();
if (registerServices != null) registerServices?.Invoke(serviceCollection);
{
registerServices(serviceCollection);
}
var serviceProvider = serviceCollection.BuildServiceProvider(); var serviceProvider = serviceCollection.BuildServiceProvider();
@ -557,7 +564,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
routeData: new RouteData(), routeData: new RouteData(),
actionDescriptor: new ActionDescriptor()); actionDescriptor: new ActionDescriptor());
var authorizationContext = new Filters.AuthorizationFilterContext( var authorizationContext = new AuthorizationFilterContext(
actionContext, actionContext,
Enumerable.Empty<IFilterMetadata>().ToList() Enumerable.Empty<IFilterMetadata>().ToList()
); );

View File

@ -9,7 +9,6 @@ using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
@ -28,10 +27,7 @@ namespace Microsoft.AspNetCore.Mvc.Controllers
{ {
public class ControllerBinderDelegateProviderTest public class ControllerBinderDelegateProviderTest
{ {
private static readonly MvcOptions _options = new MvcOptions private static readonly MvcOptions _options = new MvcOptions();
{
AllowValidatingTopLevelNodes = true,
};
private static readonly IOptions<MvcOptions> _optionsAccessor = Options.Create(_options); private static readonly IOptions<MvcOptions> _optionsAccessor = Options.Create(_options);
[Fact] [Fact]
@ -428,55 +424,6 @@ namespace Microsoft.AspNetCore.Mvc.Controllers
controllerContext.ModelState["memberName"].Errors.Single().ErrorMessage); controllerContext.ModelState["memberName"].Errors.Single().ErrorMessage);
} }
[Fact]
public async Task CreateBinderDelegate_Delegate_DoesNotCallValidator_IfNotValidatingTopLevelNodes()
{
// Arrange
var actionDescriptor = GetActionDescriptor();
actionDescriptor.Parameters.Add(
new ControllerParameterDescriptor
{
Name = "foo",
ParameterType = typeof(object),
ParameterInfo = ParameterInfos.CustomValidationParameterInfo
});
var controllerContext = GetControllerContext(actionDescriptor);
var factory = GetModelBinderFactory("Hello");
var mockValidator = new Mock<IModelValidator>();
mockValidator
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()))
.Returns(new[] { new ModelValidationResult("memberName", "some message") });
// Do not set AllowValidatingTopLevelNodes.
var mvcOptions = new MvcOptions();
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
GetObjectValidator(modelMetadataProvider, GetModelValidatorProvider(mockValidator.Object)),
Options.Create(mvcOptions),
NullLoggerFactory.Instance);
var controller = new TestController();
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
// Act
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder,
factory,
modelMetadataProvider,
actionDescriptor,
mvcOptions);
await binderDelegate(controllerContext, controller, arguments);
// Assert
Assert.True(controllerContext.ModelState.IsValid);
mockValidator.Verify(o => o.Validate(It.IsAny<ModelValidationContext>()), Times.Never());
}
[Fact] [Fact]
public async Task CreateBinderDelegate_Delegate_DoesNotCallValidator_IfModelBinderFails() public async Task CreateBinderDelegate_Delegate_DoesNotCallValidator_IfModelBinderFails()
{ {

View File

@ -6,8 +6,6 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit; using Xunit;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
@ -18,9 +16,7 @@ namespace Microsoft.Extensions.DependencyInjection
public void Configure_AssignsInvalidModelStateResponseFactory() public void Configure_AssignsInvalidModelStateResponseFactory()
{ {
// Arrange // Arrange
var optionsSetup = new ApiBehaviorOptionsSetup( var optionsSetup = new ApiBehaviorOptionsSetup();
NullLoggerFactory.Instance,
Options.Options.Create(new MvcCompatibilityOptions()));
var options = new ApiBehaviorOptions(); var options = new ApiBehaviorOptions();
// Act // Act
@ -35,9 +31,7 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
// Arrange // Arrange
var expected = new[] { 400, 401, 403, 404, 406, 409, 415, 422, }; var expected = new[] { 400, 401, 403, 404, 406, 409, 415, 422, };
var optionsSetup = new ApiBehaviorOptionsSetup( var optionsSetup = new ApiBehaviorOptionsSetup();
NullLoggerFactory.Instance,
Options.Options.Create(new MvcCompatibilityOptions()));
var options = new ApiBehaviorOptions(); var options = new ApiBehaviorOptions();
// Act // Act
@ -51,9 +45,7 @@ namespace Microsoft.Extensions.DependencyInjection
public void PostConfigure_SetProblemDetailsModelStateResponseFactory() public void PostConfigure_SetProblemDetailsModelStateResponseFactory()
{ {
// Arrange // Arrange
var optionsSetup = new ApiBehaviorOptionsSetup( var optionsSetup = new ApiBehaviorOptionsSetup();
NullLoggerFactory.Instance,
Options.Options.Create(new MvcCompatibilityOptions { CompatibilityVersion = CompatibilityVersion.Latest }));
var options = new ApiBehaviorOptions(); var options = new ApiBehaviorOptions();
// Act // Act
@ -68,9 +60,7 @@ namespace Microsoft.Extensions.DependencyInjection
public void PostConfigure_DoesNotSetProblemDetailsFactory_IfValueWasModified() public void PostConfigure_DoesNotSetProblemDetailsFactory_IfValueWasModified()
{ {
// Arrange // Arrange
var optionsSetup = new ApiBehaviorOptionsSetup( var optionsSetup = new ApiBehaviorOptionsSetup();
NullLoggerFactory.Instance,
Options.Options.Create(new MvcCompatibilityOptions { CompatibilityVersion = CompatibilityVersion.Latest }));
var options = new ApiBehaviorOptions(); var options = new ApiBehaviorOptions();
Func<ActionContext, IActionResult> expected = _ => null; Func<ActionContext, IActionResult> expected = _ => null;

View File

@ -33,11 +33,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
new[] { new DefaultFilterProvider() }); new[] { new DefaultFilterProvider() });
// Act // Act
var cacheEntry1 = controllerActionInvokerCache.GetCachedResult(controllerContext); var (cacheEntry, filters) = controllerActionInvokerCache.GetCachedResult(controllerContext);
var cacheEntry2 = controllerActionInvokerCache.GetCachedResult(controllerContext); var (cacheEntry2, filters2) = controllerActionInvokerCache.GetCachedResult(controllerContext);
// Assert // Assert
Assert.Equal(cacheEntry1.filters, cacheEntry2.filters); Assert.Equal(filters, filters2);
} }
[Fact] [Fact]
@ -54,11 +54,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
new[] { new DefaultFilterProvider() }); new[] { new DefaultFilterProvider() });
// Act // Act
var cacheEntry1 = controllerActionInvokerCache.GetCachedResult(controllerContext); var (cacheEntry, filters) = controllerActionInvokerCache.GetCachedResult(controllerContext);
var cacheEntry2 = controllerActionInvokerCache.GetCachedResult(controllerContext); var (cacheEntry2, filters2) = controllerActionInvokerCache.GetCachedResult(controllerContext);
// Assert // Assert
Assert.Same(cacheEntry1.cacheEntry, cacheEntry2.cacheEntry); Assert.Same(cacheEntry, cacheEntry2);
} }
private class TestFilter : IFilterMetadata private class TestFilter : IFilterMetadata
@ -100,10 +100,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
new[] { controllerContext.ActionDescriptor }); new[] { controllerContext.ActionDescriptor });
var modelMetadataProvider = new EmptyModelMetadataProvider(); var modelMetadataProvider = new EmptyModelMetadataProvider();
var modelBinderFactory = TestModelBinderFactory.CreateDefault(); var modelBinderFactory = TestModelBinderFactory.CreateDefault();
var mvcOptions = Options.Create(new MvcOptions var mvcOptions = Options.Create(new MvcOptions());
{
AllowValidatingTopLevelNodes = true,
});
return new ControllerActionInvokerCache( return new ControllerActionInvokerCache(
descriptorProvider, descriptorProvider,

View File

@ -1,83 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Core.Infrastructure
{
public class MvcOptionsConfigureCompatibilityOptionsTest
{
[Fact]
public void PostConfigure_ConfiguresMaxValidationDepth()
{
// Arrange
var mvcOptions = new MvcOptions();
var mvcCompatibilityOptions = new MvcCompatibilityOptions
{
CompatibilityVersion = CompatibilityVersion.Version_3_0,
};
var configureOptions = new MvcOptionsConfigureCompatibilityOptions(
NullLoggerFactory.Instance,
Options.Create(mvcCompatibilityOptions));
// Act
configureOptions.PostConfigure(string.Empty, mvcOptions);
// Assert
Assert.Equal(32, mvcOptions.MaxValidationDepth);
}
[Fact]
public void PostConfigure_DoesNotConfiguresMaxValidationDepth_WhenSetToNull()
{
// Arrange
var mvcOptions = new MvcOptions
{
MaxValidationDepth = null,
};
var mvcCompatibilityOptions = new MvcCompatibilityOptions
{
CompatibilityVersion = CompatibilityVersion.Version_3_0,
};
var configureOptions = new MvcOptionsConfigureCompatibilityOptions(
NullLoggerFactory.Instance,
Options.Create(mvcCompatibilityOptions));
// Act
configureOptions.PostConfigure(string.Empty, mvcOptions);
// Assert
Assert.Null(mvcOptions.MaxValidationDepth);
}
[Fact]
public void PostConfigure_DoesNotConfiguresMaxValidationDepth_WhenSetToValue()
{
// Arrange
var expected = 13;
var mvcOptions = new MvcOptions
{
MaxValidationDepth = expected,
};
var mvcCompatibilityOptions = new MvcCompatibilityOptions
{
CompatibilityVersion = CompatibilityVersion.Version_3_0,
};
var configureOptions = new MvcOptionsConfigureCompatibilityOptions(
NullLoggerFactory.Instance,
Options.Create(mvcCompatibilityOptions));
// Act
configureOptions.PostConfigure(string.Empty, mvcOptions);
// Assert
Assert.Equal(expected, mvcOptions.MaxValidationDepth);
}
}
}

View File

@ -51,17 +51,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
Assert.IsType(typeof(ArrayModelBinder<>).MakeGenericType(modelType.GetElementType()), result); Assert.IsType(typeof(ArrayModelBinder<>).MakeGenericType(modelType.GetElementType()), result);
} }
[Theory] [Fact]
[InlineData(false)] public void Create_ForArrayType_ReturnsBinder()
[InlineData(true)]
public void Create_ForArrayType_ReturnsBinder_WithExpectedAllowValidatingTopLevelNodes(
bool allowValidatingTopLevelNodes)
{ {
// Arrange // Arrange
var provider = new ArrayModelBinderProvider(); var provider = new ArrayModelBinderProvider();
var context = new TestModelBinderProviderContext(typeof(int[])); var context = new TestModelBinderProviderContext(typeof(int[]));
context.MvcOptions.AllowValidatingTopLevelNodes = allowValidatingTopLevelNodes;
context.OnCreatingBinder(m => context.OnCreatingBinder(m =>
{ {
Assert.Equal(typeof(int), m.ModelType); Assert.Equal(typeof(int), m.ModelType);
@ -73,7 +69,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
// Assert // Assert
var binder = Assert.IsType<ArrayModelBinder<int>>(result); var binder = Assert.IsType<ArrayModelBinder<int>>(result);
Assert.Equal(allowValidatingTopLevelNodes, binder.AllowValidatingTopLevelNodes); Assert.True(binder.AllowValidatingTopLevelNodes);
} }
[Fact] [Fact]

View File

@ -48,11 +48,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
[InlineData(false, false)] [InlineData(false, false)]
[InlineData(false, true)] [InlineData(false, true)]
[InlineData(true, false)] [InlineData(true, false)]
[InlineData(true, true)]
public async Task ArrayModelBinder_CreatesEmptyCollection_IfIsTopLevelObject( public async Task ArrayModelBinder_CreatesEmptyCollection_IfIsTopLevelObject(
bool allowValidatingTopLevelNodes, bool allowValidatingTopLevelNodes,
bool isBindingRequired) bool isBindingRequired)
{ {
// Arrange // Arrange
var expectedErrorCount = isBindingRequired ? 1 : 0;
var binder = new ArrayModelBinder<string>( var binder = new ArrayModelBinder<string>(
new SimpleTypeModelBinder(typeof(string), NullLoggerFactory.Instance), new SimpleTypeModelBinder(typeof(string), NullLoggerFactory.Instance),
NullLoggerFactory.Instance, NullLoggerFactory.Instance,
@ -81,7 +83,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
// Assert // Assert
Assert.Empty(Assert.IsType<string[]>(bindingContext.Result.Model)); Assert.Empty(Assert.IsType<string[]>(bindingContext.Result.Model));
Assert.True(bindingContext.Result.IsModelSet); Assert.True(bindingContext.Result.IsModelSet);
Assert.Equal(0, bindingContext.ModelState.ErrorCount); Assert.Equal(expectedErrorCount, bindingContext.ModelState.ErrorCount);
} }
[Fact] [Fact]
@ -218,30 +220,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
} }
} }
private static IModelBinder CreateIntBinder()
{
return new StubModelBinder(context =>
{
var value = context.ValueProvider.GetValue(context.ModelName);
if (value != ValueProviderResult.None)
{
object valueToConvert = null;
if (value.Values.Count == 1)
{
valueToConvert = value.Values[0];
}
else if (value.Values.Count > 1)
{
valueToConvert = value.Values.ToArray();
}
var model = ModelBindingHelper.ConvertTo(valueToConvert, context.ModelType, value.Culture);
return ModelBindingResult.Success(model);
}
return ModelBindingResult.Failed();
});
}
private static DefaultModelBindingContext GetBindingContext(IValueProvider valueProvider) private static DefaultModelBindingContext GetBindingContext(IValueProvider valueProvider)
{ {
var bindingContext = CreateContext(); var bindingContext = CreateContext();
@ -277,4 +255,3 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
} }
} }
} }

View File

@ -66,17 +66,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
Assert.IsType<CollectionModelBinder<int>>(result); Assert.IsType<CollectionModelBinder<int>>(result);
} }
[Theory] [Fact]
[InlineData(false)] public void Create_ForSupportedType_ReturnsBinder()
[InlineData(true)]
public void Create_ForSupportedType_ReturnsBinder_WithExpectedAllowValidatingTopLevelNodes(
bool allowValidatingTopLevelNodes)
{ {
// Arrange // Arrange
var provider = new CollectionModelBinderProvider(); var provider = new CollectionModelBinderProvider();
var context = new TestModelBinderProviderContext(typeof(List<int>)); var context = new TestModelBinderProviderContext(typeof(List<int>));
context.MvcOptions.AllowValidatingTopLevelNodes = allowValidatingTopLevelNodes;
context.OnCreatingBinder(m => context.OnCreatingBinder(m =>
{ {
Assert.Equal(typeof(int), m.ModelType); Assert.Equal(typeof(int), m.ModelType);
@ -88,7 +84,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
// Assert // Assert
var binder = Assert.IsType<CollectionModelBinder<int>>(result); var binder = Assert.IsType<CollectionModelBinder<int>>(result);
Assert.Equal(allowValidatingTopLevelNodes, binder.AllowValidatingTopLevelNodes); Assert.True(binder.AllowValidatingTopLevelNodes);
} }
private class Person private class Person

View File

@ -55,17 +55,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
Assert.IsType<ComplexTypeModelBinder>(result); Assert.IsType<ComplexTypeModelBinder>(result);
} }
[Theory] [Fact]
[InlineData(false)] public void Create_ForSupportedType_ReturnsBinder()
[InlineData(true)]
public void Create_ForSupportedType_ReturnsBinder_WithExpectedAllowValidatingTopLevelNodes(
bool allowValidatingTopLevelNodes)
{ {
// Arrange // Arrange
var provider = new ComplexTypeModelBinderProvider(); var provider = new ComplexTypeModelBinderProvider();
var context = new TestModelBinderProviderContext(typeof(Person)); var context = new TestModelBinderProviderContext(typeof(Person));
context.MvcOptions.AllowValidatingTopLevelNodes = allowValidatingTopLevelNodes;
context.OnCreatingBinder(m => context.OnCreatingBinder(m =>
{ {
if (m.ModelType == typeof(int) || m.ModelType == typeof(string)) if (m.ModelType == typeof(int) || m.ModelType == typeof(string))
@ -83,8 +79,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
var result = provider.GetBinder(context); var result = provider.GetBinder(context);
// Assert // Assert
var binder = Assert.IsType<ComplexTypeModelBinder>(result); Assert.IsType<ComplexTypeModelBinder>(result);
Assert.Equal(allowValidatingTopLevelNodes, binder.AllowValidatingTopLevelNodes);
} }
private class Person private class Person

View File

@ -57,40 +57,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
var result = provider.GetBinder(context); var result = provider.GetBinder(context);
// Assert // Assert
Assert.IsType<DictionaryModelBinder<string, int>>(result); var binder = Assert.IsType<DictionaryModelBinder<string, int>>(result);
} Assert.False(binder.AllowValidatingTopLevelNodes); // work done in DictionaryModelBinder.
[Theory]
[InlineData(false)]
[InlineData(true)]
public void Create_ForDictionaryType_ReturnsBinder_WithExpectedAllowValidatingTopLevelNodes(
bool allowValidatingTopLevelNodes)
{
// Arrange
var provider = new DictionaryModelBinderProvider();
var context = new TestModelBinderProviderContext(typeof(Dictionary<string, string>));
context.MvcOptions.AllowValidatingTopLevelNodes = allowValidatingTopLevelNodes;
context.OnCreatingBinder(m =>
{
if (m.ModelType == typeof(KeyValuePair<string, string>) || m.ModelType == typeof(string))
{
return Mock.Of<IModelBinder>();
}
else
{
Assert.False(true, "Not the right model type");
return null;
}
});
// Act
var result = provider.GetBinder(context);
// Assert
var binder = Assert.IsType<DictionaryModelBinder<string, string>>(result);
Assert.Equal(allowValidatingTopLevelNodes, binder.AllowValidatingTopLevelNodes);
Assert.False(((CollectionModelBinder<KeyValuePair<string, string>>)binder).AllowValidatingTopLevelNodes);
} }
private class Person private class Person

View File

@ -349,11 +349,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
[InlineData(false, false)] [InlineData(false, false)]
[InlineData(false, true)] [InlineData(false, true)]
[InlineData(true, false)] [InlineData(true, false)]
[InlineData(true, true)]
public async Task DictionaryModelBinder_CreatesEmptyCollection_IfIsTopLevelObject( public async Task DictionaryModelBinder_CreatesEmptyCollection_IfIsTopLevelObject(
bool allowValidatingTopLevelNodes, bool allowValidatingTopLevelNodes,
bool isBindingRequired) bool isBindingRequired)
{ {
// Arrange // Arrange
var expectedErrorCount = isBindingRequired ? 1 : 0;
var binder = new DictionaryModelBinder<string, string>( var binder = new DictionaryModelBinder<string, string>(
new SimpleTypeModelBinder(typeof(string), NullLoggerFactory.Instance), new SimpleTypeModelBinder(typeof(string), NullLoggerFactory.Instance),
new SimpleTypeModelBinder(typeof(string), NullLoggerFactory.Instance), new SimpleTypeModelBinder(typeof(string), NullLoggerFactory.Instance),
@ -383,7 +385,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
// Assert // Assert
Assert.Empty(Assert.IsType<Dictionary<string, string>>(bindingContext.Result.Model)); Assert.Empty(Assert.IsType<Dictionary<string, string>>(bindingContext.Result.Model));
Assert.True(bindingContext.Result.IsModelSet); Assert.True(bindingContext.Result.IsModelSet);
Assert.Equal(0, bindingContext.ModelState.ErrorCount); Assert.Equal(expectedErrorCount, bindingContext.ModelState.ErrorCount);
} }
[Fact] [Fact]

View File

@ -14,55 +14,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{ {
public class HeaderModelBinderProviderTest public class HeaderModelBinderProviderTest
{ {
[Theory]
[InlineData(typeof(string))]
[InlineData(typeof(string[]))]
[InlineData(typeof(List<string>))]
public void Create_WhenBindingSourceIsFromHeader_ReturnsBinder_ForStringTypes_And_CompatVersion_2_0(
Type modelType)
{
// Arrange
var provider = new HeaderModelBinderProvider();
var testBinder = Mock.Of<IModelBinder>();
var context = GetTestModelBinderProviderContext(
modelType,
allowBindingHeaderValuesToNonStringModelTypes: false);
context.BindingInfo.BindingSource = BindingSource.Header;
// Act
var result = provider.GetBinder(context);
// Assert
Assert.IsType<HeaderModelBinder>(result);
}
[Theory]
[InlineData(typeof(int))]
[InlineData(typeof(int?))]
[InlineData(typeof(IEnumerable<int>))]
[InlineData(typeof(double))]
[InlineData(typeof(double?))]
[InlineData(typeof(IEnumerable<double>))]
[InlineData(typeof(CarEnumType))]
[InlineData(typeof(CarEnumType?))]
[InlineData(typeof(IEnumerable<CarEnumType>))]
public void Create_WhenBindingSourceIsFromHeader_ReturnsNull_ForNonStringTypes_And_CompatVersion_2_0(
Type modelType)
{
// Arrange
var provider = new HeaderModelBinderProvider();
var context = GetTestModelBinderProviderContext(
modelType,
allowBindingHeaderValuesToNonStringModelTypes: false);
context.BindingInfo.BindingSource = BindingSource.Header;
// Act
var result = provider.GetBinder(context);
// Assert
Assert.Null(result);
}
public static TheoryData<BindingSource> NonHeaderBindingSources public static TheoryData<BindingSource> NonHeaderBindingSources
{ {
get get
@ -227,13 +178,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
Assert.Null(result); Assert.Null(result);
} }
private TestModelBinderProviderContext GetTestModelBinderProviderContext( private TestModelBinderProviderContext GetTestModelBinderProviderContext(Type modelType)
Type modelType,
bool allowBindingHeaderValuesToNonStringModelTypes = true)
{ {
var context = new TestModelBinderProviderContext(modelType); var context = new TestModelBinderProviderContext(modelType);
var options = context.Services.GetRequiredService<IOptions<MvcOptions>>().Value; var options = context.Services.GetRequiredService<IOptions<MvcOptions>>().Value;
options.AllowBindingHeaderValuesToNonStringModelTypes = allowBindingHeaderValuesToNonStringModelTypes;
return context; return context;
} }

View File

@ -22,11 +22,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{ {
get get
{ {
var data = new TheoryData<HeaderModelBinder>(); var data = new TheoryData<HeaderModelBinder>
{
#pragma warning disable CS0618 #pragma warning disable CS0618
data.Add(new HeaderModelBinder()); new HeaderModelBinder(),
#pragma warning restore CS0618 #pragma warning restore CS0618
data.Add(new HeaderModelBinder(NullLoggerFactory.Instance)); new HeaderModelBinder(NullLoggerFactory.Instance),
};
return data; return data;
} }
} }
@ -175,11 +178,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
} }
[Theory] [Theory]
[InlineData(typeof(CarType?), null)] [InlineData(typeof(CarType?))]
[InlineData(typeof(int?), null)] [InlineData(typeof(int?))]
public async Task HeaderBinder_DoesNotSetModel_ForHeaderNotPresentOnRequest( public async Task HeaderBinder_DoesNotSetModel_ForHeaderNotPresentOnRequest(Type modelType)
Type modelType,
object expectedModel)
{ {
// Arrange // Arrange
var bindingContext = CreateContext(modelType); var bindingContext = CreateContext(modelType);
@ -194,11 +195,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
} }
[Theory] [Theory]
[InlineData(typeof(string[]), null)] [InlineData(typeof(string[]))]
[InlineData(typeof(IEnumerable<string>), null)] [InlineData(typeof(IEnumerable<string>))]
public async Task HeaderBinder_DoesNotCreateEmptyCollection_ForNonTopLevelObjects( public async Task HeaderBinder_DoesNotCreateEmptyCollection_ForNonTopLevelObjects(Type modelType)
Type modelType,
object expectedModel)
{ {
// Arrange // Arrange
var bindingContext = CreateContext(modelType); var bindingContext = CreateContext(modelType);
@ -344,30 +343,21 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
Assert.Equal($"The value '{headerValues[1]}' is not valid.", entry.Errors[1].ErrorMessage); Assert.Equal($"The value '{headerValues[1]}' is not valid.", entry.Errors[1].ErrorMessage);
} }
private static DefaultModelBindingContext CreateContext( private static DefaultModelBindingContext CreateContext(Type modelType)
Type modelType,
bool allowBindingHeaderValuesToNonStringModelTypes = true)
{ {
return CreateContext( return CreateContext(metadata: GetMetadataForType(modelType), valueProvider: null);
metadata: GetMetadataForType(modelType),
valueProvider: null,
allowBindingHeaderValuesToNonStringModelTypes: allowBindingHeaderValuesToNonStringModelTypes);
} }
private static DefaultModelBindingContext CreateContext( private static DefaultModelBindingContext CreateContext(
ModelMetadata metadata, ModelMetadata metadata,
IValueProvider valueProvider = null, IValueProvider valueProvider = null)
bool allowBindingHeaderValuesToNonStringModelTypes = true)
{ {
if (valueProvider == null) if (valueProvider == null)
{ {
valueProvider = Mock.Of<IValueProvider>(); valueProvider = Mock.Of<IValueProvider>();
} }
var options = new MvcOptions() var options = new MvcOptions();
{
AllowBindingHeaderValuesToNonStringModelTypes = allowBindingHeaderValuesToNonStringModelTypes
};
var setup = new MvcCoreMvcOptionsSetup(new TestHttpRequestStreamReaderFactory()); var setup = new MvcCoreMvcOptionsSetup(new TestHttpRequestStreamReaderFactory());
setup.Configure(options); setup.Configure(options);

View File

@ -27,10 +27,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{ {
public class ParameterBinderTest public class ParameterBinderTest
{ {
private static readonly IOptions<MvcOptions> _optionsAccessor = Options.Create(new MvcOptions private static readonly IOptions<MvcOptions> _optionsAccessor = Options.Create(new MvcOptions());
{
AllowValidatingTopLevelNodes = true,
});
public static TheoryData BindModelAsyncData public static TheoryData BindModelAsyncData
{ {
@ -211,37 +208,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
actionContext.ModelState.Single().Value.Errors.Single().ErrorMessage); actionContext.ModelState.Single().Value.Errors.Single().ErrorMessage);
} }
[Fact]
public async Task BindModelAsync_DoesNotEnforceTopLevelBindRequired_IfNotValidatingTopLevelNodes()
{
// Arrange
var actionContext = GetControllerContext();
var mockModelMetadata = CreateMockModelMetadata();
mockModelMetadata.Setup(o => o.IsBindingRequired).Returns(true);
// Bind attribute errors are phrased in terms of the model name, not display name
mockModelMetadata.Setup(o => o.DisplayName).Returns("Ignored Display Name");
// Do not set AllowValidatingTopLevelNodes.
var optionsAccessor = Options.Create(new MvcOptions());
var parameterBinder = CreateParameterBinder(mockModelMetadata.Object, optionsAccessor: optionsAccessor);
var modelBindingResult = ModelBindingResult.Failed();
// Act
var result = await parameterBinder.BindModelAsync(
actionContext,
CreateMockModelBinder(modelBindingResult),
CreateMockValueProvider(),
new ParameterDescriptor { Name = "myParam", ParameterType = typeof(Person) },
mockModelMetadata.Object,
"ignoredvalue");
// Assert
Assert.True(actionContext.ModelState.IsValid);
Assert.Empty(actionContext.ModelState);
}
[Fact] [Fact]
public async Task BindModelAsync_EnforcesTopLevelRequired() public async Task BindModelAsync_EnforcesTopLevelRequired()
{ {
@ -280,43 +246,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
actionContext.ModelState.Single().Value.Errors.Single().ErrorMessage); actionContext.ModelState.Single().Value.Errors.Single().ErrorMessage);
} }
[Fact]
public async Task BindModelAsync_DoesNotEnforceTopLevelRequired_IfNotValidatingTopLevelNodes()
{
// Arrange
var actionContext = GetControllerContext();
var mockModelMetadata = CreateMockModelMetadata();
mockModelMetadata.Setup(o => o.IsRequired).Returns(true);
mockModelMetadata.Setup(o => o.DisplayName).Returns("My Display Name");
mockModelMetadata.Setup(o => o.ValidatorMetadata).Returns(new[]
{
new RequiredAttribute()
});
var validator = new DataAnnotationsModelValidator(
new ValidationAttributeAdapterProvider(),
new RequiredAttribute(),
stringLocalizer: null);
// Do not set AllowValidatingTopLevelNodes.
var optionsAccessor = Options.Create(new MvcOptions());
var parameterBinder = CreateParameterBinder(mockModelMetadata.Object, validator, optionsAccessor);
var modelBindingResult = ModelBindingResult.Success(null);
// Act
var result = await parameterBinder.BindModelAsync(
actionContext,
CreateMockModelBinder(modelBindingResult),
CreateMockValueProvider(),
new ParameterDescriptor { Name = "myParam", ParameterType = typeof(Person) },
mockModelMetadata.Object,
"ignoredvalue");
// Assert
Assert.True(actionContext.ModelState.IsValid);
Assert.Empty(actionContext.ModelState);
}
public static TheoryData<RequiredAttribute, ParameterDescriptor, ModelMetadata> EnforcesTopLevelRequiredDataSet public static TheoryData<RequiredAttribute, ParameterDescriptor, ModelMetadata> EnforcesTopLevelRequiredDataSet
{ {
get get

View File

@ -598,17 +598,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
} }
[Fact] [Fact]
public async Task ReadAsync_DefaultOptions_DoesNotWrapJsonInputExceptions() public async Task ReadAsync_DoNotAllowInputFormatterExceptionMessages_DoesNotWrapJsonInputExceptions()
{ {
// Arrange // Arrange
var formatter = new JsonInputFormatter( var formatter = CreateFormatter(allowInputFormatterExceptionMessages: false);
GetLogger(),
_serializerSettings,
ArrayPool<char>.Shared,
_objectPoolProvider,
new MvcOptions(),
new MvcJsonOptions());
var contentBytes = Encoding.UTF8.GetBytes("{"); var contentBytes = Encoding.UTF8.GetBytes("{");
var httpContext = GetHttpContext(contentBytes); var httpContext = GetHttpContext(contentBytes);

View File

@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Administration/EitherCookie"); var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Administration/EitherCookie");
request.Headers.Add("Cookie", cookie2); request.Headers.Add("Cookie", cookie2);
// Act 2: Will succeed because, with AllowCombiningAuthorizeFilters true, [Authorize] allows either cookie. // Act 2: Will succeed because [Authorize] allows either cookie.
response = await Client.SendAsync(request); response = await Client.SendAsync(request);
// Assert 2 // Assert 2

View File

@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
var actionContext = new ActionContext(GetHttpContext(), new RouteData(), new ControllerActionDescriptor()); var actionContext = new ActionContext(GetHttpContext(), new RouteData(), new ControllerActionDescriptor());
var authorizationFilterContext = new AuthorizationFilterContext(actionContext, action.Filters); var authorizationFilterContext = new AuthorizationFilterContext(actionContext, new[] { authorizeFilter });
// Act // Act
await authorizeFilter.OnAuthorizationAsync(authorizationFilterContext); await authorizeFilter.OnAuthorizationAsync(authorizationFilterContext);
@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
var authorizeData = action.Attributes.OfType<AuthorizeAttribute>(); var authorizeData = action.Attributes.OfType<AuthorizeAttribute>();
var authorizeFilter = new AuthorizeFilter(policyProvider, authorizeData); var authorizeFilter = new AuthorizeFilter(policyProvider, authorizeData);
var actionContext = new ActionContext(GetHttpContext(combineAuthorize: true), new RouteData(), new ControllerActionDescriptor()); var actionContext = new ActionContext(GetHttpContext(), new RouteData(), new ControllerActionDescriptor());
var authorizationFilterContext = new AuthorizationFilterContext(actionContext, action.Filters); var authorizationFilterContext = new AuthorizationFilterContext(actionContext, action.Filters);
@ -89,11 +89,13 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
Assert.Equal(4, policyProvider.GetPolicyCount); Assert.Equal(4, policyProvider.GetPolicyCount);
} }
private HttpContext GetHttpContext(bool combineAuthorize = false) private HttpContext GetHttpContext()
{ {
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext
{
RequestServices = GetServices()
};
httpContext.RequestServices = GetServices(combineAuthorize);
return httpContext; return httpContext;
} }
@ -108,11 +110,11 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
return context; return context;
} }
private static IServiceProvider GetServices(bool combineAuthorize) private static IServiceProvider GetServices()
{ {
var serviceCollection = new ServiceCollection(); var serviceCollection = new ServiceCollection();
serviceCollection.AddAuthorization(); serviceCollection.AddAuthorization();
serviceCollection.AddMvc(o => o.AllowCombiningAuthorizeFilters = combineAuthorize); serviceCollection.AddMvc();
serviceCollection serviceCollection
.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance) .AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance)
.AddTransient<ILogger<DefaultAuthorizationService>, Logger<DefaultAuthorizationService>>() .AddTransient<ILogger<DefaultAuthorizationService>, Logger<DefaultAuthorizationService>>()

View File

@ -189,51 +189,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
Assert.NotNull(entry.RawValue); // Value is set by test model binder, no need to validate it. Assert.NotNull(entry.RawValue); // Value is set by test model binder, no need to validate it.
} }
// Make sure the metadata is honored when a [ModelBinder] attribute is associated with an action parameter's
// type. This should behave identically to such an attribute on an action parameter. (Tests such as
// BindParameter_WithData_WithPrefix_GetsBound cover associating [ModelBinder] with an action parameter.)
//
// This is a regression test for aspnet/Mvc#4652
[Theory]
[MemberData(nameof(NullAndEmptyBindingInfo))]
public async Task BinderTypeOnParameterType_WithDataEmptyPrefixAndVersion20_GetsBound(
BindingInfo bindingInfo)
{
// Arrange
var testContext = ModelBindingTestHelper.GetTestContext(
// ParameterBinder will use ModelMetadata for typeof(Address), not Parameter1's ParameterInfo.
updateOptions: options => options.AllowValidatingTopLevelNodes = false);
var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameter = new ParameterDescriptor
{
Name = "Parameter1",
BindingInfo = bindingInfo,
ParameterType = typeof(Address),
};
// Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
// Assert
// ModelBindingResult
Assert.True(modelBindingResult.IsModelSet);
// Model
var address = Assert.IsType<Address>(modelBindingResult.Model);
Assert.Equal("SomeStreet", address.Street);
// ModelState
Assert.True(modelState.IsValid);
var kvp = Assert.Single(modelState);
Assert.Equal("Street", kvp.Key);
var entry = kvp.Value;
Assert.NotNull(entry);
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
Assert.NotNull(entry.RawValue); // Value is set by test model binder, no need to validate it.
}
private class Person3 private class Person3
{ {
[ModelBinder(BinderType = typeof(Address3ModelBinder))] [ModelBinder(BinderType = typeof(Address3ModelBinder))]

View File

@ -41,8 +41,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
} }
ModelMetadata metadata; ModelMetadata metadata;
if (optionsAccessor.Value.AllowValidatingTopLevelNodes && if (modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase &&
modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase &&
parameterInfo != null) parameterInfo != null)
{ {
metadata = modelMetadataProviderBase.GetMetadataForParameter(parameterInfo); metadata = modelMetadataProviderBase.GetMetadataForParameter(parameterInfo);

View File

@ -1705,7 +1705,8 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
public async Task FromBody_JToken_ExcludedFromValidation() public async Task FromBody_JToken_ExcludedFromValidation()
{ {
// Arrange // Arrange
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(new TestMvcOptions().Value); var options = new TestMvcOptions().Value;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(options);
var parameter = new ParameterDescriptor var parameter = new ParameterDescriptor
{ {
Name = "Parameter1", Name = "Parameter1",
@ -1718,11 +1719,12 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
}; };
var testContext = ModelBindingTestHelper.GetTestContext( var testContext = ModelBindingTestHelper.GetTestContext(
request => updateRequest: request =>
{ {
request.Body = new MemoryStream(Encoding.UTF8.GetBytes("{ message: \"Hello\" }")); request.Body = new MemoryStream(Encoding.UTF8.GetBytes("{ message: \"Hello\" }"));
request.ContentType = "application/json"; request.ContentType = "application/json";
}); },
mvcOptions: options);
var httpContext = testContext.HttpContext; var httpContext = testContext.HttpContext;
var modelState = testContext.ModelState; var modelState = testContext.ModelState;

View File

@ -169,10 +169,11 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{ {
var defaultProvider = new DefaultPageApplicationModelProvider( var defaultProvider = new DefaultPageApplicationModelProvider(
TestModelMetadataProvider.CreateDefaultProvider(), TestModelMetadataProvider.CreateDefaultProvider(),
Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = true }),
Options.Create(new RazorPagesOptions { AllowDefaultHandlingForOptionsRequests = true })); Options.Create(new RazorPagesOptions { AllowDefaultHandlingForOptionsRequests = 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

@ -881,42 +881,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
}); });
} }
[Fact]
public void CreateHandlerMethods_WithLegacyValidationBehavior_AddsParameterDescriptors()
{
// Arrange
var provider = new DefaultPageApplicationModelProvider(
TestModelMetadataProvider.CreateDefaultProvider(),
Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = false }),
Options.Create(new RazorPagesOptions()));
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);
});
}
private class PageWithHandlerParameters private class PageWithHandlerParameters
{ {
public void OnPost(string name, [ModelBinder(Name = "personId")] int id) { } public void OnPost(string name, [ModelBinder(Name = "personId")] int id) { }
@ -1078,8 +1042,8 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
// Arrange // Arrange
var provider = new DefaultPageApplicationModelProvider( var provider = new DefaultPageApplicationModelProvider(
TestModelMetadataProvider.CreateDefaultProvider(), TestModelMetadataProvider.CreateDefaultProvider(),
Options.Create(new MvcOptions()),
Options.Create(new RazorPagesOptions())); Options.Create(new RazorPagesOptions()));
var typeInfo = typeof(object).GetTypeInfo(); var typeInfo = typeof(object).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true)); var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true));
@ -1225,7 +1189,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{ {
return new DefaultPageApplicationModelProvider( return new DefaultPageApplicationModelProvider(
TestModelMetadataProvider.CreateDefaultProvider(), TestModelMetadataProvider.CreateDefaultProvider(),
Options.Create(new MvcOptions { AllowValidatingTopLevelNodes = true }),
Options.Create(new RazorPagesOptions { AllowDefaultHandlingForOptionsRequests = true })); Options.Create(new RazorPagesOptions { AllowDefaultHandlingForOptionsRequests = true }));
} }
} }

View File

@ -6,7 +6,6 @@ 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.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
@ -145,10 +144,11 @@ namespace Microsoft.AspNetCore.Mvc.Filters
{ {
var defaultProvider = new DefaultPageApplicationModelProvider( var defaultProvider = new DefaultPageApplicationModelProvider(
TestModelMetadataProvider.CreateDefaultProvider(), TestModelMetadataProvider.CreateDefaultProvider(),
Options.Create(new MvcOptions()),
Options.Create(new RazorPagesOptions { AllowDefaultHandlingForOptionsRequests = true })); Options.Create(new RazorPagesOptions { AllowDefaultHandlingForOptionsRequests = 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

@ -5,7 +5,6 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
@ -461,10 +460,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var modelBinderFactory = TestModelBinderFactory.CreateDefault(); var modelBinderFactory = TestModelBinderFactory.CreateDefault();
var mvcOptions = new MvcOptions var mvcOptions = new MvcOptions();
{
AllowValidatingTopLevelNodes = true,
};
var parameterBinder = new ParameterBinder( var parameterBinder = new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,

View File

@ -1544,15 +1544,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
} }
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var mvcOptions = new MvcOptions var mvcOptions = new MvcOptions();
{
AllowValidatingTopLevelNodes = true,
};
return new ParameterBinder( return new ParameterBinder(
metadataProvider, metadataProvider,
factory, factory,
new DefaultObjectValidator(metadataProvider, new[] { validator }, new MvcOptions()), new DefaultObjectValidator(metadataProvider, new[] { validator }, mvcOptions),
Options.Create(mvcOptions), Options.Create(mvcOptions),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
} }

View File

@ -21,10 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{ {
public class PageBinderFactoryTest public class PageBinderFactoryTest
{ {
private static readonly MvcOptions _options = new MvcOptions private static readonly MvcOptions _options = new MvcOptions();
{
AllowValidatingTopLevelNodes = true,
};
private static readonly IOptions<MvcOptions> _optionsAccessor = Options.Create(_options); private static readonly IOptions<MvcOptions> _optionsAccessor = Options.Create(_options);
[Fact] [Fact]
@ -712,56 +709,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
}); });
} }
[Fact]
public async Task CreateHandlerBinder_DoesNotValidateTopLevelParameters_IfDisabled()
{
// Arrange
var type = typeof(PageModelWithExecutors);
var actionDescriptor = GetActionDescriptorWithHandlerMethod(
type,
nameof(PageModelWithExecutors.OnPostWithValidation));
// Act
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var modelBinderFactory = TestModelBinderFactory.CreateDefault();
var mvcOptions = new MvcOptions();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
modelBinderFactory,
new DefaultObjectValidator(
modelMetadataProvider,
new[] { TestModelValidatorProvider.CreateDefaultProvider() },
new MvcOptions()),
Options.Create(mvcOptions),
NullLoggerFactory.Instance);
var factory = PageBinderFactory.CreateHandlerBinder(
parameterBinder,
modelMetadataProvider,
modelBinderFactory,
actionDescriptor,
actionDescriptor.HandlerMethods[0],
mvcOptions);
var page = new PageWithProperty
{
PageContext = GetPageContext()
};
var model = new PageModelWithExecutors();
var arguments = new Dictionary<string, object>();
// Act
await factory(page.PageContext, arguments);
// Assert
var modelState = page.PageContext.ModelState;
Assert.True(modelState.IsValid);
Assert.Empty(modelState);
}
private static CompiledPageActionDescriptor GetActionDescriptorWithHandlerMethod(Type type, string method) private static CompiledPageActionDescriptor GetActionDescriptorWithHandlerMethod(Type type, string method)
{ {
var handlerMethodInfo = type.GetMethod(method); var handlerMethodInfo = type.GetMethod(method);
@ -923,24 +870,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
public string PropertyWithNoValue { get; set; } public string PropertyWithNoValue { get; set; }
} }
private class PageModelWithModelBinderAttribute
{
[ModelBinder(BinderType = typeof(DeclarativeSecurityAction))]
public Guid PropertyWithBinderType { get; set; }
}
private class PageModelWithPropertyFilterAttribute
{
[ModelBinder]
[TestPropertyFilterProvider]
public object PropertyWithFilter { get; set; }
}
private class TestPropertyFilterProvider : Attribute, IPropertyFilterProvider
{
public Func<ModelMetadata, bool> PropertyFilter => _ => true;
}
private class PageModelWithDefaultValue private class PageModelWithDefaultValue
{ {
[ModelBinder] [ModelBinder]

View File

@ -43,20 +43,12 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
var xmlOptions = services.GetRequiredService<IOptions<MvcXmlOptions>>().Value; var xmlOptions = services.GetRequiredService<IOptions<MvcXmlOptions>>().Value;
// Assert // Assert
Assert.True(mvcOptions.AllowCombiningAuthorizeFilters);
Assert.True(mvcOptions.AllowBindingHeaderValuesToNonStringModelTypes);
Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType); Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType);
Assert.Equal(InputFormatterExceptionPolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionPolicy); Assert.Equal(InputFormatterExceptionPolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionPolicy);
Assert.True(jsonOptions.AllowInputFormatterExceptionMessages);
Assert.True(razorPagesOptions.AllowAreas); Assert.True(razorPagesOptions.AllowAreas);
Assert.True(mvcOptions.EnableEndpointRouting);
Assert.Equal(32, mvcOptions.MaxValidationDepth);
Assert.False(apiBehaviorOptions.SuppressUseValidationProblemDetailsForInvalidModelStateResponses);
Assert.False(apiBehaviorOptions.SuppressMapClientErrors);
Assert.True(razorPagesOptions.AllowDefaultHandlingForOptionsRequests); Assert.True(razorPagesOptions.AllowDefaultHandlingForOptionsRequests);
Assert.True(xmlOptions.AllowRfc7807CompliantProblemDetailsFormat); Assert.True(xmlOptions.AllowRfc7807CompliantProblemDetailsFormat);
Assert.True(mvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent); Assert.True(mvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent);
Assert.False(apiBehaviorOptions.AllowInferringBindingSourceForCollectionTypesAsFromQuery);
} }
[Fact] [Fact]
@ -81,20 +73,12 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
var xmlOptions = services.GetRequiredService<IOptions<MvcXmlOptions>>().Value; var xmlOptions = services.GetRequiredService<IOptions<MvcXmlOptions>>().Value;
// Assert // Assert
Assert.True(mvcOptions.AllowCombiningAuthorizeFilters);
Assert.True(mvcOptions.AllowBindingHeaderValuesToNonStringModelTypes);
Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType); Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType);
Assert.Equal(InputFormatterExceptionPolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionPolicy); Assert.Equal(InputFormatterExceptionPolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionPolicy);
Assert.True(jsonOptions.AllowInputFormatterExceptionMessages);
Assert.True(razorPagesOptions.AllowAreas); Assert.True(razorPagesOptions.AllowAreas);
Assert.True(mvcOptions.EnableEndpointRouting);
Assert.Equal(32, mvcOptions.MaxValidationDepth);
Assert.False(apiBehaviorOptions.SuppressUseValidationProblemDetailsForInvalidModelStateResponses);
Assert.False(apiBehaviorOptions.SuppressMapClientErrors);
Assert.True(razorPagesOptions.AllowDefaultHandlingForOptionsRequests); Assert.True(razorPagesOptions.AllowDefaultHandlingForOptionsRequests);
Assert.True(xmlOptions.AllowRfc7807CompliantProblemDetailsFormat); Assert.True(xmlOptions.AllowRfc7807CompliantProblemDetailsFormat);
Assert.True(mvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent); Assert.True(mvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent);
Assert.False(apiBehaviorOptions.AllowInferringBindingSourceForCollectionTypesAsFromQuery);
} }
// This just does the minimum needed to be able to resolve these options. // This just does the minimum needed to be able to resolve these options.

View File

@ -389,13 +389,6 @@ namespace Microsoft.AspNetCore.Mvc
typeof(RazorPagesOptionsConfigureCompatibilityOptions), typeof(RazorPagesOptionsConfigureCompatibilityOptions),
} }
}, },
{
typeof(IPostConfigureOptions<MvcJsonOptions>),
new[]
{
typeof(MvcJsonOptionsConfigureCompatibilityOptions),
}
},
{ {
typeof(IActionConstraintProvider), typeof(IActionConstraintProvider),
new Type[] new Type[]

View File

@ -16,7 +16,7 @@ namespace BasicWebSite
services.AddRouting(); services.AddRouting();
services.AddMvc() services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Latest) // this compat version enables endpoint routing .SetCompatibilityVersion(CompatibilityVersion.Latest)
.AddXmlDataContractSerializerFormatters(); .AddXmlDataContractSerializerFormatters();
services.ConfigureBaseWebSiteAuthPolicies(); services.ConfigureBaseWebSiteAuthPolicies();

View File

@ -20,8 +20,7 @@ namespace SecurityWebSite.Controllers
return Content("Administration.Index"); return Content("Administration.Index");
} }
// Either cookie should allow access to this action (if AllowCombiningAuthorizeFilters is true) // Either cookie should allow access to this action.
// If AllowCombiningAuthorizeFilters is false, the main cookie is required.
[Authorize(AuthenticationSchemes = "Cookie2")] [Authorize(AuthenticationSchemes = "Cookie2")]
public IActionResult EitherCookie() public IActionResult EitherCookie()
{ {