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:
parent
51784bb2d6
commit
5e245da326
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
@ -779,4 +778,4 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,16 @@ 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.
|
||||||
//
|
//
|
||||||
// This means that all changes in behavior should be opt-in.
|
// This means that all changes in behavior should be opt-in.
|
||||||
//
|
//
|
||||||
// -----
|
// -----
|
||||||
//
|
//
|
||||||
// Moving on from general philosophy, here's how to implement a behavior change and corresponding
|
// Moving on from general philosophy, here's how to implement a behavior change and corresponding
|
||||||
// compatibility switch.
|
// compatibility switch.
|
||||||
//
|
//
|
||||||
// Add a new property on options that uses a CompatibilitySwitch<T> as a backing field. Make sure the
|
// Add a new property on options that uses a CompatibilitySwitch<T> as a backing field. Make sure the
|
||||||
// new switch is exposed by implementing IEnumerable<ICompatibilitySwitch> on the options class. Pass the
|
// new switch is exposed by implementing IEnumerable<ICompatibilitySwitch> on the options class. Pass the
|
||||||
|
|
@ -34,33 +34,33 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
//
|
//
|
||||||
// Choose a boolean value or a new enum type as the 'value' of the property.
|
// Choose a boolean value or a new enum type as the 'value' of the property.
|
||||||
//
|
//
|
||||||
// If the new property has a boolean value, it should be named something like `SuppressFoo`
|
// If the new property has a boolean value, it should be named something like `SuppressFoo`
|
||||||
// (if the new value deactivates some behavior) or like `AllowFoo` (if the new value enables some behavior).
|
// (if the new value deactivates some behavior) or like `AllowFoo` (if the new value enables some behavior).
|
||||||
// Choose a name so that the old behavior equates to 'false'.
|
// Choose a name so that the old behavior equates to 'false'.
|
||||||
//
|
//
|
||||||
// If it's an enum, make sure you initialize the compatibility switch using the
|
// If it's an enum, make sure you initialize the compatibility switch using the
|
||||||
// CompatibilitySwitch(string, value) constructor to make it obvious the correct value is passed in. It's
|
// CompatibilitySwitch(string, value) constructor to make it obvious the correct value is passed in. It's
|
||||||
// a good idea to equate the original behavior with the default enum value as well.
|
// a good idea to equate the original behavior with the default enum value as well.
|
||||||
//
|
//
|
||||||
// Then create (or modify) a subclass of ConfigureCompatibilityOptions appropriate for your options type.
|
// Then create (or modify) a subclass of ConfigureCompatibilityOptions appropriate for your options type.
|
||||||
// Override the DefaultValues property and provide appropriate values based on the value of the Version
|
// Override the DefaultValues property and provide appropriate values based on the value of the Version
|
||||||
// property. If you just added this class, register it as an IPostConfigureOptions<TOptions> in DI.
|
// property. If you just added this class, register it as an IPostConfigureOptions<TOptions> in DI.
|
||||||
//
|
//
|
||||||
/// <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.
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
{
|
{
|
||||||
public MvcOptionsConfigureCompatibilityOptions(
|
public MvcOptionsConfigureCompatibilityOptions(
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
IOptions<MvcCompatibilityOptions> compatibilityOptions)
|
IOptions<MvcCompatibilityOptions> compatibilityOptions)
|
||||||
: base(loggerFactory, compatibilityOptions)
|
: base(loggerFactory, compatibilityOptions)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -210,22 +210,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// internal for testing
|
// internal for testing
|
||||||
internal void InferParameterModelPrefixes(ActionModel actionModel)
|
internal void InferParameterModelPrefixes(ActionModel actionModel)
|
||||||
{
|
{
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,13 +85,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
_parameterBinder,
|
_parameterBinder,
|
||||||
_modelBinderFactory,
|
_modelBinderFactory,
|
||||||
_modelMetadataProvider,
|
_modelMetadataProvider,
|
||||||
actionDescriptor);
|
actionDescriptor,
|
||||||
|
_mvcOptions);
|
||||||
|
|
||||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||||
|
|
||||||
cacheEntry = new ControllerActionInvokerCacheEntry(
|
cacheEntry = new ControllerActionInvokerCacheEntry(
|
||||||
filterFactoryResult.CacheableFilters,
|
filterFactoryResult.CacheableFilters,
|
||||||
controllerFactory,
|
controllerFactory,
|
||||||
controllerReleaser,
|
controllerReleaser,
|
||||||
propertyBinderFactory,
|
propertyBinderFactory,
|
||||||
objectMethodExecutor,
|
objectMethodExecutor,
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
@ -89,8 +92,8 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// are applied.
|
/// are applied.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// This property is associated with a compatibility switch and can provide a different behavior depending on
|
/// 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
|
/// the configured compatibility version for the application. See <see cref="CompatibilityVersion"/> for
|
||||||
/// guidance and examples of setting the application's compatibility version.
|
/// guidance and examples of setting the application's compatibility version.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <para>
|
/// <para>
|
||||||
|
|
@ -114,15 +117,15 @@ 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>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// This property is associated with a compatibility switch and can provide a different behavior depending on
|
/// 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
|
/// the configured compatibility version for the application. See <see cref="CompatibilityVersion"/> for
|
||||||
/// guidance and examples of setting the application's compatibility version.
|
/// guidance and examples of setting the application's compatibility version.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <para>
|
/// <para>
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -173,8 +211,8 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// This property is associated with a compatibility switch and can provide a different behavior depending on
|
/// 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
|
/// the configured compatibility version for the application. See <see cref="CompatibilityVersion"/> for
|
||||||
/// guidance and examples of setting the application's compatibility version.
|
/// guidance and examples of setting the application's compatibility version.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <para>
|
/// <para>
|
||||||
|
|
@ -204,13 +242,13 @@ 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>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// This property is associated with a compatibility switch and can provide a different behavior depending on
|
/// 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
|
/// the configured compatibility version for the application. See <see cref="CompatibilityVersion"/> for
|
||||||
/// guidance and examples of setting the application's compatibility version.
|
/// guidance and examples of setting the application's compatibility version.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <para>
|
/// <para>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
@ -1917,4 +1956,4 @@ namespace Microsoft.AspNetCore.Mvc.Description
|
||||||
public BindingSource BindingSource => BindingSource.FormFile;
|
public BindingSource BindingSource => BindingSource.FormFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -390,7 +465,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
});
|
});
|
||||||
return validatorProvider.Object;
|
return validatorProvider.Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ParameterBinder CreateBackCompatParameterBinder(
|
private static ParameterBinder CreateBackCompatParameterBinder(
|
||||||
ModelMetadata modelMetadata,
|
ModelMetadata modelMetadata,
|
||||||
IObjectModelValidator validator)
|
IObjectModelValidator validator)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -384,7 +384,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
|
|
||||||
var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>();
|
var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>();
|
||||||
optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = true;
|
optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = true;
|
||||||
|
|
||||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value);
|
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -428,7 +428,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
|
|
||||||
var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>();
|
var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>();
|
||||||
optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = true;
|
optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = true;
|
||||||
|
|
||||||
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value);
|
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -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
|
||||||
|
|
@ -486,7 +490,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
Assert.Null(entry.Value.AttemptedValue);
|
Assert.Null(entry.Value.AttemptedValue);
|
||||||
Assert.Null(entry.Value.RawValue);
|
Assert.Null(entry.Value.RawValue);
|
||||||
var error = Assert.Single(entry.Value.Errors);
|
var error = Assert.Single(entry.Value.Errors);
|
||||||
|
|
||||||
// Update me in 3.0 when MvcJsonOptions.AllowInputFormatterExceptionMessages is removed
|
// Update me in 3.0 when MvcJsonOptions.AllowInputFormatterExceptionMessages is removed
|
||||||
Assert.IsType<JsonSerializationException>(error.Exception);
|
Assert.IsType<JsonSerializationException>(error.Exception);
|
||||||
Assert.Empty(error.ErrorMessage);
|
Assert.Empty(error.ErrorMessage);
|
||||||
|
|
@ -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
|
||||||
|
|
@ -596,7 +604,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
[InlineData(false, false)]
|
[InlineData(false, false)]
|
||||||
[InlineData(true, true)]
|
[InlineData(true, true)]
|
||||||
public async Task FromBodyWithEmptyBody_JsonFormatterAddsModelErrorWhenExpected(
|
public async Task FromBodyWithEmptyBody_JsonFormatterAddsModelErrorWhenExpected(
|
||||||
bool allowEmptyInputInBodyModelBindingSetting,
|
bool allowEmptyInputInBodyModelBindingSetting,
|
||||||
bool expectedModelStateIsValid)
|
bool expectedModelStateIsValid)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -630,7 +638,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
Assert.True(modelBindingResult.IsModelSet);
|
Assert.True(modelBindingResult.IsModelSet);
|
||||||
var boundPerson = Assert.IsType<Person5>(modelBindingResult.Model);
|
var boundPerson = Assert.IsType<Person5>(modelBindingResult.Model);
|
||||||
Assert.NotNull(boundPerson);
|
Assert.NotNull(boundPerson);
|
||||||
|
|
||||||
if (expectedModelStateIsValid)
|
if (expectedModelStateIsValid)
|
||||||
{
|
{
|
||||||
Assert.True(modelState.IsValid);
|
Assert.True(modelState.IsValid);
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -520,4 +512,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>()
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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?}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue