Add compatibility switch controlling parameter metadata and top-level validation

- #7413 part 1 of 2
- made all `ModelMetadataProvider` and `ObjectModelValidator`-specific code conditional
  - fortunately, `MvcOptions` easy to get; affected code is primarily `internal` or pub-`Internal`
  - remove unnecessary `ModelMetadataProvider` use in `ApiBehaviorApplicationModelProvider`
- run integration and functional tests with `CompatibilityVersion.Version_2_1`
  - functional test change depends on @javiercn's recent #7541 fix
  - remove test code now redundantly turning compatibility switches on

nits:
- correct spelling errors in `CompatibilitySwitch`
- take VS suggestions, mostly in test code
- rename methods in `ControllerBinderDelegateProviderTest` to match current API
- slightly refactor in `ApiBehaviorApplicationModelProvider`
This commit is contained in:
Doug Bunting 2018-03-23 16:09:53 -07:00
parent 51784bb2d6
commit 5e245da326
No known key found for this signature in database
GPG Key ID: 888B4EB7822B32E9
31 changed files with 672 additions and 351 deletions

View File

@ -24,10 +24,9 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
/// </summary> /// </summary>
public class DefaultApiDescriptionProvider : IApiDescriptionProvider public class DefaultApiDescriptionProvider : IApiDescriptionProvider
{ {
private readonly IList<IInputFormatter> _inputFormatters; private readonly MvcOptions _mvcOptions;
private readonly IList<IOutputFormatter> _outputFormatters;
private readonly IModelMetadataProvider _modelMetadataProvider;
private readonly IInlineConstraintResolver _constraintResolver; private readonly IInlineConstraintResolver _constraintResolver;
private readonly IModelMetadataProvider _modelMetadataProvider;
/// <summary> /// <summary>
/// Creates a new instance of <see cref="DefaultApiDescriptionProvider"/>. /// Creates a new instance of <see cref="DefaultApiDescriptionProvider"/>.
@ -41,8 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
IInlineConstraintResolver constraintResolver, IInlineConstraintResolver constraintResolver,
IModelMetadataProvider modelMetadataProvider) IModelMetadataProvider modelMetadataProvider)
{ {
_inputFormatters = optionsAccessor.Value.InputFormatters; _mvcOptions = optionsAccessor.Value;
_outputFormatters = optionsAccessor.Value.OutputFormatters;
_constraintResolver = constraintResolver; _constraintResolver = constraintResolver;
_modelMetadataProvider = modelMetadataProvider; _modelMetadataProvider = modelMetadataProvider;
} }
@ -164,7 +162,8 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
var visitor = new PseudoModelBindingVisitor(context, actionParameter); var visitor = new PseudoModelBindingVisitor(context, actionParameter);
ModelMetadata metadata = null; ModelMetadata metadata = null;
if (actionParameter is ControllerParameterDescriptor controllerParameterDescriptor && if (_mvcOptions.AllowValidatingTopLevelNodes &&
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
@ -344,7 +343,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
var results = new List<ApiRequestFormat>(); var results = new List<ApiRequestFormat>();
foreach (var contentType in contentTypes) foreach (var contentType in contentTypes)
{ {
foreach (var formatter in _inputFormatters) foreach (var formatter in _mvcOptions.InputFormatters)
{ {
if (formatter is IApiRequestFormatMetadataProvider requestFormatMetadataProvider) if (formatter is IApiRequestFormatMetadataProvider requestFormatMetadataProvider)
{ {
@ -422,7 +421,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
contentTypes.Add((string)null); contentTypes.Add((string)null);
} }
var responseTypeMetadataProviders = _outputFormatters.OfType<IApiResponseTypeMetadataProvider>(); var responseTypeMetadataProviders = _mvcOptions.OutputFormatters.OfType<IApiResponseTypeMetadataProvider>();
foreach (var objectType in objectTypes) foreach (var objectType in objectTypes)
{ {

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
// behaviors in a minor release. // behaviors in a minor release.
// //
// To make things better for future users, we also want to provide an easy way for applications to get // To make things better for future users, we also want to provide an easy way for applications to get
// access to the new behaviors. We make changes when they are improvments, and if we're changing something // access to the new behaviors. We make changes when they are improvements, and if we're changing something
// we've already shipped, it must add value for all of our users (eventually). To this end, new applications // we've already shipped, it must add value for all of our users (eventually). To this end, new applications
// created using the template are always opted in to the 'current' version. // created using the template are always opted in to the 'current' version.
// //
@ -48,19 +48,19 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
// //
/// <summary> /// <summary>
/// Infrastructure supporting the implementation of <see cref="CompatibilityVersion"/>. This is an /// Infrastructure supporting the implementation of <see cref="CompatibilityVersion"/>. This is an
/// implementation of <see cref="ICompatibilitySwitch"/> suitible for use with the <see cref="IOptions{T}"/> /// implementation of <see cref="ICompatibilitySwitch"/> suitable for use with the <see cref="IOptions{T}"/>
/// pattern. This is framework infrastructure and should not be used by application code. /// pattern. This is framework infrastructure and should not be used by application code.
/// </summary> /// </summary>
/// <typeparam name="TValue">The type of value assoicated with the compatibility switch.</typeparam> /// <typeparam name="TValue">The type of value associated with the compatibility switch.</typeparam>
public class CompatibilitySwitch<TValue> : ICompatibilitySwitch where TValue : struct public class CompatibilitySwitch<TValue> : ICompatibilitySwitch where TValue : struct
{ {
private TValue _value; private TValue _value;
/// <summary> /// <summary>
/// Creates a new compatiblity switch with the provided name. /// Creates a new compatibility switch with the provided name.
/// </summary> /// </summary>
/// <param name="name"> /// <param name="name">
/// The compatiblity switch name. The name must match a property name on an options type. /// The compatibility switch name. The name must match a property name on an options type.
/// </param> /// </param>
public CompatibilitySwitch(string name) public CompatibilitySwitch(string name)
: this(name, default) : this(name, default)
@ -68,10 +68,10 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
} }
/// <summary> /// <summary>
/// Creates a new compatiblity switch with the provided name and initial value. /// Creates a new compatibility switch with the provided name and initial value.
/// </summary> /// </summary>
/// <param name="name"> /// <param name="name">
/// The compatiblity switch name. The name must match a property name on an options type. /// The compatibility switch name. The name must match a property name on an options type.
/// </param> /// </param>
/// <param name="initialValue"> /// <param name="initialValue">
/// The initial value to assign to the switch. /// The initial value to assign to the switch.

View File

@ -27,6 +27,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
{ {
values[nameof(MvcOptions.AllowCombiningAuthorizeFilters)] = true; values[nameof(MvcOptions.AllowCombiningAuthorizeFilters)] = true;
values[nameof(MvcOptions.AllowBindingHeaderValuesToNonStringModelTypes)] = true; values[nameof(MvcOptions.AllowBindingHeaderValuesToNonStringModelTypes)] = true;
values[nameof(MvcOptions.AllowValidatingTopLevelNodes)] = true;
values[nameof(MvcOptions.InputFormatterExceptionPolicy)] = InputFormatterExceptionPolicy.MalformedInputExceptions; values[nameof(MvcOptions.InputFormatterExceptionPolicy)] = InputFormatterExceptionPolicy.MalformedInputExceptions;
values[nameof(MvcOptions.SuppressBindingUndefinedValueToEnumType)] = true; values[nameof(MvcOptions.SuppressBindingUndefinedValueToEnumType)] = true;
} }

View File

@ -216,16 +216,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{ {
foreach (var parameter in actionModel.Parameters) foreach (var parameter in actionModel.Parameters)
{ {
if (parameter.BindingInfo != null && var bindingInfo = parameter.BindingInfo;
parameter.BindingInfo.BinderModelName == null && if (bindingInfo?.BindingSource != null &&
parameter.BindingInfo.BindingSource != null && bindingInfo.BinderModelName == null &&
!parameter.BindingInfo.BindingSource.IsGreedy) !bindingInfo.BindingSource.IsGreedy &&
IsComplexTypeParameter(parameter))
{ {
var metadata = GetParameterMetadata(parameter); parameter.BindingInfo.BinderModelName = string.Empty;
if (metadata.IsComplexType)
{
parameter.BindingInfo.BinderModelName = string.Empty;
}
} }
} }
} }
@ -233,25 +230,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Internal for unit testing. // Internal for unit testing.
internal BindingSource InferBindingSourceForParameter(ParameterModel parameter) internal BindingSource InferBindingSourceForParameter(ParameterModel parameter)
{ {
var parameterType = parameter.ParameterInfo.ParameterType;
if (ParameterExistsInAllRoutes(parameter.Action, parameter.ParameterName)) if (ParameterExistsInAllRoutes(parameter.Action, parameter.ParameterName))
{ {
return BindingSource.Path; return BindingSource.Path;
} }
else
{
var parameterMetadata = GetParameterMetadata(parameter);
if (parameterMetadata != null)
{
var bindingSource = parameterMetadata.IsComplexType ?
BindingSource.Body :
BindingSource.Query;
return bindingSource; var bindingSource = IsComplexTypeParameter(parameter) ?
} BindingSource.Body :
} BindingSource.Query;
return null; return bindingSource;
} }
private bool ParameterExistsInAllRoutes(ActionModel actionModel, string parameterName) private bool ParameterExistsInAllRoutes(ActionModel actionModel, string parameterName)
@ -277,16 +265,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
return parameterExistsInSomeRoute; return parameterExistsInSomeRoute;
} }
private ModelMetadata GetParameterMetadata(ParameterModel parameter) private bool IsComplexTypeParameter(ParameterModel parameter)
{ {
if (_modelMetadataProvider is ModelMetadataProvider modelMetadataProvider) // No need for information from attributes on the parameter. Just use its type.
{ return _modelMetadataProvider
return modelMetadataProvider.GetMetadataForParameter(parameter.ParameterInfo); .GetMetadataForType(parameter.ParameterInfo.ParameterType)
} .IsComplexType;
else
{
return _modelMetadataProvider.GetMetadataForType(parameter.ParameterInfo.ParameterType);
}
} }
} }
} }

View File

@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Internal; using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.Internal namespace Microsoft.AspNetCore.Mvc.Internal
{ {
@ -21,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private readonly IModelMetadataProvider _modelMetadataProvider; private readonly IModelMetadataProvider _modelMetadataProvider;
private readonly IFilterProvider[] _filterProviders; private readonly IFilterProvider[] _filterProviders;
private readonly IControllerFactoryProvider _controllerFactoryProvider; private readonly IControllerFactoryProvider _controllerFactoryProvider;
private readonly MvcOptions _mvcOptions;
private volatile InnerCache _currentCache; private volatile InnerCache _currentCache;
public ControllerActionInvokerCache( public ControllerActionInvokerCache(
@ -30,7 +31,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
IModelBinderFactory modelBinderFactory, IModelBinderFactory modelBinderFactory,
IModelMetadataProvider modelMetadataProvider, IModelMetadataProvider modelMetadataProvider,
IEnumerable<IFilterProvider> filterProviders, IEnumerable<IFilterProvider> filterProviders,
IControllerFactoryProvider factoryProvider) IControllerFactoryProvider factoryProvider,
IOptions<MvcOptions> mvcOptions)
{ {
_collectionProvider = collectionProvider; _collectionProvider = collectionProvider;
_parameterBinder = parameterBinder; _parameterBinder = parameterBinder;
@ -38,6 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
_modelMetadataProvider = modelMetadataProvider; _modelMetadataProvider = modelMetadataProvider;
_filterProviders = filterProviders.OrderBy(item => item.Order).ToArray(); _filterProviders = filterProviders.OrderBy(item => item.Order).ToArray();
_controllerFactoryProvider = factoryProvider; _controllerFactoryProvider = factoryProvider;
_mvcOptions = mvcOptions.Value;
} }
private InnerCache CurrentCache private InnerCache CurrentCache
@ -82,7 +85,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
_parameterBinder, _parameterBinder,
_modelBinderFactory, _modelBinderFactory,
_modelMetadataProvider, _modelMetadataProvider,
actionDescriptor); actionDescriptor,
_mvcOptions);
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor); var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);

View File

@ -16,13 +16,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal
ParameterBinder parameterBinder, ParameterBinder parameterBinder,
IModelBinderFactory modelBinderFactory, IModelBinderFactory modelBinderFactory,
IModelMetadataProvider modelMetadataProvider, IModelMetadataProvider modelMetadataProvider,
ControllerActionDescriptor actionDescriptor) ControllerActionDescriptor actionDescriptor,
MvcOptions mvcOptions)
{ {
if (parameterBinder == null) if (parameterBinder == null)
{ {
throw new ArgumentNullException(nameof(parameterBinder)); throw new ArgumentNullException(nameof(parameterBinder));
} }
if (modelBinderFactory == null)
{
throw new ArgumentNullException(nameof(modelBinderFactory));
}
if (modelMetadataProvider == null) if (modelMetadataProvider == null)
{ {
throw new ArgumentNullException(nameof(modelMetadataProvider)); throw new ArgumentNullException(nameof(modelMetadataProvider));
@ -33,7 +39,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
throw new ArgumentNullException(nameof(actionDescriptor)); throw new ArgumentNullException(nameof(actionDescriptor));
} }
var parameterBindingInfo = GetParameterBindingInfo(modelBinderFactory, modelMetadataProvider, actionDescriptor); if (mvcOptions == null)
{
throw new ArgumentNullException(nameof(mvcOptions));
}
var parameterBindingInfo = GetParameterBindingInfo(
modelBinderFactory,
modelMetadataProvider,
actionDescriptor,
mvcOptions);
var propertyBindingInfo = GetPropertyBindingInfo(modelBinderFactory, modelMetadataProvider, actionDescriptor); var propertyBindingInfo = GetPropertyBindingInfo(modelBinderFactory, modelMetadataProvider, actionDescriptor);
if (parameterBindingInfo == null && propertyBindingInfo == null) if (parameterBindingInfo == null && propertyBindingInfo == null)
@ -104,7 +119,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private static BinderItem[] GetParameterBindingInfo( private static BinderItem[] GetParameterBindingInfo(
IModelBinderFactory modelBinderFactory, IModelBinderFactory modelBinderFactory,
IModelMetadataProvider modelMetadataProvider, IModelMetadataProvider modelMetadataProvider,
ControllerActionDescriptor actionDescriptor) ControllerActionDescriptor actionDescriptor,
MvcOptions mvcOptions)
{ {
var parameters = actionDescriptor.Parameters; var parameters = actionDescriptor.Parameters;
if (parameters.Count == 0) if (parameters.Count == 0)
@ -118,8 +134,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var parameter = parameters[i]; var parameter = parameters[i];
ModelMetadata metadata; ModelMetadata metadata;
if (modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase if (mvcOptions.AllowValidatingTopLevelNodes &&
&& parameter is ControllerParameterDescriptor controllerParameterDescriptor) modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase &&
parameter is ControllerParameterDescriptor controllerParameterDescriptor)
{ {
// 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

@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="ObjectModelValidator"/>. /// Initializes a new instance of <see cref="ObjectModelValidator"/>.
/// </summary> /// </summary>
/// <param name="modelMetadataProvider">The <see cref="ModelMetadataProvider"/>.</param> /// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="validatorProviders">The list of <see cref="IModelValidatorProvider"/>.</param> /// <param name="validatorProviders">The list of <see cref="IModelValidatorProvider"/>.</param>
public ObjectModelValidator( public ObjectModelValidator(
IModelMetadataProvider modelMetadataProvider, IModelMetadataProvider modelMetadataProvider,

View File

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.ModelBinding namespace Microsoft.AspNetCore.Mvc.ModelBinding
{ {
@ -18,23 +19,31 @@ 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>
/// <para>This constructor is obsolete and will be removed in a future version. The recommended alternative /// <para>This constructor is obsolete and will be removed in a future version. The recommended alternative
/// is the overload that also takes an <see cref="ILoggerFactory"/>.</para> /// is the overload that also takes a <see cref="MvcOptions"/> accessor and an <see cref="ILoggerFactory"/>.
/// </para>
/// <para>Initializes a new instance of <see cref="ParameterBinder"/>.</para> /// <para>Initializes a new instance of <see cref="ParameterBinder"/>.</para>
/// </summary> /// </summary>
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param> /// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="modelBinderFactory">The <see cref="IModelBinderFactory"/>.</param> /// <param name="modelBinderFactory">The <see cref="IModelBinderFactory"/>.</param>
/// <param name="validator">The <see cref="IObjectModelValidator"/>.</param> /// <param name="validator">The <see cref="IObjectModelValidator"/>.</param>
[Obsolete("This constructor is obsolete and will be removed in a future version. The recommended alternative" [Obsolete("This constructor is obsolete and will be removed in a future version. The recommended alternative"
+ " is the overload that also takes an " + nameof(ILoggerFactory) + ".")] + " is the overload that also takes a " + nameof(MvcOptions) + " accessor and an "
+ nameof(ILoggerFactory) + " .")]
public ParameterBinder( public ParameterBinder(
IModelMetadataProvider modelMetadataProvider, IModelMetadataProvider modelMetadataProvider,
IModelBinderFactory modelBinderFactory, IModelBinderFactory modelBinderFactory,
IObjectModelValidator validator) IObjectModelValidator validator)
: this(modelMetadataProvider, modelBinderFactory, validator, NullLoggerFactory.Instance) : this(
modelMetadataProvider,
modelBinderFactory,
validator,
Options.Create(new MvcOptions()),
NullLoggerFactory.Instance)
{ {
} }
@ -44,11 +53,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param> /// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="modelBinderFactory">The <see cref="IModelBinderFactory"/>.</param> /// <param name="modelBinderFactory">The <see cref="IModelBinderFactory"/>.</param>
/// <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="loggerFactory">The <see cref="ILoggerFactory"/>.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public ParameterBinder( public ParameterBinder(
IModelMetadataProvider modelMetadataProvider, IModelMetadataProvider modelMetadataProvider,
IModelBinderFactory modelBinderFactory, IModelBinderFactory modelBinderFactory,
IObjectModelValidator validator, IObjectModelValidator validator,
IOptions<MvcOptions> mvcOptions,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
{ {
if (modelMetadataProvider == null) if (modelMetadataProvider == null)
@ -66,6 +77,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
throw new ArgumentNullException(nameof(validator)); throw new ArgumentNullException(nameof(validator));
} }
if (mvcOptions == null)
{
throw new ArgumentNullException(nameof(mvcOptions));
}
if (loggerFactory == null) if (loggerFactory == null)
{ {
throw new ArgumentNullException(nameof(loggerFactory)); throw new ArgumentNullException(nameof(loggerFactory));
@ -74,6 +90,7 @@ 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());
} }
@ -224,8 +241,21 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var modelBindingResult = modelBindingContext.Result; var modelBindingResult = modelBindingContext.Result;
var baseObjectValidator = _objectModelValidator as ObjectModelValidator; if (_mvcOptions.AllowValidatingTopLevelNodes &&
if (baseObjectValidator == null) _objectModelValidator is ObjectModelValidator baseObjectValidator)
{
Logger.AttemptingToValidateParameterOrProperty(parameter, modelBindingContext);
EnforceBindRequiredAndValidate(
baseObjectValidator,
actionContext,
metadata,
modelBindingContext,
modelBindingResult);
Logger.DoneAttemptingToValidateParameterOrProperty(parameter, modelBindingContext);
}
else
{ {
// For legacy implementations (which directly implemented IObjectModelValidator), fall back to the // For legacy implementations (which directly implemented IObjectModelValidator), fall back to the
// back-compatibility logic. In this scenario, top-level validation attributes will be ignored like // back-compatibility logic. In this scenario, top-level validation attributes will be ignored like
@ -239,19 +269,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
modelBindingResult.Model); modelBindingResult.Model);
} }
} }
else
{
Logger.AttemptingToValidateParameterOrProperty(parameter, modelBindingContext);
EnforceBindRequiredAndValidate(
baseObjectValidator,
actionContext,
metadata,
modelBindingContext,
modelBindingResult);
Logger.DoneAttemptingToValidateParameterOrProperty(parameter, modelBindingContext);
}
return modelBindingResult; return modelBindingResult;
} }

View File

@ -27,6 +27,7 @@ namespace Microsoft.AspNetCore.Mvc
// 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> _allowBindingHeaderValuesToNonStringModelTypes;
private readonly CompatibilitySwitch<bool> _allowCombiningAuthorizeFilters; 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 ICompatibilitySwitch[] _switches; private readonly ICompatibilitySwitch[] _switches;
@ -50,6 +51,7 @@ namespace Microsoft.AspNetCore.Mvc
_allowCombiningAuthorizeFilters = new CompatibilitySwitch<bool>(nameof(AllowCombiningAuthorizeFilters)); _allowCombiningAuthorizeFilters = new CompatibilitySwitch<bool>(nameof(AllowCombiningAuthorizeFilters));
_allowBindingHeaderValuesToNonStringModelTypes = new CompatibilitySwitch<bool>(nameof(AllowBindingHeaderValuesToNonStringModelTypes)); _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));
@ -57,6 +59,7 @@ namespace Microsoft.AspNetCore.Mvc
{ {
_allowCombiningAuthorizeFilters, _allowCombiningAuthorizeFilters,
_allowBindingHeaderValuesToNonStringModelTypes, _allowBindingHeaderValuesToNonStringModelTypes,
_allowValidatingTopLevelNodes,
_inputFormatterExceptionPolicy, _inputFormatterExceptionPolicy,
_suppressBindingUndefinedValueToEnumType, _suppressBindingUndefinedValueToEnumType,
}; };
@ -114,9 +117,9 @@ namespace Microsoft.AspNetCore.Mvc
/// <summary> /// <summary>
/// Gets or sets a value that determines if <see cref="HeaderModelBinder"/> should bind to types other than /// 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="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="Int32"/>, /// <see cref="HeaderModelBinder"/> would bind to simple types (like <see cref="string"/>, <see cref="int"/>,
/// <see cref="Enum"/>, <see cref="Boolean"/> etc.) or a collection of simple types. The default value of the /// <see cref="Enum"/>, <see cref="bool"/> etc.) or a collection of simple types. The default value of the
/// property is <c>false</c>. /// property is <c>false</c>.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
@ -144,6 +147,41 @@ namespace Microsoft.AspNetCore.Mvc
set => _allowBindingHeaderValuesToNonStringModelTypes.Value = 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"/> if the version is
/// <see cref="CompatibilityVersion.Version_2_1"/> or later; <see langword="false"/> otherwise.
/// </value>
/// <remarks>
/// <para>
/// This property is associated with a compatibility switch and can provide a different behavior depending on
/// the configured compatibility version for the application. See <see cref="CompatibilityVersion"/> for
/// guidance and examples of setting the application's compatibility version.
/// </para>
/// <para>
/// Configuring the desired value of the compatibility switch by calling this property's setter will take
/// precedence over the value implied by the application's <see cref="CompatibilityVersion"/>.
/// </para>
/// <para>
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_0"/> then
/// this setting will have the value <see langword="false"/> unless explicitly configured.
/// </para>
/// <para>
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_1"/> or
/// higher then this setting will have the value <see langword="true"/> unless explicitly configured.
/// </para>
/// </remarks>
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.
@ -204,7 +242,7 @@ namespace Microsoft.AspNetCore.Mvc
public FormatterCollection<IInputFormatter> InputFormatters { get; } public FormatterCollection<IInputFormatter> InputFormatters { get; }
/// <summary> /// <summary>
/// Gets or sets an value indicating whether the model binding system will bind undefined values to /// Gets or sets a value indicating whether the model binding system will bind undefined values to
/// enum types. The default value of the property is <c>false</c>. /// enum types. The default value of the property is <c>false</c>.
/// </summary> /// </summary>
/// <remarks> /// <remarks>

View File

@ -6,7 +6,6 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Infrastructure;
@ -37,6 +36,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private readonly ParameterBinder _parameterBinder; private readonly ParameterBinder _parameterBinder;
private readonly IModelMetadataProvider _modelMetadataProvider; private readonly IModelMetadataProvider _modelMetadataProvider;
private readonly ITempDataDictionaryFactory _tempDataFactory; private readonly ITempDataDictionaryFactory _tempDataFactory;
private readonly MvcOptions _mvcOptions;
private readonly HtmlHelperOptions _htmlHelperOptions; private readonly HtmlHelperOptions _htmlHelperOptions;
private readonly IPageHandlerMethodSelector _selector; private readonly IPageHandlerMethodSelector _selector;
private readonly RazorProjectFileSystem _razorFileSystem; private readonly RazorProjectFileSystem _razorFileSystem;
@ -73,6 +73,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
_parameterBinder = parameterBinder; _parameterBinder = parameterBinder;
_modelMetadataProvider = modelMetadataProvider; _modelMetadataProvider = modelMetadataProvider;
_tempDataFactory = tempDataFactory; _tempDataFactory = tempDataFactory;
_mvcOptions = mvcOptions.Value;
_htmlHelperOptions = htmlHelperOptions.Value; _htmlHelperOptions = htmlHelperOptions.Value;
_selector = selector; _selector = selector;
_razorFileSystem = razorFileSystem; _razorFileSystem = razorFileSystem;
@ -261,7 +262,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
_modelMetadataProvider, _modelMetadataProvider,
_modelBinderFactory, _modelBinderFactory,
actionDescriptor, actionDescriptor,
actionDescriptor.HandlerMethods[i]); actionDescriptor.HandlerMethods[i],
_mvcOptions);
} }
return results; return results;

View File

@ -90,7 +90,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
IModelMetadataProvider modelMetadataProvider, IModelMetadataProvider modelMetadataProvider,
IModelBinderFactory modelBinderFactory, IModelBinderFactory modelBinderFactory,
CompiledPageActionDescriptor actionDescriptor, CompiledPageActionDescriptor actionDescriptor,
HandlerMethodDescriptor handler) HandlerMethodDescriptor handler,
MvcOptions mvcOptions)
{ {
if (handler.Parameters == null || handler.Parameters.Count == 0) if (handler.Parameters == null || handler.Parameters.Count == 0)
{ {
@ -103,7 +104,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{ {
var parameter = handler.Parameters[i]; var parameter = handler.Parameters[i];
ModelMetadata metadata; ModelMetadata metadata;
if (modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase) if (mvcOptions.AllowValidatingTopLevelNodes &&
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

@ -326,8 +326,10 @@ namespace Microsoft.AspNetCore.Mvc.Description
{ {
// Arrange // Arrange
var action = CreateActionDescriptor(); var action = CreateActionDescriptor();
action.AttributeRouteInfo = new AttributeRouteInfo(); action.AttributeRouteInfo = new AttributeRouteInfo
action.AttributeRouteInfo.Template = template; {
Template = template
};
// Act // Act
var descriptions = GetApiDescriptions(action); var descriptions = GetApiDescriptions(action);
@ -342,8 +344,10 @@ namespace Microsoft.AspNetCore.Mvc.Description
{ {
// Arrange // Arrange
var action = CreateActionDescriptor(); var action = CreateActionDescriptor();
action.AttributeRouteInfo = new AttributeRouteInfo(); action.AttributeRouteInfo = new AttributeRouteInfo
action.AttributeRouteInfo.Template = "api/Products/{id1}-{id2:int}"; {
Template = "api/Products/{id1}-{id2:int}"
};
// Act // Act
var descriptions = GetApiDescriptions(action); var descriptions = GetApiDescriptions(action);
@ -364,8 +368,10 @@ namespace Microsoft.AspNetCore.Mvc.Description
{ {
// Arrange // Arrange
var action = CreateActionDescriptor(); var action = CreateActionDescriptor();
action.AttributeRouteInfo = new AttributeRouteInfo(); action.AttributeRouteInfo = new AttributeRouteInfo
action.AttributeRouteInfo.Template = "api/Products/{id1}-{id2}/{id3:int}/{id4:int?}/{*id5:int}"; {
Template = "api/Products/{id1}-{id2}/{id3:int}/{id4:int?}/{*id5:int}"
};
// Act // Act
var descriptions = GetApiDescriptions(action); var descriptions = GetApiDescriptions(action);
@ -685,8 +691,10 @@ namespace Microsoft.AspNetCore.Mvc.Description
// Arrange // Arrange
var action = CreateActionDescriptor(methodName); var action = CreateActionDescriptor(methodName);
var filter = new ProducesResponseTypeAttribute(typeof(void), statusCode: 204); var filter = new ProducesResponseTypeAttribute(typeof(void), statusCode: 204);
action.FilterDescriptors = new List<FilterDescriptor>(); action.FilterDescriptors = new List<FilterDescriptor>
action.FilterDescriptors.Add(new FilterDescriptor(filter, FilterScope.Action)); {
new FilterDescriptor(filter, FilterScope.Action)
};
// Act // Act
var descriptions = GetApiDescriptions(action); var descriptions = GetApiDescriptions(action);
@ -717,8 +725,10 @@ namespace Microsoft.AspNetCore.Mvc.Description
Type = typeof(Order) Type = typeof(Order)
}; };
action.FilterDescriptors = new List<FilterDescriptor>(); action.FilterDescriptors = new List<FilterDescriptor>
action.FilterDescriptors.Add(new FilterDescriptor(filter, FilterScope.Action)); {
new FilterDescriptor(filter, FilterScope.Action)
};
// Act // Act
var descriptions = GetApiDescriptions(action); var descriptions = GetApiDescriptions(action);
@ -758,8 +768,10 @@ namespace Microsoft.AspNetCore.Mvc.Description
// Arrange // Arrange
var action = CreateActionDescriptor(nameof(ReturnsProduct)); var action = CreateActionDescriptor(nameof(ReturnsProduct));
var expectedMediaTypes = new[] { "text/json", "text/xml" }; var expectedMediaTypes = new[] { "text/json", "text/xml" };
action.FilterDescriptors = new List<FilterDescriptor>(); action.FilterDescriptors = new List<FilterDescriptor>
action.FilterDescriptors.Add(new FilterDescriptor(new ContentTypeAttribute("text/*"), FilterScope.Action)); {
new FilterDescriptor(new ContentTypeAttribute("text/*"), FilterScope.Action)
};
// Act // Act
var descriptions = GetApiDescriptions(action); var descriptions = GetApiDescriptions(action);
@ -780,8 +792,10 @@ namespace Microsoft.AspNetCore.Mvc.Description
Type = typeof(Order) Type = typeof(Order)
}; };
action.FilterDescriptors = new List<FilterDescriptor>(); action.FilterDescriptors = new List<FilterDescriptor>
action.FilterDescriptors.Add(new FilterDescriptor(filter, FilterScope.Action)); {
new FilterDescriptor(filter, FilterScope.Action)
};
var formatters = CreateOutputFormatters(); var formatters = CreateOutputFormatters();
@ -843,8 +857,10 @@ namespace Microsoft.AspNetCore.Mvc.Description
// Arrange // Arrange
var action = CreateActionDescriptor(nameof(AcceptsProduct_Body)); var action = CreateActionDescriptor(nameof(AcceptsProduct_Body));
action.FilterDescriptors = new List<FilterDescriptor>(); action.FilterDescriptors = new List<FilterDescriptor>
action.FilterDescriptors.Add(new FilterDescriptor(new ContentTypeAttribute("text/*"), FilterScope.Action)); {
new FilterDescriptor(new ContentTypeAttribute("text/*"), FilterScope.Action)
};
// Act // Act
var descriptions = GetApiDescriptions(action); var descriptions = GetApiDescriptions(action);
@ -863,8 +879,10 @@ namespace Microsoft.AspNetCore.Mvc.Description
// Arrange // Arrange
var action = CreateActionDescriptor(nameof(AcceptsProduct_Body)); var action = CreateActionDescriptor(nameof(AcceptsProduct_Body));
action.FilterDescriptors = new List<FilterDescriptor>(); action.FilterDescriptors = new List<FilterDescriptor>
action.FilterDescriptors.Add(new FilterDescriptor(new ContentTypeAttribute("text/*"), FilterScope.Action)); {
new FilterDescriptor(new ContentTypeAttribute("text/*"), FilterScope.Action)
};
var formatters = CreateInputFormatters(); var formatters = CreateInputFormatters();
@ -914,7 +932,7 @@ namespace Microsoft.AspNetCore.Mvc.Description
} }
[Fact] [Fact]
public void GetApiDescription_ParameterDescription_IsRequired() public void GetApiDescription_ParameterDescription_IsRequiredSet()
{ {
// Arrange // Arrange
var action = CreateActionDescriptor(nameof(RequiredParameter)); var action = CreateActionDescriptor(nameof(RequiredParameter));
@ -932,6 +950,25 @@ namespace Microsoft.AspNetCore.Mvc.Description
Assert.True(parameter.ModelMetadata.IsBindingRequired); Assert.True(parameter.ModelMetadata.IsBindingRequired);
} }
[Fact]
public void GetApiDescription_ParameterDescription_IsRequiredNotSet_IfNotValiatingTopLevelNodes()
{
// 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()
{ {
@ -1382,11 +1419,15 @@ namespace Microsoft.AspNetCore.Mvc.Description
private IReadOnlyList<ApiDescription> GetApiDescriptions( private IReadOnlyList<ApiDescription> GetApiDescriptions(
ActionDescriptor action, ActionDescriptor action,
List<MockInputFormatter> inputFormatters = null, List<MockInputFormatter> inputFormatters = null,
List<MockOutputFormatter> outputFormatters = null) List<MockOutputFormatter> outputFormatters = null,
bool allowValidatingTopLevelNodes = true)
{ {
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);
@ -1397,9 +1438,7 @@ namespace Microsoft.AspNetCore.Mvc.Description
options.OutputFormatters.Add(formatter); options.OutputFormatters.Add(formatter);
} }
var optionsAccessor = new Mock<IOptions<MvcOptions>>(); var optionsAccessor = Options.Create(options);
optionsAccessor.SetupGet(o => o.Value)
.Returns(options);
var constraintResolver = new Mock<IInlineConstraintResolver>(); var constraintResolver = new Mock<IInlineConstraintResolver>();
constraintResolver.Setup(c => c.ResolveConstraint("int")) constraintResolver.Setup(c => c.ResolveConstraint("int"))
@ -1408,7 +1447,7 @@ namespace Microsoft.AspNetCore.Mvc.Description
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var provider = new DefaultApiDescriptionProvider( var provider = new DefaultApiDescriptionProvider(
optionsAccessor.Object, optionsAccessor,
constraintResolver.Object, constraintResolver.Object,
modelMetadataProvider); modelMetadataProvider);

View File

@ -501,11 +501,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{ {
InvalidModelStateResponseFactory = _ => null, InvalidModelStateResponseFactory = _ => null,
}; };
var optionsProvider = Options.Create(options); var optionsAccessor = Options.Create(options);
modelMetadataProvider = modelMetadataProvider ?? new TestModelMetadataProvider(); modelMetadataProvider = modelMetadataProvider ?? new TestModelMetadataProvider();
var loggerFactory = NullLoggerFactory.Instance; var loggerFactory = NullLoggerFactory.Instance;
return new ApiBehaviorApplicationModelProvider(optionsProvider, modelMetadataProvider, loggerFactory); return new ApiBehaviorApplicationModelProvider(optionsAccessor, modelMetadataProvider, loggerFactory);
} }
private static ApplicationModelProviderContext GetContext(Type type) private static ApplicationModelProviderContext GetContext(Type type)

View File

@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq; using Moq;
using Xunit; using Xunit;
@ -103,6 +104,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new[] { controllerContext.ActionDescriptor }); new[] { controllerContext.ActionDescriptor });
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var modelBinderFactory = TestModelBinderFactory.CreateDefault(); var modelBinderFactory = TestModelBinderFactory.CreateDefault();
var mvcOptions = Options.Create(new MvcOptions
{
AllowValidatingTopLevelNodes = true,
});
return new ControllerActionInvokerCache( return new ControllerActionInvokerCache(
descriptorProvider, descriptorProvider,
@ -110,11 +115,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IObjectModelValidator>(), Mock.Of<IObjectModelValidator>(),
mvcOptions,
NullLoggerFactory.Instance), NullLoggerFactory.Instance),
modelBinderFactory, modelBinderFactory,
modelMetadataProvider, modelMetadataProvider,
filterProviders, filterProviders,
Mock.Of<IControllerFactoryProvider>()); Mock.Of<IControllerFactoryProvider>(),
mvcOptions);
} }
private static ControllerContext CreateControllerContext(FilterDescriptor[] filterDescriptors) private static ControllerContext CreateControllerContext(FilterDescriptor[] filterDescriptors)

View File

@ -20,6 +20,7 @@ using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq; using Moq;
using Xunit; using Xunit;
@ -27,8 +28,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{ {
public class ControllerBinderDelegateProviderTest public class ControllerBinderDelegateProviderTest
{ {
private static readonly MvcOptions _options = new MvcOptions
{
AllowValidatingTopLevelNodes = true,
};
private static readonly IOptions<MvcOptions> _optionsAccessor = Options.Create(_options);
[Fact] [Fact]
public async Task BindActionArgumentsAsync_DoesNotAddActionArgumentsOrCallBinderOrValidator_IfBindingIsNotAllowed_OnParameter() public async Task CreateBinderDelegate_Delegate_DoesNotAddActionArgumentsOrCallBinderOrValidator_IfBindingIsNotAllowed_OnParameter()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -61,6 +68,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new[] { GetModelValidatorProvider(mockValidator.Object) }), new[] { GetModelValidatorProvider(mockValidator.Object) }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -68,7 +76,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), TestModelMetadataProvider.CreateDefaultProvider(),
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -85,7 +94,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_DoesNotAddActionArgumentsOrCallBinderOrValidator_IfBindingIsNotAllowed_OnProperty() public async Task CreateBinderDelegate_Delegate_DoesNotAddActionArgumentsOrCallBinderOrValidator_IfBindingIsNotAllowed_OnProperty()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -109,12 +118,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var factory = GetModelBinderFactory(binder.Object); var factory = GetModelBinderFactory(binder.Object);
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var controller = new TestController(); var controller = new TestController();
var parameterBinder = new ParameterBinder( var parameterBinder = new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,
factory, factory,
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new[] { GetModelValidatorProvider(mockValidator.Object) }), new[] { GetModelValidatorProvider(mockValidator.Object) }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -122,7 +133,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), TestModelMetadataProvider.CreateDefaultProvider(),
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -139,7 +151,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_DoesNotAddActionArguments_IfBinderReturnsNull() public async Task CreateBinderDelegate_Delegate_DoesNotAddActionArguments_IfBinderReturnsNull()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -155,6 +167,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
binder binder
.Setup(b => b.BindModelAsync(It.IsAny<DefaultModelBindingContext>())) .Setup(b => b.BindModelAsync(It.IsAny<DefaultModelBindingContext>()))
.Returns(Task.CompletedTask); .Returns(Task.CompletedTask);
var factory = GetModelBinderFactory(binder.Object); var factory = GetModelBinderFactory(binder.Object);
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder( var parameterBinder = new ParameterBinder(
@ -163,6 +176,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
var controllerContext = GetControllerContext(actionDescriptor); var controllerContext = GetControllerContext(actionDescriptor);
@ -174,7 +188,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -183,7 +198,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_DoesNotAddActionArguments_IfBinderDoesNotSetModel() public async Task CreateBinderDelegate_Delegate_DoesNotAddActionArguments_IfBinderDoesNotSetModel()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -199,6 +214,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
binder binder
.Setup(b => b.BindModelAsync(It.IsAny<DefaultModelBindingContext>())) .Setup(b => b.BindModelAsync(It.IsAny<DefaultModelBindingContext>()))
.Returns(Task.CompletedTask); .Returns(Task.CompletedTask);
var factory = GetModelBinderFactory(binder.Object); var factory = GetModelBinderFactory(binder.Object);
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder( var parameterBinder = new ParameterBinder(
@ -207,6 +223,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
var controllerContext = GetControllerContext(actionDescriptor); var controllerContext = GetControllerContext(actionDescriptor);
@ -218,7 +235,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -227,7 +245,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_AddsActionArguments_IfBinderReturnsNotNull() public async Task CreateBinderDelegate_Delegate_AddsActionArguments_IfBinderReturnsNotNull()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -251,6 +269,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
context.Result = ModelBindingResult.Success(value); context.Result = ModelBindingResult.Success(value);
}) })
.Returns(Task.CompletedTask); .Returns(Task.CompletedTask);
var factory = GetModelBinderFactory(binder.Object); var factory = GetModelBinderFactory(binder.Object);
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder( var parameterBinder = new ParameterBinder(
@ -259,6 +278,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
var controllerContext = GetControllerContext(actionDescriptor); var controllerContext = GetControllerContext(actionDescriptor);
@ -270,7 +290,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -280,7 +301,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_GetsMetadataFromParameter() public async Task CreateBinderDelegate_Delegate_GetsMetadataFromParameter()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -307,12 +328,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
mockMetadataProvider mockMetadataProvider
.Setup(p => p.GetMetadataForParameter(ParameterInfos.NoAttributesParameterInfo)) .Setup(p => p.GetMetadataForParameter(ParameterInfos.NoAttributesParameterInfo))
.Returns(modelMetadata.Object); .Returns(modelMetadata.Object);
var parameterBinder = new ParameterBinder( var parameterBinder = new ParameterBinder(
mockMetadataProvider.Object, mockMetadataProvider.Object,
factory, factory,
new DefaultObjectValidator( new DefaultObjectValidator(
mockMetadataProvider.Object, mockMetadataProvider.Object,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -320,7 +343,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
mockMetadataProvider.Object, mockMetadataProvider.Object,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -332,7 +356,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_GetsMetadataFromType_IsMetadataProviderIsNotDefaultMetadataProvider() public async Task CreateBinderDelegate_Delegate_GetsMetadataFromType_IsMetadataProviderIsNotDefaultMetadataProvider()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -357,12 +381,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
mockMetadataProvider mockMetadataProvider
.Setup(p => p.GetMetadataForType(typeof(Person))) .Setup(p => p.GetMetadataForType(typeof(Person)))
.Returns(modelMetadata.Object); .Returns(modelMetadata.Object);
var parameterBinder = new ParameterBinder( var parameterBinder = new ParameterBinder(
mockMetadataProvider.Object, mockMetadataProvider.Object,
factory, factory,
new DefaultObjectValidator( new DefaultObjectValidator(
mockMetadataProvider.Object, mockMetadataProvider.Object,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -370,7 +396,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
mockMetadataProvider.Object, mockMetadataProvider.Object,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -382,7 +409,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_CallsValidator_IfModelBinderSucceeds() public async Task CreateBinderDelegate_Delegate_CallsValidator_IfModelBinderSucceeds()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -410,6 +437,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new[] { GetModelValidatorProvider(mockValidator.Object) }), new[] { GetModelValidatorProvider(mockValidator.Object) }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
var controller = new TestController(); var controller = new TestController();
var arguments = new Dictionary<string, object>(StringComparer.Ordinal); var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
@ -419,15 +447,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
// Assert // Assert
mockValidator mockValidator.Verify(o => o.Validate(It.IsAny<ModelValidationContext>()), Times.Once());
.Verify(o => o.Validate(
It.IsAny<ModelValidationContext>()),
Times.Once());
Assert.False(controllerContext.ModelState.IsValid); Assert.False(controllerContext.ModelState.IsValid);
Assert.Equal( Assert.Equal(
@ -436,7 +462,58 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_DoesNotCallValidator_IfModelBinderFails() 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,
new DefaultObjectValidator(
modelMetadataProvider,
new[] { 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]
public async Task CreateBinderDelegate_Delegate_DoesNotCallValidator_IfModelBinderFails()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -467,6 +544,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -474,7 +552,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -486,7 +565,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_CallsValidator_ForControllerProperties_IfModelBinderSucceeds() public async Task CreateBinderDelegate_Delegate_CallsValidator_ForControllerProperties_IfModelBinderSucceeds()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -508,13 +587,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var factory = GetModelBinderFactory("Hello"); var factory = GetModelBinderFactory("Hello");
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder( var parameterBinder = new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,
factory, factory,
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new[] { GetModelValidatorProvider(mockValidator.Object) }), new[] { GetModelValidatorProvider(mockValidator.Object) }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -522,15 +601,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
// Assert // Assert
mockValidator mockValidator.Verify(o => o.Validate(It.IsAny<ModelValidationContext>()), Times.Once());
.Verify(o => o.Validate(
It.IsAny<ModelValidationContext>()),
Times.Once());
Assert.False(controllerContext.ModelState.IsValid); Assert.False(controllerContext.ModelState.IsValid);
Assert.Equal( Assert.Equal(
"some message", "some message",
@ -567,6 +644,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
modelMetadataProvider, modelMetadataProvider,
factory, factory,
mockValidator.Object, mockValidator.Object,
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -574,7 +652,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -583,7 +662,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_DoesNotCallValidator_ForControllerProperties_IfModelBinderFails() public async Task CreateBinderDelegate_Delegate_DoesNotCallValidator_ForControllerProperties_IfModelBinderFails()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -613,6 +692,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new[] { GetModelValidatorProvider(mockValidator.Object) }), new[] { GetModelValidatorProvider(mockValidator.Object) }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -620,7 +700,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -632,7 +713,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_SetsControllerProperties_ForReferenceTypes() public async Task CreateBinderDelegate_Delegate_SetsControllerProperties_ForReferenceTypes()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -656,6 +737,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -663,7 +745,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -674,7 +757,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_AddsToCollectionControllerProperties() public async Task CreateBinderDelegate_Delegate_AddsToCollectionControllerProperties()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -699,6 +782,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -706,7 +790,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -717,7 +802,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_DoesNotSetNullValues_ForNonNullableProperties() public async Task CreateBinderDelegate_Delegate_DoesNotSetNullValues_ForNonNullableProperties()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -742,6 +827,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Some non default value. // Some non default value.
@ -752,7 +838,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -761,7 +848,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_SetsNullValues_ForNullableProperties() public async Task CreateBinderDelegate_Delegate_SetsNullValues_ForNullableProperties()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -786,6 +873,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Some non default value. // Some non default value.
@ -796,7 +884,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -805,7 +894,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_SupportsRequestPredicate_ForPropertiesAndParameters_NotBound() public async Task CreateBinderDelegate_Delegate_SupportsRequestPredicate_ForPropertiesAndParameters_NotBound()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -851,6 +940,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Some non default value. // Some non default value.
@ -861,7 +951,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -871,7 +962,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_SupportsRequestPredicate_ForPropertiesAndParameters_Bound() public async Task CreateBinderDelegate_Delegate_SupportsRequestPredicate_ForPropertiesAndParameters_Bound()
{ {
// Arrange // Arrange
var actionDescriptor = GetActionDescriptor(); var actionDescriptor = GetActionDescriptor();
@ -917,6 +1008,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Some non default value. // Some non default value.
@ -927,7 +1019,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -978,7 +1071,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
[Theory] [Theory]
[MemberData(nameof(SkippedPropertyData))] [MemberData(nameof(SkippedPropertyData))]
public async Task BindActionArgumentsAsync_SkipsReadOnlyControllerProperties( public async Task CreateBinderDelegate_Delegate_SkipsReadOnlyControllerProperties(
string propertyName, string propertyName,
Type propertyType, Type propertyType,
Func<object, object> propertyAccessor, Func<object, object> propertyAccessor,
@ -1007,6 +1100,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -1014,7 +1108,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -1025,7 +1120,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
} }
[Fact] [Fact]
public async Task BindActionArgumentsAsync_SetsMultipleControllerProperties() public async Task CreateBinderDelegate_Delegate_SetsMultipleControllerProperties()
{ {
// Arrange // Arrange
var boundPropertyTypes = new Dictionary<string, Type> var boundPropertyTypes = new Dictionary<string, Type>
@ -1085,6 +1180,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new IModelValidatorProvider[] { }), new IModelValidatorProvider[] { }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -1092,7 +1188,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder, parameterBinder,
factory, factory,
modelMetadataProvider, modelMetadataProvider,
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -1182,6 +1279,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new EmptyModelMetadataProvider(), new EmptyModelMetadataProvider(),
factory, factory,
new DefaultObjectValidator(modelMetadataProvider, new[] { modelValidatorProvider }), new DefaultObjectValidator(modelMetadataProvider, new[] { modelValidatorProvider }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
parameterBinder.Setup(p => p.BindModelAsync( parameterBinder.Setup(p => p.BindModelAsync(
It.IsAny<ActionContext>(), It.IsAny<ActionContext>(),
@ -1223,7 +1321,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
parameterBinder.Object, parameterBinder.Object,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), TestModelMetadataProvider.CreateDefaultProvider(),
actionDescriptor); actionDescriptor,
_options);
await binderDelegate(controllerContext, new TestController(), arguments); await binderDelegate(controllerContext, new TestController(), arguments);
@ -1340,10 +1439,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var objectModelValidator = new DefaultObjectValidator( var objectModelValidator = new DefaultObjectValidator(
metadataProvider, metadataProvider,
new[] { modelValidatorProvider }); new[] { modelValidatorProvider });
return new ParameterBinder( return new ParameterBinder(
metadataProvider, metadataProvider,
factory, factory,
objectModelValidator, objectModelValidator,
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
} }

View File

@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public async Task MiddlewareFilter_SetsMiddlewareFilterFeature_OnExecution() public async Task MiddlewareFilter_SetsMiddlewareFilterFeature_OnExecution()
{ {
// Arrange // Arrange
RequestDelegate requestDelegate = (context) => Task.FromResult(true); Task requestDelegate(HttpContext context) => Task.FromResult(true);
var middlwareFilter = new MiddlewareFilter(requestDelegate); var middlwareFilter = new MiddlewareFilter(requestDelegate);
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
var resourceExecutingContext = GetResourceExecutingContext(httpContext); var resourceExecutingContext = GetResourceExecutingContext(httpContext);
@ -315,8 +315,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{ {
var services = CreateServices(); var services = CreateServices();
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext
httpContext.RequestServices = services.BuildServiceProvider(); {
RequestServices = services.BuildServiceProvider()
};
return httpContext; return httpContext;
} }
@ -326,8 +328,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var applicationServices = new ServiceCollection(); var applicationServices = new ServiceCollection();
var applicationBuilder = new ApplicationBuilder(applicationServices.BuildServiceProvider()); var applicationBuilder = new ApplicationBuilder(applicationServices.BuildServiceProvider());
var middlewareFilterBuilderService = new MiddlewareFilterBuilder( var middlewareFilterBuilderService = new MiddlewareFilterBuilder(
new MiddlewareFilterConfigurationProvider()); new MiddlewareFilterConfigurationProvider())
middlewareFilterBuilderService.ApplicationBuilder = applicationBuilder; {
ApplicationBuilder = applicationBuilder
};
return middlewareFilterBuilderService.GetPipeline(middlewarePipelineProviderType); return middlewareFilterBuilderService.GetPipeline(middlewarePipelineProviderType);
} }
@ -444,11 +449,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private class TestParameterBinder : ParameterBinder private class TestParameterBinder : ParameterBinder
{ {
private readonly IDictionary<string, object> _actionParameters; private readonly IDictionary<string, object> _actionParameters;
public TestParameterBinder(IDictionary<string, object> actionParameters) public TestParameterBinder(IDictionary<string, object> actionParameters)
: base( : base(
new EmptyModelMetadataProvider(), new EmptyModelMetadataProvider(),
TestModelBinderFactory.CreateDefault(), TestModelBinderFactory.CreateDefault(),
Mock.Of<IObjectModelValidator>(), Mock.Of<IObjectModelValidator>(),
Options.Create(new MvcOptions
{
AllowValidatingTopLevelNodes = true,
}),
NullLoggerFactory.Instance) NullLoggerFactory.Instance)
{ {
_actionParameters = actionParameters; _actionParameters = actionParameters;

View File

@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq; using Moq;
using Xunit; using Xunit;
@ -22,6 +23,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{ {
public class ParameterBinderTest public class ParameterBinderTest
{ {
private static readonly IOptions<MvcOptions> _optionsAccessor = Options.Create(new MvcOptions
{
AllowValidatingTopLevelNodes = true,
});
public static TheoryData BindModelAsyncData public static TheoryData BindModelAsyncData
{ {
get get
@ -98,7 +104,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var parameterBinder = new ParameterBinder( var parameterBinder = new ParameterBinder(
metadataProvider, metadataProvider,
factory.Object, factory.Object,
Mock.Of< IObjectModelValidator>(), Mock.Of<IObjectModelValidator>(),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
var controllerContext = GetControllerContext(); var controllerContext = GetControllerContext();
@ -150,6 +157,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
metadataProvider, metadataProvider,
factory.Object, factory.Object,
Mock.Of<IObjectModelValidator>(), Mock.Of<IObjectModelValidator>(),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
var valueProvider = new SimpleValueProvider var valueProvider = new SimpleValueProvider
@ -175,7 +183,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
mockModelMetadata.Setup(o => o.IsBindingRequired).Returns(true); mockModelMetadata.Setup(o => o.IsBindingRequired).Returns(true);
mockModelMetadata.Setup(o => o.DisplayName).Returns("Ignored Display Name"); // Bind attribute errors are phrased in terms of the model name, not display name mockModelMetadata.Setup(o => o.DisplayName).Returns("Ignored Display Name"); // Bind attribute errors are phrased in terms of the model name, not display name
var parameterBinder = CreateParameterBinder(mockModelMetadata.Object, validator: null); var parameterBinder = CreateParameterBinder(mockModelMetadata.Object);
var modelBindingResult = ModelBindingResult.Failed(); var modelBindingResult = ModelBindingResult.Failed();
// Act // Act
@ -195,6 +203,37 @@ 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()
{ {
@ -213,9 +252,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
new RequiredAttribute(), new RequiredAttribute(),
stringLocalizer: null); stringLocalizer: null);
var parameterBinder = CreateParameterBinder( var parameterBinder = CreateParameterBinder(mockModelMetadata.Object, validator);
mockModelMetadata.Object,
validator);
var modelBindingResult = ModelBindingResult.Success(null); var modelBindingResult = ModelBindingResult.Success(null);
// Act // Act
@ -235,6 +272,43 @@ 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);
}
[Fact] [Fact]
public async Task BindModelAsync_EnforcesTopLevelDataAnnotationsAttribute() public async Task BindModelAsync_EnforcesTopLevelDataAnnotationsAttribute()
{ {
@ -252,9 +326,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
validationAttribute, validationAttribute,
stringLocalizer: null); stringLocalizer: null);
var parameterBinder = CreateParameterBinder( var parameterBinder = CreateParameterBinder(mockModelMetadata.Object, validator);
mockModelMetadata.Object,
validator);
var modelBindingResult = ModelBindingResult.Success(123); var modelBindingResult = ModelBindingResult.Success(123);
// Act // Act
@ -353,7 +425,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
private static ParameterBinder CreateParameterBinder( private static ParameterBinder CreateParameterBinder(
ModelMetadata modelMetadata, ModelMetadata modelMetadata,
IModelValidator validator) IModelValidator validator = null,
IOptions<MvcOptions> optionsAccessor = null)
{ {
var mockModelMetadataProvider = new Mock<IModelMetadataProvider>(MockBehavior.Strict); var mockModelMetadataProvider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
mockModelMetadataProvider mockModelMetadataProvider
@ -361,12 +434,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
.Returns(modelMetadata); .Returns(modelMetadata);
var mockModelBinderFactory = new Mock<IModelBinderFactory>(MockBehavior.Strict); var mockModelBinderFactory = new Mock<IModelBinderFactory>(MockBehavior.Strict);
optionsAccessor = optionsAccessor ?? _optionsAccessor;
return new ParameterBinder( return new ParameterBinder(
mockModelMetadataProvider.Object, mockModelMetadataProvider.Object,
mockModelBinderFactory.Object, mockModelBinderFactory.Object,
new DefaultObjectValidator( new DefaultObjectValidator(
mockModelMetadataProvider.Object, mockModelMetadataProvider.Object,
new[] { GetModelValidatorProvider(validator) }), new[] { GetModelValidatorProvider(validator) }),
optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
} }

View File

@ -1,43 +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;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using SecurityWebSite;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class CombineAuthorizeTests : IClassFixture<MvcTestFixture<StartupWithGlobalAuthorizeAndAllowCombiningAuthorizeFilters>>
{
public CombineAuthorizeTests(MvcTestFixture<StartupWithGlobalAuthorizeAndAllowCombiningAuthorizeFilters> fixture)
{
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }
[Fact]
public async Task CanAuthorizeWithOnlyCookie2()
{
// Arrange & Act
var response = await Client.PostAsync("http://localhost/Administration/SignInCookie2", null);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.True(response.Headers.Contains("Set-Cookie"));
var cookie2 = response.Headers.GetValues("Set-Cookie").SingleOrDefault();
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Administration/EitherCookie");
request.Headers.Add("Cookie", cookie2);
response = await Client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Assert.Equal("Administration.EitherCookie:AuthorizeCount=1", body);
}
}
}

View File

@ -6,7 +6,9 @@ using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests namespace Microsoft.AspNetCore.Mvc.FunctionalTests
@ -15,8 +17,8 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{ {
public GlobalAuthorizationFilterTest(MvcTestFixture<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter> fixture) public GlobalAuthorizationFilterTest(MvcTestFixture<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter> fixture)
{ {
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder); Factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
Client = factory.CreateDefaultClient(); Client = Factory.CreateDefaultClient();
} }
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) => private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
@ -24,6 +26,8 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public HttpClient Client { get; } public HttpClient Client { get; }
public WebApplicationFactory<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter> Factory { get; }
[Fact] [Fact]
public async Task DeniesAnonymousUsers_ByDefault() public async Task DeniesAnonymousUsers_ByDefault()
{ {
@ -51,10 +55,15 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
} }
[Fact] [Fact]
public async Task AuthorizationPoliciesDoNotCombineByDefault() public async Task AuthorizationPoliciesDoNotCombine_WithV2_0()
{ {
// Arrange & Act // Arrange & Act
var response = await Client.PostAsync("http://localhost/Administration/SignInCookie2", null); var factory = Factory.WithWebHostBuilder(
builder => builder.ConfigureServices(
services => services.Configure<MvcCompatibilityOptions>(
options => options.CompatibilityVersion = CompatibilityVersion.Version_2_0)));
var client = factory.CreateDefaultClient();
var response = await client.PostAsync("http://localhost/Administration/SignInCookie2", null);
// Assert // Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -65,8 +74,8 @@ 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);
// Will fail because default cookie is not sent so [Authorize] fails // Will fail because default cookie is not sent so [Authorize] fails.
response = await Client.SendAsync(request); response = await client.SendAsync(request);
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.NotNull(response.Headers.Location); Assert.NotNull(response.Headers.Location);
Assert.Equal( Assert.Equal(
@ -74,5 +83,28 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
response.Headers.Location.ToString()); response.Headers.Location.ToString());
} }
} [Fact]
public async Task AuthorizationPoliciesCombine_WithV2_1()
{
// Arrange & Act 1
var response = await Client.PostAsync("http://localhost/Administration/SignInCookie2", null);
// Assert 1
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.True(response.Headers.Contains("Set-Cookie"));
// Arrange 2
var cookie2 = response.Headers.GetValues("Set-Cookie").SingleOrDefault();
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Administration/EitherCookie");
request.Headers.Add("Cookie", cookie2);
// Act 2: Will succeed because, with AllowCombiningAuthorizeFilters true, [Authorize] allows either cookie.
response = await Client.SendAsync(request);
// Assert 2
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Null(response.Headers.Location);
}
}
} }

View File

@ -1,20 +1,26 @@
// 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.Globalization; using System.Globalization;
using System.IO;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{ {
public class MvcTestFixture<TStartup> : WebApplicationFactory<TStartup> public class MvcTestFixture<TStartup> : WebApplicationFactory<TStartup>
where TStartup : class where TStartup : class
{ {
protected override void ConfigureWebHost(IWebHostBuilder builder) => protected override void ConfigureWebHost(IWebHostBuilder builder)
builder.UseRequestCulture<TStartup>("en-GB", "en-US"); {
builder
.UseRequestCulture<TStartup>("en-GB", "en-US")
.ConfigureServices(
services => services.Configure<MvcCompatibilityOptions>(
options => options.CompatibilityVersion = CompatibilityVersion.Version_2_1));
}
protected override TestServer CreateServer(IWebHostBuilder builder) protected override TestServer CreateServer(IWebHostBuilder builder)
{ {

View File

@ -109,9 +109,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
// Assert // Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.Equal("{\"\":[\"Unexpected end when reading JSON. Path '', line 1, position 1.\"]}", responseBody);
// Update me in 3.0 xD
Assert.Equal("{\"\":[\"The input was not valid.\"]}", responseBody);
} }
[Theory] [Theory]

View File

@ -453,7 +453,18 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
public async Task FromBodyAndRequiredOnValueTypeProperty_EmptyBody_JsonFormatterAddsModelStateError() public async Task FromBodyAndRequiredOnValueTypeProperty_EmptyBody_JsonFormatterAddsModelStateError()
{ {
// Arrange // Arrange
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(); var testContext = ModelBindingTestHelper.GetTestContext(
request =>
{
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(string.Empty));
request.ContentType = "application/json";
});
// Override the AllowInputFormatterExceptionMessages setting ModelBindingTestHelper chooses.
var options = testContext.GetService<IOptions<MvcJsonOptions>>().Value;
options.AllowInputFormatterExceptionMessages = false;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameter = new ParameterDescriptor var parameter = new ParameterDescriptor
{ {
Name = "Parameter1", Name = "Parameter1",
@ -464,13 +475,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
ParameterType = typeof(Person4) ParameterType = typeof(Person4)
}; };
var testContext = ModelBindingTestHelper.GetTestContext(
request =>
{
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(string.Empty));
request.ContentType = "application/json";
});
var modelState = testContext.ModelState; var modelState = testContext.ModelState;
// Act // Act
@ -549,7 +553,18 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
public async Task FromBodyWithInvalidPropertyData_JsonFormatterAddsModelError() public async Task FromBodyWithInvalidPropertyData_JsonFormatterAddsModelError()
{ {
// Arrange // Arrange
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(); var testContext = ModelBindingTestHelper.GetTestContext(
request =>
{
request.Body = new MemoryStream(Encoding.UTF8.GetBytes("{ \"Number\": \"not a number\" }"));
request.ContentType = "application/json";
});
// Override the AllowInputFormatterExceptionMessages setting ModelBindingTestHelper chooses.
var options = testContext.GetService<IOptions<MvcJsonOptions>>().Value;
options.AllowInputFormatterExceptionMessages = false;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameter = new ParameterDescriptor var parameter = new ParameterDescriptor
{ {
Name = "Parameter1", Name = "Parameter1",
@ -560,13 +575,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
ParameterType = typeof(Person5) ParameterType = typeof(Person5)
}; };
var testContext = ModelBindingTestHelper.GetTestContext(
request =>
{
request.Body = new MemoryStream(Encoding.UTF8.GetBytes("{ \"Number\": \"not a number\" }"));
request.ContentType = "application/json";
});
var modelState = testContext.ModelState; var modelState = testContext.ModelState;
// Act // Act
@ -615,10 +623,10 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
{ {
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(string.Empty)); request.Body = new MemoryStream(Encoding.UTF8.GetBytes(string.Empty));
request.ContentType = "application/json"; request.ContentType = "application/json";
}); },
options => options.AllowEmptyInputInBodyModelBinding = allowEmptyInputInBodyModelBindingSetting);
var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>(); var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>();
optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = allowEmptyInputInBodyModelBindingSetting;
var modelState = testContext.ModelState; var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value); var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value);

View File

@ -2701,7 +2701,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
entry = Assert.Single(modelState, e => e.Key == "Info.Value.GpsCoordinates").Value; entry = Assert.Single(modelState, e => e.Key == "Info.Value.GpsCoordinates").Value;
Assert.Equal("10,20", entry.AttemptedValue); Assert.Equal("10,20", entry.AttemptedValue);
Assert.Equal(new[] { "10", "20" }, entry.RawValue); Assert.Equal("10,20", entry.RawValue);
} }
private static void SetJsonBodyContent(HttpRequest request, string content) private static void SetJsonBodyContent(HttpRequest request, string content)

View File

@ -441,14 +441,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
Action<HttpRequest> updateRequest = null, Action<HttpRequest> updateRequest = null,
Action<MvcOptions> updateOptions = null) Action<MvcOptions> updateOptions = null)
{ {
if (updateOptions == null)
{
updateOptions = o =>
{
o.AllowBindingHeaderValuesToNonStringModelTypes = true;
};
}
return ModelBindingTestHelper.GetTestContext(updateRequest, updateOptions); return ModelBindingTestHelper.GetTestContext(updateRequest, updateOptions);
} }

View File

@ -71,6 +71,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
new DefaultObjectValidator( new DefaultObjectValidator(
metadataProvider, metadataProvider,
new[] { new CompositeModelValidatorProvider(GetModelValidatorProviders(options)) }), new[] { new CompositeModelValidatorProvider(GetModelValidatorProviders(options)) }),
options,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
} }
@ -95,6 +96,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
new DefaultObjectValidator( new DefaultObjectValidator(
metadataProvider, metadataProvider,
new[] { new CompositeModelValidatorProvider(GetModelValidatorProviders(options)) }), new[] { new CompositeModelValidatorProvider(GetModelValidatorProviders(options)) }),
options,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
} }
@ -151,7 +153,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
var serviceCollection = new ServiceCollection(); var serviceCollection = new ServiceCollection();
serviceCollection.AddAuthorization(); serviceCollection.AddAuthorization();
serviceCollection.AddSingleton(new ApplicationPartManager()); serviceCollection.AddSingleton(new ApplicationPartManager());
serviceCollection.AddMvc(); serviceCollection
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
serviceCollection serviceCollection
.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>() .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
.AddTransient<ILoggerFactory, LoggerFactory>() .AddTransient<ILoggerFactory, LoggerFactory>()

View File

@ -487,10 +487,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var modelBinderFactory = TestModelBinderFactory.CreateDefault(); var modelBinderFactory = TestModelBinderFactory.CreateDefault();
var mvcOptions = new MvcOptions
{
AllowValidatingTopLevelNodes = true,
};
var parameterBinder = new ParameterBinder( var parameterBinder = new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,
TestModelBinderFactory.CreateDefault(), TestModelBinderFactory.CreateDefault(),
Mock.Of<IObjectModelValidator>(), Mock.Of<IObjectModelValidator>(),
Options.Create(mvcOptions),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
return new PageActionInvokerProvider( return new PageActionInvokerProvider(

View File

@ -26,6 +26,7 @@ using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq; using Moq;
using Xunit; using Xunit;
@ -1240,10 +1241,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
} }
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var mvcOptions = new MvcOptions
{
AllowValidatingTopLevelNodes = true,
};
return new ParameterBinder( return new ParameterBinder(
metadataProvider, metadataProvider,
factory, factory,
new DefaultObjectValidator(metadataProvider, new[] { validator }), new DefaultObjectValidator(metadataProvider, new[] { validator }),
Options.Create(mvcOptions),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
} }

View File

@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq; using Moq;
using Xunit; using Xunit;
@ -22,6 +23,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{ {
public class PageBinderFactoryTest public class PageBinderFactoryTest
{ {
private static readonly MvcOptions _options = new MvcOptions
{
AllowValidatingTopLevelNodes = true,
};
private static readonly IOptions<MvcOptions> _optionsAccessor = Options.Create(_options);
[Fact] [Fact]
public void GetModelBinderFactory_ReturnsNullIfPageHasNoBoundProperties() public void GetModelBinderFactory_ReturnsNullIfPageHasNoBoundProperties()
{ {
@ -37,6 +44,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IObjectModelValidator>(), Mock.Of<IObjectModelValidator>(),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -62,6 +70,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IObjectModelValidator>(), Mock.Of<IObjectModelValidator>(),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -86,6 +95,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IObjectModelValidator>(), Mock.Of<IObjectModelValidator>(),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -111,6 +121,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IObjectModelValidator>(), Mock.Of<IObjectModelValidator>(),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -135,6 +146,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IObjectModelValidator>(), Mock.Of<IObjectModelValidator>(),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -160,6 +172,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IObjectModelValidator>(), Mock.Of<IObjectModelValidator>(),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -285,7 +298,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
// Assert // Assert
// Verify that the page properties were not bound. // Verify that the page properties were not bound.
Assert.Equal(default(int), page.Id); Assert.Equal(default, page.Id);
Assert.Null(page.RouteDifferentValue); Assert.Null(page.RouteDifferentValue);
Assert.Equal(10, model.Id); Assert.Equal(10, model.Id);
@ -541,6 +554,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new[] { TestModelValidatorProvider.CreateDefaultProvider() }), new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
var factory = PageBinderFactory.CreatePropertyBinder(binder, modelMetadataProvider, modelBinderFactory, actionDescriptor); var factory = PageBinderFactory.CreatePropertyBinder(binder, modelMetadataProvider, modelBinderFactory, actionDescriptor);
@ -581,13 +595,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var modelBinderFactory = TestModelBinderFactory.CreateDefault(); var modelBinderFactory = TestModelBinderFactory.CreateDefault();
var factory = PageBinderFactory.CreateHandlerBinder( var factory = PageBinderFactory.CreateHandlerBinder(
parameterBinder, parameterBinder,
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
actionDescriptor, actionDescriptor,
actionDescriptor.HandlerMethods[0]); actionDescriptor.HandlerMethods[0],
_options);
var page = new PageWithProperty var page = new PageWithProperty
{ {
@ -625,13 +639,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var modelBinderFactory = TestModelBinderFactory.CreateDefault(); var modelBinderFactory = TestModelBinderFactory.CreateDefault();
var factory = PageBinderFactory.CreateHandlerBinder( var factory = PageBinderFactory.CreateHandlerBinder(
parameterBinder, parameterBinder,
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
actionDescriptor, actionDescriptor,
actionDescriptor.HandlerMethods[0]); actionDescriptor.HandlerMethods[0],
_options);
var page = new PageWithProperty var page = new PageWithProperty
{ {
@ -659,13 +673,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var modelBinderFactory = TestModelBinderFactory.CreateDefault(); var modelBinderFactory = TestModelBinderFactory.CreateDefault();
var parameterBinder = new ParameterBinder( var parameterBinder = new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
new DefaultObjectValidator( new DefaultObjectValidator(
modelMetadataProvider, modelMetadataProvider,
new[] { TestModelValidatorProvider.CreateDefaultProvider() }), new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
_optionsAccessor,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
var factory = PageBinderFactory.CreateHandlerBinder( var factory = PageBinderFactory.CreateHandlerBinder(
@ -673,7 +687,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
actionDescriptor, actionDescriptor,
actionDescriptor.HandlerMethods[0]); actionDescriptor.HandlerMethods[0],
_options);
var page = new PageWithProperty var page = new PageWithProperty
{ {
@ -690,13 +705,62 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var modelState = page.PageContext.ModelState; var modelState = page.PageContext.ModelState;
Assert.False(modelState.IsValid); Assert.False(modelState.IsValid);
Assert.Collection( Assert.Collection(
page.PageContext.ModelState, modelState,
kvp => kvp =>
{ {
Assert.Equal("name", kvp.Key); Assert.Equal("name", kvp.Key);
}); });
} }
[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() }),
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);
@ -756,6 +820,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
TestModelMetadataProvider.CreateDefaultProvider(), TestModelMetadataProvider.CreateDefaultProvider(),
TestModelBinderFactory.CreateDefault(), TestModelBinderFactory.CreateDefault(),
Mock.Of<IObjectModelValidator>(), Mock.Of<IObjectModelValidator>(),
_optionsAccessor,
NullLoggerFactory.Instance) NullLoggerFactory.Instance)
{ {
_args = args; _args = args;
@ -914,8 +979,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void OnPostWithValidation([Required] string name) public void OnPostWithValidation([Required] string name)
{ {
} }
} }
} }
} }

View File

@ -25,8 +25,7 @@ namespace RazorPagesWebSite
options.Conventions.AddPageRoute("/Pages/NotTheRoot", string.Empty); options.Conventions.AddPageRoute("/Pages/NotTheRoot", string.Empty);
options.Conventions.Add(new CustomModelTypeConvention()); options.Conventions.Add(new CustomModelTypeConvention());
}) })
.WithRazorPagesAtContentRoot() .WithRazorPagesAtContentRoot();
.SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1);
} }
public void Configure(IApplicationBuilder app) public void Configure(IApplicationBuilder app)

View File

@ -1,8 +1,8 @@
// 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 Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using RazorPagesWebSite.Conventions; using RazorPagesWebSite.Conventions;
@ -24,8 +24,7 @@ namespace RazorPagesWebSite
options.Conventions.AuthorizeAreaFolder("Accounts", "/RequiresAuth"); options.Conventions.AuthorizeAreaFolder("Accounts", "/RequiresAuth");
options.Conventions.AllowAnonymousToAreaPage("Accounts", "/RequiresAuth/AllowAnonymous"); options.Conventions.AllowAnonymousToAreaPage("Accounts", "/RequiresAuth/AllowAnonymous");
options.Conventions.Add(new CustomModelTypeConvention()); options.Conventions.Add(new CustomModelTypeConvention());
}) });
.SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1);
} }
public void Configure(IApplicationBuilder app) public void Configure(IApplicationBuilder app)

View File

@ -1,47 +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.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.DependencyInjection;
namespace SecurityWebSite
{
public class StartupWithGlobalAuthorizeAndAllowCombiningAuthorizeFilters
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc(o =>
{
o.AllowCombiningAuthorizeFilters = true;
o.Filters.Add(new AuthorizeFilter());
});
services.AddAntiforgery();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.LoginPath = "/Home/Login";
options.LogoutPath = "/Home/Logout";
}).AddCookie("Cookie2");
services.AddScoped<IPolicyEvaluator, CountingPolicyEvaluator>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}