[Fixes #7412] AspNetCore 2.1 breaks integration with 3rd party validation libraries

This commit is contained in:
Kiran Challa 2018-03-07 07:58:09 -08:00
parent a952313f1c
commit a0b1b15101
12 changed files with 478 additions and 211 deletions

View File

@ -232,15 +232,7 @@ namespace Microsoft.Extensions.DependencyInjection
return new DefaultObjectValidator(metadataProvider, options.ModelValidatorProviders); return new DefaultObjectValidator(metadataProvider, options.ModelValidatorProviders);
}); });
services.TryAddSingleton<ClientValidatorCache>(); services.TryAddSingleton<ClientValidatorCache>();
services.TryAddSingleton<ParameterBinder>(s => services.TryAddSingleton<ParameterBinder>();
{
var options = s.GetRequiredService<IOptions<MvcOptions>>().Value;
var loggerFactory = s.GetRequiredService<ILoggerFactory>();
var metadataProvider = s.GetRequiredService<IModelMetadataProvider>();
var modelBinderFactory = s.GetRequiredService<IModelBinderFactory>();
var modelValidatorProvider = new CompositeModelValidatorProvider(options.ModelValidatorProviders);
return new ParameterBinder(metadataProvider, modelBinderFactory, modelValidatorProvider, loggerFactory);
});
// //
// Random Infrastructure // Random Infrastructure

View File

@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
@ -11,12 +10,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
/// <summary> /// <summary>
/// The default implementation of <see cref="IObjectModelValidator"/>. /// The default implementation of <see cref="IObjectModelValidator"/>.
/// </summary> /// </summary>
public class DefaultObjectValidator : IObjectModelValidator public class DefaultObjectValidator : ObjectModelValidator
{ {
private readonly IModelMetadataProvider _modelMetadataProvider;
private readonly ValidatorCache _validatorCache;
private readonly IModelValidatorProvider _validatorProvider;
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="DefaultObjectValidator"/>. /// Initializes a new instance of <see cref="DefaultObjectValidator"/>.
/// </summary> /// </summary>
@ -25,44 +20,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public DefaultObjectValidator( public DefaultObjectValidator(
IModelMetadataProvider modelMetadataProvider, IModelMetadataProvider modelMetadataProvider,
IList<IModelValidatorProvider> validatorProviders) IList<IModelValidatorProvider> validatorProviders)
: base(modelMetadataProvider, validatorProviders)
{ {
if (modelMetadataProvider == null)
{
throw new ArgumentNullException(nameof(modelMetadataProvider));
}
if (validatorProviders == null)
{
throw new ArgumentNullException(nameof(validatorProviders));
}
_modelMetadataProvider = modelMetadataProvider;
_validatorCache = new ValidatorCache();
_validatorProvider = new CompositeModelValidatorProvider(validatorProviders);
} }
/// <inheritdoc /> public override ValidationVisitor GetValidationVisitor(
public void Validate(
ActionContext actionContext, ActionContext actionContext,
ValidationStateDictionary validationState, IModelValidatorProvider validatorProvider,
string prefix, ValidatorCache validatorCache,
object model) IModelMetadataProvider metadataProvider,
ValidationStateDictionary validationState)
{ {
if (actionContext == null) return new ValidationVisitor(
{
throw new ArgumentNullException(nameof(actionContext));
}
var visitor = new ValidationVisitor(
actionContext, actionContext,
_validatorProvider, validatorProvider,
_validatorCache, validatorCache,
_modelMetadataProvider, metadataProvider,
validationState); validationState);
var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType());
visitor.Validate(metadata, prefix, model);
} }
} }
} }

View File

@ -0,0 +1,108 @@
// 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.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
/// <summary>
/// Provides a base <see cref="IObjectModelValidator"/> implementation for validating an object graph.
/// </summary>
public abstract class ObjectModelValidator : IObjectModelValidator
{
private readonly IModelMetadataProvider _modelMetadataProvider;
private readonly ValidatorCache _validatorCache;
private readonly CompositeModelValidatorProvider _validatorProvider;
/// <summary>
/// Initializes a new instance of <see cref="ObjectModelValidator"/>.
/// </summary>
/// <param name="modelMetadataProvider">The <see cref="ModelMetadataProvider"/>.</param>
/// <param name="validatorProviders">The list of <see cref="IModelValidatorProvider"/>.</param>
public ObjectModelValidator(
IModelMetadataProvider modelMetadataProvider,
IList<IModelValidatorProvider> validatorProviders)
{
if (modelMetadataProvider == null)
{
throw new ArgumentNullException(nameof(modelMetadataProvider));
}
if (validatorProviders == null)
{
throw new ArgumentNullException(nameof(validatorProviders));
}
_modelMetadataProvider = modelMetadataProvider;
_validatorCache = new ValidatorCache();
_validatorProvider = new CompositeModelValidatorProvider(validatorProviders);
}
/// <inheritdoc />
public virtual void Validate(
ActionContext actionContext,
ValidationStateDictionary validationState,
string prefix,
object model)
{
var visitor = GetValidationVisitor(
actionContext,
_validatorProvider,
_validatorCache,
_modelMetadataProvider,
validationState);
var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType());
visitor.Validate(metadata, prefix, model, alwaysValidateAtTopLevel: false);
}
/// <summary>
/// Validates the provided object model.
/// If <paramref name="model"/> is <see langword="null"/> and the <paramref name="metadata"/>'s
/// <see cref="ModelMetadata.IsRequired"/> is <see langword="true"/>, will add one or more
/// model state errors that <see cref="Validate(ActionContext, ValidationStateDictionary, string, object)"/>
/// would not.
/// </summary>
/// <param name="actionContext">The <see cref="ActionContext"/>.</param>
/// <param name="validationState">The <see cref="ValidationStateDictionary"/>.</param>
/// <param name="prefix">The model prefix key.</param>
/// <param name="model">The model object.</param>
/// <param name="metadata">The <see cref="ModelMetadata"/>.</param>
public virtual void Validate(
ActionContext actionContext,
ValidationStateDictionary validationState,
string prefix,
object model,
ModelMetadata metadata)
{
var visitor = GetValidationVisitor(
actionContext,
_validatorProvider,
_validatorCache,
_modelMetadataProvider,
validationState);
visitor.Validate(metadata, prefix, model, alwaysValidateAtTopLevel: metadata.IsRequired);
}
/// <summary>
/// Gets a <see cref="ValidationVisitor"/> that traverses the object model graph and performs validation.
/// </summary>
/// <param name="actionContext">The <see cref="ActionContext"/>.</param>
/// <param name="validatorProvider">The <see cref="IModelValidatorProvider"/>.</param>
/// <param name="validatorCache">The <see cref="ValidatorCache"/>.</param>
/// <param name="metadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="validationState">The <see cref="ValidationStateDictionary"/>.</param>
/// <returns>A <see cref="ValidationVisitor"/> which traverses the object model graph.</returns>
public abstract ValidationVisitor GetValidationVisitor(
ActionContext actionContext,
IModelValidatorProvider validatorProvider,
ValidatorCache validatorCache,
IModelMetadataProvider metadataProvider,
ValidationStateDictionary validationState);
}
}

View File

@ -18,9 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{ {
private readonly IModelMetadataProvider _modelMetadataProvider; private readonly IModelMetadataProvider _modelMetadataProvider;
private readonly IModelBinderFactory _modelBinderFactory; private readonly IModelBinderFactory _modelBinderFactory;
private readonly IObjectModelValidator _validatorForBackCompatOnly; private readonly IObjectModelValidator _objectModelValidator;
private readonly IModelValidatorProvider _validatorProvider;
private readonly ValidatorCache _validatorCache;
/// <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
@ -29,19 +27,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// </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="validatorProvider">The <see cref="IModelValidatorProvider"/>.</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 an " + nameof(ILoggerFactory) + ".")]
public ParameterBinder( public ParameterBinder(
IModelMetadataProvider modelMetadataProvider, IModelMetadataProvider modelMetadataProvider,
IModelBinderFactory modelBinderFactory, IModelBinderFactory modelBinderFactory,
IModelValidatorProvider validatorProvider) IObjectModelValidator validator)
: this( : this(modelMetadataProvider, modelBinderFactory, validator, NullLoggerFactory.Instance)
modelMetadataProvider,
modelBinderFactory,
validatorProvider,
validatorForBackCompatOnly: null,
loggerFactory: NullLoggerFactory.Instance)
{ {
} }
@ -50,63 +43,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// </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="validatorProvider">The <see cref="IModelValidatorProvider"/>.</param> /// <param name="validator">The <see cref="IObjectModelValidator"/>.</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,
IModelValidatorProvider validatorProvider, IObjectModelValidator validator,
ILoggerFactory loggerFactory)
: this(
modelMetadataProvider,
modelBinderFactory,
validatorProvider,
validatorForBackCompatOnly: null,
loggerFactory: loggerFactory)
{
if (validatorProvider == null)
{
throw new ArgumentNullException(nameof(validatorProvider));
}
}
/// <summary>
/// <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="IModelValidatorProvider"/> and <see cref="ILoggerFactory"/>
/// instead of an <see cref="IObjectModelValidator"/>.</para>
/// <para>Initializes a new instance of <see cref="ParameterBinder"/>.</para>
/// </summary>
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="modelBinderFactory">The <see cref="IModelBinderFactory"/>.</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"
+ " is the overload that takes an " + nameof(IModelValidatorProvider) + " and " + nameof(ILoggerFactory)
+ " instead of an " + nameof(IObjectModelValidator) + ".")]
public ParameterBinder(
IModelMetadataProvider modelMetadataProvider,
IModelBinderFactory modelBinderFactory,
IObjectModelValidator validator)
: this(
modelMetadataProvider,
modelBinderFactory,
validatorProvider: null,
validatorForBackCompatOnly: validator,
loggerFactory: NullLoggerFactory.Instance)
{
// Note: When this obsolete constructor overload is removed, also remember
// to remove the validatorForBackCompatOnly field.
if (validator == null)
{
throw new ArgumentNullException(nameof(validator));
}
}
private ParameterBinder(
IModelMetadataProvider modelMetadataProvider,
IModelBinderFactory modelBinderFactory,
IModelValidatorProvider validatorProvider,
IObjectModelValidator validatorForBackCompatOnly,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
{ {
if (modelMetadataProvider == null) if (modelMetadataProvider == null)
@ -119,6 +61,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
throw new ArgumentNullException(nameof(modelBinderFactory)); throw new ArgumentNullException(nameof(modelBinderFactory));
} }
if (validator == null)
{
throw new ArgumentNullException(nameof(validator));
}
if (loggerFactory == null) if (loggerFactory == null)
{ {
throw new ArgumentNullException(nameof(loggerFactory)); throw new ArgumentNullException(nameof(loggerFactory));
@ -126,9 +73,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
_modelMetadataProvider = modelMetadataProvider; _modelMetadataProvider = modelMetadataProvider;
_modelBinderFactory = modelBinderFactory; _modelBinderFactory = modelBinderFactory;
_validatorProvider = validatorProvider; _objectModelValidator = validator;
_validatorForBackCompatOnly = validatorForBackCompatOnly;
_validatorCache = new ValidatorCache();
Logger = loggerFactory.CreateLogger(GetType()); Logger = loggerFactory.CreateLogger(GetType());
} }
@ -279,14 +224,15 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var modelBindingResult = modelBindingContext.Result; var modelBindingResult = modelBindingContext.Result;
if (_validatorForBackCompatOnly != null) var baseObjectValidator = _objectModelValidator as ObjectModelValidator;
if (baseObjectValidator == null)
{ {
// Since we don't have access to an IModelValidatorProvider, fall back // For legacy implementations (which directly implemented IObjectModelValidator), fall back to the
// on back-compatibility logic. In this scenario, top-level validation // back-compatibility logic. In this scenario, top-level validation attributes will be ignored like
// attributes will be ignored like they were historically. // they were historically.
if (modelBindingResult.IsModelSet) if (modelBindingResult.IsModelSet)
{ {
_validatorForBackCompatOnly.Validate( _objectModelValidator.Validate(
actionContext, actionContext,
modelBindingContext.ValidationState, modelBindingContext.ValidationState,
modelBindingContext.ModelName, modelBindingContext.ModelName,
@ -298,6 +244,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
Logger.AttemptingToValidateParameterOrProperty(parameter, modelBindingContext); Logger.AttemptingToValidateParameterOrProperty(parameter, modelBindingContext);
EnforceBindRequiredAndValidate( EnforceBindRequiredAndValidate(
baseObjectValidator,
actionContext, actionContext,
metadata, metadata,
modelBindingContext, modelBindingContext,
@ -309,7 +256,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
return modelBindingResult; return modelBindingResult;
} }
private void EnforceBindRequiredAndValidate(ActionContext actionContext, ModelMetadata metadata, ModelBindingContext modelBindingContext, ModelBindingResult modelBindingResult) private void EnforceBindRequiredAndValidate(
ObjectModelValidator baseObjectValidator,
ActionContext actionContext,
ModelMetadata metadata,
ModelBindingContext modelBindingContext,
ModelBindingResult modelBindingResult)
{ {
if (!modelBindingResult.IsModelSet && metadata.IsBindingRequired) if (!modelBindingResult.IsModelSet && metadata.IsBindingRequired)
{ {
@ -321,18 +273,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
else if (modelBindingResult.IsModelSet || metadata.IsRequired) else if (modelBindingResult.IsModelSet || metadata.IsRequired)
{ {
// Enforce any other validation rules // Enforce any other validation rules
var visitor = new ValidationVisitor( baseObjectValidator.Validate(
actionContext, actionContext,
_validatorProvider, modelBindingContext.ValidationState,
_validatorCache,
_modelMetadataProvider,
modelBindingContext.ValidationState);
visitor.Validate(
metadata,
modelBindingContext.ModelName, modelBindingContext.ModelName,
modelBindingResult.Model, modelBindingResult.Model,
alwaysValidateAtTopLevel: metadata.IsRequired); metadata);
} }
} }
} }

View File

@ -109,7 +109,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
new ParameterBinder( new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IModelValidatorProvider>(), Mock.Of<IObjectModelValidator>(),
NullLoggerFactory.Instance), NullLoggerFactory.Instance),
modelBinderFactory, modelBinderFactory,
modelMetadataProvider, modelMetadataProvider,

View File

@ -49,10 +49,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal
.Setup(b => b.BindModelAsync(It.IsAny<DefaultModelBindingContext>())) .Setup(b => b.BindModelAsync(It.IsAny<DefaultModelBindingContext>()))
.Verifiable(); .Verifiable();
var mockValidator = CreateMockValidator(); var mockValidator = new Mock<IModelValidator>(MockBehavior.Strict);
mockValidator.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()));
var factory = GetModelBinderFactory(binder.Object); var factory = GetModelBinderFactory(binder.Object);
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var controller = new TestController(); var controller = new TestController();
var parameterBinder = GetParameterBinder(factory); var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new[] { GetModelValidatorProvider(mockValidator.Object) }),
NullLoggerFactory.Instance);
// Act // Act
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
@ -95,10 +104,18 @@ namespace Microsoft.AspNetCore.Mvc.Internal
.Setup(b => b.BindModelAsync(It.IsAny<DefaultModelBindingContext>())) .Setup(b => b.BindModelAsync(It.IsAny<DefaultModelBindingContext>()))
.Verifiable(); .Verifiable();
var mockValidator = CreateMockValidator(); var mockValidator = new Mock<IModelValidator>(MockBehavior.Strict);
mockValidator.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()));
var factory = GetModelBinderFactory(binder.Object); var factory = GetModelBinderFactory(binder.Object);
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var controller = new TestController(); var controller = new TestController();
var parameterBinder = GetParameterBinder(factory); var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new[] { GetModelValidatorProvider(mockValidator.Object) }),
NullLoggerFactory.Instance);
// Act // Act
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
@ -139,7 +156,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
.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 parameterBinder = GetParameterBinder(factory); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
var controllerContext = GetControllerContext(actionDescriptor); var controllerContext = GetControllerContext(actionDescriptor);
var controller = new TestController(); var controller = new TestController();
@ -149,7 +173,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), modelMetadataProvider,
actionDescriptor); actionDescriptor);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -176,7 +200,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
.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 parameterBinder = GetParameterBinder(factory); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
var controllerContext = GetControllerContext(actionDescriptor); var controllerContext = GetControllerContext(actionDescriptor);
var controller = new TestController(); var controller = new TestController();
@ -186,7 +217,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), modelMetadataProvider,
actionDescriptor); actionDescriptor);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -221,7 +252,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
}) })
.Returns(Task.CompletedTask); .Returns(Task.CompletedTask);
var factory = GetModelBinderFactory(binder.Object); var factory = GetModelBinderFactory(binder.Object);
var parameterBinder = GetParameterBinder(factory); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
var controllerContext = GetControllerContext(actionDescriptor); var controllerContext = GetControllerContext(actionDescriptor);
var controller = new TestController(); var controller = new TestController();
@ -231,7 +269,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), modelMetadataProvider,
actionDescriptor); actionDescriptor);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -259,7 +297,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var mockBinder = new Mock<IModelBinder>(); var mockBinder = new Mock<IModelBinder>();
var factory = GetModelBinderFactory(mockBinder.Object); var factory = GetModelBinderFactory(mockBinder.Object);
var parameterBinder = GetParameterBinder(factory, CreateMockValidator().Object);
var controller = new TestController(); var controller = new TestController();
var arguments = new Dictionary<string, object>(StringComparer.Ordinal); var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
@ -270,6 +307,13 @@ 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(
mockMetadataProvider.Object,
factory,
new DefaultObjectValidator(
mockMetadataProvider.Object,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
// Act // Act
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
@ -304,7 +348,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var mockBinder = new Mock<IModelBinder>(); var mockBinder = new Mock<IModelBinder>();
var factory = GetModelBinderFactory(mockBinder.Object); var factory = GetModelBinderFactory(mockBinder.Object);
var parameterBinder = GetParameterBinder(factory, CreateMockValidator().Object);
var controller = new TestController(); var controller = new TestController();
var arguments = new Dictionary<string, object>(StringComparer.Ordinal); var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
@ -314,6 +357,13 @@ 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(
mockMetadataProvider.Object,
factory,
new DefaultObjectValidator(
mockMetadataProvider.Object,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
// Act // Act
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
@ -348,13 +398,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var factory = GetModelBinderFactory("Hello"); var factory = GetModelBinderFactory("Hello");
var mockValidator = CreateMockValidator(); var mockValidator = new Mock<IModelValidator>();
mockValidator mockValidator
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>())) .Setup(o => o.Validate(It.IsAny<ModelValidationContext>()))
.Returns(new[] { new ModelValidationResult("memberName", "some message") }); .Returns(new[] { new ModelValidationResult("memberName", "some message") });
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = GetParameterBinder(factory, mockValidator.Object, modelMetadataProvider); var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new[] { GetModelValidatorProvider(mockValidator.Object) }),
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);
@ -400,16 +456,24 @@ namespace Microsoft.AspNetCore.Mvc.Internal
.Setup(b => b.BindModelAsync(It.IsAny<DefaultModelBindingContext>())) .Setup(b => b.BindModelAsync(It.IsAny<DefaultModelBindingContext>()))
.Returns(Task.CompletedTask); .Returns(Task.CompletedTask);
var mockValidator = CreateMockValidator(); var mockValidator = new Mock<IModelValidator>(MockBehavior.Strict);
mockValidator.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()));
var factory = GetModelBinderFactory(binder.Object); var factory = GetModelBinderFactory(binder.Object);
var controller = new TestController(); var controller = new TestController();
var parameterBinder = GetParameterBinder(factory, mockValidator.Object); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
// Act // Act
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), modelMetadataProvider,
actionDescriptor); actionDescriptor);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -437,14 +501,21 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var controller = new TestController(); var controller = new TestController();
var arguments = new Dictionary<string, object>(StringComparer.Ordinal); var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
var mockValidator = CreateMockValidator(); var mockValidator = new Mock<IModelValidator>(MockBehavior.Strict);
mockValidator mockValidator
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>())) .Setup(o => o.Validate(It.IsAny<ModelValidationContext>()))
.Returns(new[] { new ModelValidationResult("memberName", "some message") }); .Returns(new[] { new ModelValidationResult("memberName", "some message") });
var factory = GetModelBinderFactory("Hello"); var factory = GetModelBinderFactory("Hello");
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = GetParameterBinder(factory, mockValidator.Object, modelMetadataProvider);
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new[] { GetModelValidatorProvider(mockValidator.Object) }),
NullLoggerFactory.Instance);
// Act // Act
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
@ -466,6 +537,51 @@ namespace Microsoft.AspNetCore.Mvc.Internal
controllerContext.ModelState["memberName"].Errors.Single().ErrorMessage); controllerContext.ModelState["memberName"].Errors.Single().ErrorMessage);
} }
[Fact]
public async Task DoesNotValidate_ForControllerProperties_IfObjectValidatorDoesNotInheritFromBase()
{
// Arrange
var actionDescriptor = GetActionDescriptor();
actionDescriptor.BoundProperties.Add(
new ParameterDescriptor
{
Name = nameof(TestController.ValidatedProperty),
ParameterType = typeof(string),
});
var controllerContext = GetControllerContext(actionDescriptor);
var controller = new TestController();
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
var factory = GetModelBinderFactory("Hello");
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
mockValidator
.Setup(o => o.Validate(
It.IsAny<ActionContext>(),
It.IsAny<ValidationStateDictionary>(),
It.IsAny<string>(),
It.IsAny<object>()));
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
mockValidator.Object,
NullLoggerFactory.Instance);
// Act
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder,
factory,
modelMetadataProvider,
actionDescriptor);
await binderDelegate(controllerContext, controller, arguments);
// Assert
Assert.True(controllerContext.ModelState.IsValid);
}
[Fact] [Fact]
public async Task BindActionArgumentsAsync_DoesNotCallValidator_ForControllerProperties_IfModelBinderFails() public async Task BindActionArgumentsAsync_DoesNotCallValidator_ForControllerProperties_IfModelBinderFails()
{ {
@ -487,15 +603,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal
.Setup(b => b.BindModelAsync(It.IsAny<DefaultModelBindingContext>())) .Setup(b => b.BindModelAsync(It.IsAny<DefaultModelBindingContext>()))
.Returns(Task.CompletedTask); .Returns(Task.CompletedTask);
var mockValidator = CreateMockValidator(); var mockValidator = new Mock<IModelValidator>(MockBehavior.Strict);
mockValidator.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()));
var factory = GetModelBinderFactory(binder.Object); var factory = GetModelBinderFactory(binder.Object);
var parameterBinder = GetParameterBinder(factory, mockValidator.Object); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new[] { GetModelValidatorProvider(mockValidator.Object) }),
NullLoggerFactory.Instance);
// Act // Act
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), modelMetadataProvider,
actionDescriptor); actionDescriptor);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -525,14 +649,20 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var arguments = new Dictionary<string, object>(StringComparer.Ordinal); var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
var factory = GetModelBinderFactory("Hello"); var factory = GetModelBinderFactory("Hello");
var parameterBinder = GetParameterBinder(factory); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
// Act // Act
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), modelMetadataProvider,
actionDescriptor); actionDescriptor);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -562,13 +692,20 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var expected = new List<string> { "Hello", "World", "!!" }; var expected = new List<string> { "Hello", "World", "!!" };
var factory = GetModelBinderFactory(expected); var factory = GetModelBinderFactory(expected);
var parameterBinder = GetParameterBinder(factory); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
// Act // Act
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), modelMetadataProvider,
actionDescriptor); actionDescriptor);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -598,8 +735,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var binder = new StubModelBinder(ModelBindingResult.Success(model: null)); var binder = new StubModelBinder(ModelBindingResult.Success(model: null));
var factory = GetModelBinderFactory(binder); var factory = GetModelBinderFactory(binder);
var parameterBinder = GetParameterBinder(factory); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
// Some non default value. // Some non default value.
controller.NonNullableProperty = -1; controller.NonNullableProperty = -1;
@ -608,7 +751,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), modelMetadataProvider,
actionDescriptor); actionDescriptor);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -636,8 +779,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var binder = new StubModelBinder(ModelBindingResult.Success(model: null)); var binder = new StubModelBinder(ModelBindingResult.Success(model: null));
var factory = GetModelBinderFactory(binder); var factory = GetModelBinderFactory(binder);
var parameterBinder = GetParameterBinder(factory); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
// Some non default value. // Some non default value.
controller.NullableProperty = -1; controller.NullableProperty = -1;
@ -646,7 +795,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), modelMetadataProvider,
actionDescriptor); actionDescriptor);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -695,7 +844,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var binder = new StubModelBinder(ModelBindingResult.Success(model: null)); var binder = new StubModelBinder(ModelBindingResult.Success(model: null));
var factory = GetModelBinderFactory(binder); var factory = GetModelBinderFactory(binder);
var parameterBinder = GetParameterBinder(factory); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
// Some non default value. // Some non default value.
controller.NullableProperty = -1; controller.NullableProperty = -1;
@ -704,7 +860,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), modelMetadataProvider,
actionDescriptor); actionDescriptor);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -754,7 +910,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var binder = new StubModelBinder(ModelBindingResult.Success(model: null)); var binder = new StubModelBinder(ModelBindingResult.Success(model: null));
var factory = GetModelBinderFactory(binder); var factory = GetModelBinderFactory(binder);
var parameterBinder = GetParameterBinder(factory); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
// Some non default value. // Some non default value.
controller.NullableProperty = -1; controller.NullableProperty = -1;
@ -763,7 +926,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), modelMetadataProvider,
actionDescriptor); actionDescriptor);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -837,13 +1000,20 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var arguments = new Dictionary<string, object>(StringComparer.Ordinal); var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
var factory = GetModelBinderFactory(inputValue); var factory = GetModelBinderFactory(inputValue);
var parameterBinder = GetParameterBinder(factory); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
// Act // Act
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), modelMetadataProvider,
actionDescriptor); actionDescriptor);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -908,13 +1078,20 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var factory = GetModelBinderFactory(binder); var factory = GetModelBinderFactory(binder);
controllerContext.ValueProviderFactories.Add(new SimpleValueProviderFactory()); controllerContext.ValueProviderFactories.Add(new SimpleValueProviderFactory());
var parameterBinder = GetParameterBinder(factory); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(
modelMetadataProvider,
factory,
new DefaultObjectValidator(
modelMetadataProvider,
new IModelValidatorProvider[] { }),
NullLoggerFactory.Instance);
// Act // Act
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
parameterBinder, parameterBinder,
factory, factory,
TestModelMetadataProvider.CreateDefaultProvider(), modelMetadataProvider,
actionDescriptor); actionDescriptor);
await binderDelegate(controllerContext, controller, arguments); await binderDelegate(controllerContext, controller, arguments);
@ -1004,7 +1181,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var parameterBinder = new Mock<ParameterBinder>( var parameterBinder = new Mock<ParameterBinder>(
new EmptyModelMetadataProvider(), new EmptyModelMetadataProvider(),
factory, factory,
modelValidatorProvider); new DefaultObjectValidator(modelMetadataProvider, new[] { modelValidatorProvider }),
NullLoggerFactory.Instance);
parameterBinder.Setup(p => p.BindModelAsync( parameterBinder.Setup(p => p.BindModelAsync(
It.IsAny<ActionContext>(), It.IsAny<ActionContext>(),
It.IsAny<IModelBinder>(), It.IsAny<IModelBinder>(),
@ -1092,6 +1270,27 @@ namespace Microsoft.AspNetCore.Mvc.Internal
}; };
} }
private static IModelValidatorProvider GetModelValidatorProvider(IModelValidator validator = null)
{
if (validator == null)
{
validator = Mock.Of<IModelValidator>();
}
var validatorProvider = new Mock<IModelValidatorProvider>();
validatorProvider
.Setup(p => p.CreateValidators(It.IsAny<ModelValidatorProviderContext>()))
.Callback<ModelValidatorProviderContext>(context =>
{
foreach (var result in context.Results)
{
result.Validator = validator;
result.IsReusable = true;
}
});
return validatorProvider.Object;
}
private static ModelBinderFactory GetModelBinderFactory(object model = null) private static ModelBinderFactory GetModelBinderFactory(object model = null)
{ {
var binder = new Mock<IModelBinder>(); var binder = new Mock<IModelBinder>();
@ -1118,12 +1317,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private static ParameterBinder GetParameterBinder( private static ParameterBinder GetParameterBinder(
IModelBinderFactory factory = null, IModelBinderFactory factory = null,
IModelValidator validator = null, IObjectModelValidator validator = null,
IModelMetadataProvider modelMetadataProvider = null) IModelMetadataProvider modelMetadataProvider = null,
IModelValidatorProvider modelValidatorProvider = null)
{ {
if (validator == null) if (validator == null)
{ {
validator = CreateMockValidator().Object; validator = CreateObjectValidator();
} }
if (factory == null) if (factory == null)
@ -1131,32 +1331,32 @@ namespace Microsoft.AspNetCore.Mvc.Internal
factory = TestModelBinderFactory.CreateDefault(); factory = TestModelBinderFactory.CreateDefault();
} }
var validatorProvider = new Mock<IModelValidatorProvider>(); if (modelValidatorProvider == null)
validatorProvider {
.Setup(p => p.CreateValidators(It.IsAny<ModelValidatorProviderContext>())) modelValidatorProvider = Mock.Of<IModelValidatorProvider>();
.Callback<ModelValidatorProviderContext>(context => }
{
foreach (var result in context.Results)
{
result.Validator = validator;
result.IsReusable = true;
}
});
var metadataProvider = modelMetadataProvider ?? TestModelMetadataProvider.CreateDefaultProvider();
var objectModelValidator = new DefaultObjectValidator(
metadataProvider,
new[] { modelValidatorProvider });
return new ParameterBinder( return new ParameterBinder(
modelMetadataProvider ?? TestModelMetadataProvider.CreateDefaultProvider(), metadataProvider,
factory, factory,
validatorProvider.Object, objectModelValidator,
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
} }
private static Mock<IModelValidator> CreateMockValidator() private static IObjectModelValidator CreateObjectValidator()
{ {
var mockValidator = new Mock<IModelValidator>(MockBehavior.Strict); var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
mockValidator mockValidator
.Setup(o => o.Validate( .Setup(o => o.Validate(
It.IsAny<ModelValidationContext>())); It.IsAny<ActionContext>(),
return mockValidator; It.IsAny<ValidationStateDictionary>(),
It.IsAny<string>(),
It.IsAny<object>()));
return mockValidator.Object;
} }
// No need for bind-related attributes on properties in this controller class. Properties are added directly // No need for bind-related attributes on properties in this controller class. Properties are added directly
@ -1258,5 +1458,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{ {
} }
} }
private class TestObjectModelValidator : IObjectModelValidator
{
public void Validate(
ActionContext actionContext,
ValidationStateDictionary validationState,
string prefix,
object model)
{
throw new NotImplementedException();
}
}
} }
} }

View File

@ -448,7 +448,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
: base( : base(
new EmptyModelMetadataProvider(), new EmptyModelMetadataProvider(),
TestModelBinderFactory.CreateDefault(), TestModelBinderFactory.CreateDefault(),
Mock.Of<IModelValidatorProvider>(), Mock.Of<IObjectModelValidator>(),
NullLoggerFactory.Instance) NullLoggerFactory.Instance)
{ {
_actionParameters = actionParameters; _actionParameters = actionParameters;

View File

@ -98,7 +98,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var parameterBinder = new ParameterBinder( var parameterBinder = new ParameterBinder(
metadataProvider, metadataProvider,
factory.Object, factory.Object,
CreateMockValidatorProvider(), Mock.Of< IObjectModelValidator>(),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
var controllerContext = GetControllerContext(); var controllerContext = GetControllerContext();
@ -149,7 +149,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var argumentBinder = new ParameterBinder( var argumentBinder = new ParameterBinder(
metadataProvider, metadataProvider,
factory.Object, factory.Object,
CreateMockValidatorProvider(), Mock.Of<IObjectModelValidator>(),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
var valueProvider = new SimpleValueProvider var valueProvider = new SimpleValueProvider
@ -364,10 +364,33 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
return new ParameterBinder( return new ParameterBinder(
mockModelMetadataProvider.Object, mockModelMetadataProvider.Object,
mockModelBinderFactory.Object, mockModelBinderFactory.Object,
CreateMockValidatorProvider(validator), new DefaultObjectValidator(
mockModelMetadataProvider.Object,
new[] { GetModelValidatorProvider(validator) }),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
} }
private static IModelValidatorProvider GetModelValidatorProvider(IModelValidator validator = null)
{
if (validator == null)
{
validator = Mock.Of<IModelValidator>();
}
var validatorProvider = new Mock<IModelValidatorProvider>();
validatorProvider
.Setup(p => p.CreateValidators(It.IsAny<ModelValidatorProviderContext>()))
.Callback<ModelValidatorProviderContext>(context =>
{
foreach (var result in context.Results)
{
result.Validator = validator;
result.IsReusable = true;
}
});
return validatorProvider.Object;
}
private static ParameterBinder CreateBackCompatParameterBinder( private static ParameterBinder CreateBackCompatParameterBinder(
ModelMetadata modelMetadata, ModelMetadata modelMetadata,
IObjectModelValidator validator) IObjectModelValidator validator)

View File

@ -68,7 +68,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
return new ParameterBinder( return new ParameterBinder(
metadataProvider, metadataProvider,
new ModelBinderFactory(metadataProvider, options, serviceProvider), new ModelBinderFactory(metadataProvider, options, serviceProvider),
new CompositeModelValidatorProvider(GetModelValidatorProviders(options)), new DefaultObjectValidator(
metadataProvider,
new[] { new CompositeModelValidatorProvider(GetModelValidatorProviders(options)) }),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
} }
@ -90,7 +92,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
return new ParameterBinder( return new ParameterBinder(
metadataProvider, metadataProvider,
new ModelBinderFactory(metadataProvider, options, services), new ModelBinderFactory(metadataProvider, options, services),
new CompositeModelValidatorProvider(GetModelValidatorProviders(options)), new DefaultObjectValidator(
metadataProvider,
new[] { new CompositeModelValidatorProvider(GetModelValidatorProviders(options)) }),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
} }

View File

@ -490,7 +490,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var parameterBinder = new ParameterBinder( var parameterBinder = new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,
TestModelBinderFactory.CreateDefault(), TestModelBinderFactory.CreateDefault(),
Mock.Of<IModelValidatorProvider>(), Mock.Of<IObjectModelValidator>(),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
return new PageActionInvokerProvider( return new PageActionInvokerProvider(

View File

@ -1239,10 +1239,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
factory = TestModelBinderFactory.CreateDefault(); factory = TestModelBinderFactory.CreateDefault();
} }
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
return new ParameterBinder( return new ParameterBinder(
TestModelMetadataProvider.CreateDefaultProvider(), metadataProvider,
factory, factory,
validator, new DefaultObjectValidator(metadataProvider, new[] { validator }),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
} }

View File

@ -8,6 +8,7 @@ using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
@ -35,7 +36,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var binder = new ParameterBinder( var binder = new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IModelValidatorProvider>(), Mock.Of<IObjectModelValidator>(),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -60,7 +61,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var binder = new ParameterBinder( var binder = new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IModelValidatorProvider>(), Mock.Of<IObjectModelValidator>(),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -84,7 +85,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var binder = new ParameterBinder( var binder = new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IModelValidatorProvider>(), Mock.Of<IObjectModelValidator>(),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -109,7 +110,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var binder = new ParameterBinder( var binder = new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IModelValidatorProvider>(), Mock.Of<IObjectModelValidator>(),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -133,7 +134,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var binder = new ParameterBinder( var binder = new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IModelValidatorProvider>(), Mock.Of<IObjectModelValidator>(),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -158,7 +159,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var binder = new ParameterBinder( var binder = new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
Mock.Of<IModelValidatorProvider>(), Mock.Of<IObjectModelValidator>(),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
// Act // Act
@ -533,12 +534,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var modelBinderFactory = TestModelBinderFactory.CreateDefault(); var modelBinderFactory = TestModelBinderFactory.CreateDefault();
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
var binder = new ParameterBinder( var binder = new ParameterBinder(
modelMetadataProvider, modelMetadataProvider,
modelBinderFactory, modelBinderFactory,
validatorProvider, new DefaultObjectValidator(
modelMetadataProvider,
new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
var factory = PageBinderFactory.CreatePropertyBinder(binder, modelMetadataProvider, modelBinderFactory, actionDescriptor); var factory = PageBinderFactory.CreatePropertyBinder(binder, modelMetadataProvider, modelBinderFactory, actionDescriptor);
@ -657,9 +659,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var modelBinderFactory = TestModelBinderFactory.CreateDefault(); var modelBinderFactory = TestModelBinderFactory.CreateDefault();
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
var parameterBinder = new ParameterBinder(modelMetadataProvider, modelBinderFactory, validatorProvider, NullLoggerFactory.Instance); var parameterBinder = new ParameterBinder(
modelMetadataProvider,
modelBinderFactory,
new DefaultObjectValidator(
modelMetadataProvider,
new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
NullLoggerFactory.Instance);
var factory = PageBinderFactory.CreateHandlerBinder( var factory = PageBinderFactory.CreateHandlerBinder(
parameterBinder, parameterBinder,
@ -748,7 +755,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
: base( : base(
TestModelMetadataProvider.CreateDefaultProvider(), TestModelMetadataProvider.CreateDefaultProvider(),
TestModelBinderFactory.CreateDefault(), TestModelBinderFactory.CreateDefault(),
Mock.Of<IModelValidatorProvider>(), Mock.Of<IObjectModelValidator>(),
NullLoggerFactory.Instance) NullLoggerFactory.Instance)
{ {
_args = args; _args = args;