From a0b1b15101c885fe24f9d009465094d5c502f841 Mon Sep 17 00:00:00 2001 From: Kiran Challa Date: Wed, 7 Mar 2018 07:58:09 -0800 Subject: [PATCH] [Fixes #7412] AspNetCore 2.1 breaks integration with 3rd party validation libraries --- .../MvcCoreServiceCollectionExtensions.cs | 10 +- .../Internal/DefaultObjectValidator.cs | 48 +-- .../ModelBinding/ObjectModelValidator.cs | 108 ++++++ .../ModelBinding/ParameterBinder.cs | 110 ++---- .../ControllerActionInvokerCacheTest.cs | 2 +- .../ControllerBinderDelegateProviderTest.cs | 336 ++++++++++++++---- .../Internal/MiddlewareFilterTest.cs | 2 +- .../ModelBinding/ParameterBinderTest.cs | 29 +- .../ModelBindingTestHelper.cs | 8 +- .../Internal/PageActionInvokerProviderTest.cs | 2 +- .../Internal/PageActionInvokerTest.cs | 5 +- .../Internal/PageBinderFactoryTest.cs | 29 +- 12 files changed, 478 insertions(+), 211 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ObjectModelValidator.cs diff --git a/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreServiceCollectionExtensions.cs index 226b0be44d..da091e5d7d 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreServiceCollectionExtensions.cs @@ -232,15 +232,7 @@ namespace Microsoft.Extensions.DependencyInjection return new DefaultObjectValidator(metadataProvider, options.ModelValidatorProviders); }); services.TryAddSingleton(); - services.TryAddSingleton(s => - { - var options = s.GetRequiredService>().Value; - var loggerFactory = s.GetRequiredService(); - var metadataProvider = s.GetRequiredService(); - var modelBinderFactory = s.GetRequiredService(); - var modelValidatorProvider = new CompositeModelValidatorProvider(options.ModelValidatorProviders); - return new ParameterBinder(metadataProvider, modelBinderFactory, modelValidatorProvider, loggerFactory); - }); + services.TryAddSingleton(); // // Random Infrastructure diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultObjectValidator.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultObjectValidator.cs index 695b7d6aaf..0afcdb0f5e 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultObjectValidator.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultObjectValidator.cs @@ -1,7 +1,6 @@ // 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.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; @@ -11,12 +10,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal /// /// The default implementation of . /// - public class DefaultObjectValidator : IObjectModelValidator + public class DefaultObjectValidator : ObjectModelValidator { - private readonly IModelMetadataProvider _modelMetadataProvider; - private readonly ValidatorCache _validatorCache; - private readonly IModelValidatorProvider _validatorProvider; - /// /// Initializes a new instance of . /// @@ -25,44 +20,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal public DefaultObjectValidator( IModelMetadataProvider modelMetadataProvider, IList 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); } - /// - public void Validate( + public override ValidationVisitor GetValidationVisitor( ActionContext actionContext, - ValidationStateDictionary validationState, - string prefix, - object model) + IModelValidatorProvider validatorProvider, + ValidatorCache validatorCache, + IModelMetadataProvider metadataProvider, + ValidationStateDictionary validationState) { - if (actionContext == null) - { - throw new ArgumentNullException(nameof(actionContext)); - } - - var visitor = new ValidationVisitor( + return new ValidationVisitor( actionContext, - _validatorProvider, - _validatorCache, - _modelMetadataProvider, + validatorProvider, + validatorCache, + metadataProvider, validationState); - - var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType()); - visitor.Validate(metadata, prefix, model); } } } diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ObjectModelValidator.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ObjectModelValidator.cs new file mode 100644 index 0000000000..ec17f06eb9 --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ObjectModelValidator.cs @@ -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 +{ + /// + /// Provides a base implementation for validating an object graph. + /// + public abstract class ObjectModelValidator : IObjectModelValidator + { + private readonly IModelMetadataProvider _modelMetadataProvider; + private readonly ValidatorCache _validatorCache; + private readonly CompositeModelValidatorProvider _validatorProvider; + + /// + /// Initializes a new instance of . + /// + /// The . + /// The list of . + public ObjectModelValidator( + IModelMetadataProvider modelMetadataProvider, + IList 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); + } + + /// + 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); + } + + /// + /// Validates the provided object model. + /// If is and the 's + /// is , will add one or more + /// model state errors that + /// would not. + /// + /// The . + /// The . + /// The model prefix key. + /// The model object. + /// The . + 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); + } + + /// + /// Gets a that traverses the object model graph and performs validation. + /// + /// The . + /// The . + /// The . + /// The . + /// The . + /// A which traverses the object model graph. + public abstract ValidationVisitor GetValidationVisitor( + ActionContext actionContext, + IModelValidatorProvider validatorProvider, + ValidatorCache validatorCache, + IModelMetadataProvider metadataProvider, + ValidationStateDictionary validationState); + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ParameterBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ParameterBinder.cs index 647fc86e45..f7a15a8780 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ParameterBinder.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ParameterBinder.cs @@ -18,9 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding { private readonly IModelMetadataProvider _modelMetadataProvider; private readonly IModelBinderFactory _modelBinderFactory; - private readonly IObjectModelValidator _validatorForBackCompatOnly; - private readonly IModelValidatorProvider _validatorProvider; - private readonly ValidatorCache _validatorCache; + private readonly IObjectModelValidator _objectModelValidator; /// /// This constructor is obsolete and will be removed in a future version. The recommended alternative @@ -29,19 +27,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding /// /// The . /// The . - /// The . + /// The . [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) + ".")] public ParameterBinder( IModelMetadataProvider modelMetadataProvider, IModelBinderFactory modelBinderFactory, - IModelValidatorProvider validatorProvider) - : this( - modelMetadataProvider, - modelBinderFactory, - validatorProvider, - validatorForBackCompatOnly: null, - loggerFactory: NullLoggerFactory.Instance) + IObjectModelValidator validator) + : this(modelMetadataProvider, modelBinderFactory, validator, NullLoggerFactory.Instance) { } @@ -50,63 +43,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding /// /// The . /// The . - /// The . + /// The . /// The . public ParameterBinder( IModelMetadataProvider modelMetadataProvider, IModelBinderFactory modelBinderFactory, - IModelValidatorProvider validatorProvider, - ILoggerFactory loggerFactory) - : this( - modelMetadataProvider, - modelBinderFactory, - validatorProvider, - validatorForBackCompatOnly: null, - loggerFactory: loggerFactory) - { - if (validatorProvider == null) - { - throw new ArgumentNullException(nameof(validatorProvider)); - } - } - - /// - /// This constructor is obsolete and will be removed in a future version. The recommended alternative - /// is the overload that also takes an and - /// instead of an . - /// Initializes a new instance of . - /// - /// The . - /// The . - /// The . - [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, + IObjectModelValidator validator, ILoggerFactory loggerFactory) { if (modelMetadataProvider == null) @@ -119,6 +61,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding throw new ArgumentNullException(nameof(modelBinderFactory)); } + if (validator == null) + { + throw new ArgumentNullException(nameof(validator)); + } + if (loggerFactory == null) { throw new ArgumentNullException(nameof(loggerFactory)); @@ -126,9 +73,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding _modelMetadataProvider = modelMetadataProvider; _modelBinderFactory = modelBinderFactory; - _validatorProvider = validatorProvider; - _validatorForBackCompatOnly = validatorForBackCompatOnly; - _validatorCache = new ValidatorCache(); + _objectModelValidator = validator; Logger = loggerFactory.CreateLogger(GetType()); } @@ -279,14 +224,15 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding 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 - // on back-compatibility logic. In this scenario, top-level validation - // attributes will be ignored like they were historically. + // 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 + // they were historically. if (modelBindingResult.IsModelSet) { - _validatorForBackCompatOnly.Validate( + _objectModelValidator.Validate( actionContext, modelBindingContext.ValidationState, modelBindingContext.ModelName, @@ -298,6 +244,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding Logger.AttemptingToValidateParameterOrProperty(parameter, modelBindingContext); EnforceBindRequiredAndValidate( + baseObjectValidator, actionContext, metadata, modelBindingContext, @@ -309,7 +256,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding 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) { @@ -321,18 +273,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding else if (modelBindingResult.IsModelSet || metadata.IsRequired) { // Enforce any other validation rules - var visitor = new ValidationVisitor( + baseObjectValidator.Validate( actionContext, - _validatorProvider, - _validatorCache, - _modelMetadataProvider, - modelBindingContext.ValidationState); - - visitor.Validate( - metadata, + modelBindingContext.ValidationState, modelBindingContext.ModelName, modelBindingResult.Model, - alwaysValidateAtTopLevel: metadata.IsRequired); + metadata); } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionInvokerCacheTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionInvokerCacheTest.cs index c96254795e..93fa243d2d 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionInvokerCacheTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionInvokerCacheTest.cs @@ -109,7 +109,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal new ParameterBinder( modelMetadataProvider, modelBinderFactory, - Mock.Of(), + Mock.Of(), NullLoggerFactory.Instance), modelBinderFactory, modelMetadataProvider, diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerBinderDelegateProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerBinderDelegateProviderTest.cs index e0595ef42c..c1a5165621 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerBinderDelegateProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerBinderDelegateProviderTest.cs @@ -49,10 +49,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal .Setup(b => b.BindModelAsync(It.IsAny())) .Verifiable(); - var mockValidator = CreateMockValidator(); + var mockValidator = new Mock(MockBehavior.Strict); + mockValidator.Setup(o => o.Validate(It.IsAny())); + var factory = GetModelBinderFactory(binder.Object); + var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var controller = new TestController(); - var parameterBinder = GetParameterBinder(factory); + var parameterBinder = new ParameterBinder( + modelMetadataProvider, + factory, + new DefaultObjectValidator( + modelMetadataProvider, + new[] { GetModelValidatorProvider(mockValidator.Object) }), + NullLoggerFactory.Instance); // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( @@ -95,10 +104,18 @@ namespace Microsoft.AspNetCore.Mvc.Internal .Setup(b => b.BindModelAsync(It.IsAny())) .Verifiable(); - var mockValidator = CreateMockValidator(); + var mockValidator = new Mock(MockBehavior.Strict); + mockValidator.Setup(o => o.Validate(It.IsAny())); var factory = GetModelBinderFactory(binder.Object); + var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var controller = new TestController(); - var parameterBinder = GetParameterBinder(factory); + var parameterBinder = new ParameterBinder( + modelMetadataProvider, + factory, + new DefaultObjectValidator( + modelMetadataProvider, + new[] { GetModelValidatorProvider(mockValidator.Object) }), + NullLoggerFactory.Instance); // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( @@ -139,7 +156,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal .Setup(b => b.BindModelAsync(It.IsAny())) .Returns(Task.CompletedTask); 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 controller = new TestController(); @@ -149,7 +173,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, - TestModelMetadataProvider.CreateDefaultProvider(), + modelMetadataProvider, actionDescriptor); await binderDelegate(controllerContext, controller, arguments); @@ -176,7 +200,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal .Setup(b => b.BindModelAsync(It.IsAny())) .Returns(Task.CompletedTask); 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 controller = new TestController(); @@ -186,7 +217,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, - TestModelMetadataProvider.CreateDefaultProvider(), + modelMetadataProvider, actionDescriptor); await binderDelegate(controllerContext, controller, arguments); @@ -221,7 +252,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal }) .Returns(Task.CompletedTask); 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 controller = new TestController(); @@ -231,7 +269,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, - TestModelMetadataProvider.CreateDefaultProvider(), + modelMetadataProvider, actionDescriptor); await binderDelegate(controllerContext, controller, arguments); @@ -259,7 +297,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal var mockBinder = new Mock(); var factory = GetModelBinderFactory(mockBinder.Object); - var parameterBinder = GetParameterBinder(factory, CreateMockValidator().Object); var controller = new TestController(); var arguments = new Dictionary(StringComparer.Ordinal); @@ -270,6 +307,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal mockMetadataProvider .Setup(p => p.GetMetadataForParameter(ParameterInfos.NoAttributesParameterInfo)) .Returns(modelMetadata.Object); + var parameterBinder = new ParameterBinder( + mockMetadataProvider.Object, + factory, + new DefaultObjectValidator( + mockMetadataProvider.Object, + new IModelValidatorProvider[] { }), + NullLoggerFactory.Instance); // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( @@ -304,7 +348,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal var mockBinder = new Mock(); var factory = GetModelBinderFactory(mockBinder.Object); - var parameterBinder = GetParameterBinder(factory, CreateMockValidator().Object); var controller = new TestController(); var arguments = new Dictionary(StringComparer.Ordinal); @@ -314,6 +357,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal mockMetadataProvider .Setup(p => p.GetMetadataForType(typeof(Person))) .Returns(modelMetadata.Object); + var parameterBinder = new ParameterBinder( + mockMetadataProvider.Object, + factory, + new DefaultObjectValidator( + mockMetadataProvider.Object, + new IModelValidatorProvider[] { }), + NullLoggerFactory.Instance); // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( @@ -348,13 +398,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal var factory = GetModelBinderFactory("Hello"); - var mockValidator = CreateMockValidator(); + var mockValidator = new Mock(); mockValidator .Setup(o => o.Validate(It.IsAny())) .Returns(new[] { new ModelValidationResult("memberName", "some message") }); 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 arguments = new Dictionary(StringComparer.Ordinal); @@ -400,16 +456,24 @@ namespace Microsoft.AspNetCore.Mvc.Internal .Setup(b => b.BindModelAsync(It.IsAny())) .Returns(Task.CompletedTask); - var mockValidator = CreateMockValidator(); + var mockValidator = new Mock(MockBehavior.Strict); + mockValidator.Setup(o => o.Validate(It.IsAny())); var factory = GetModelBinderFactory(binder.Object); 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 var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, - TestModelMetadataProvider.CreateDefaultProvider(), + modelMetadataProvider, actionDescriptor); await binderDelegate(controllerContext, controller, arguments); @@ -437,14 +501,21 @@ namespace Microsoft.AspNetCore.Mvc.Internal var controller = new TestController(); var arguments = new Dictionary(StringComparer.Ordinal); - var mockValidator = CreateMockValidator(); + var mockValidator = new Mock(MockBehavior.Strict); mockValidator .Setup(o => o.Validate(It.IsAny())) .Returns(new[] { new ModelValidationResult("memberName", "some message") }); var factory = GetModelBinderFactory("Hello"); 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 var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( @@ -466,6 +537,51 @@ namespace Microsoft.AspNetCore.Mvc.Internal 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(StringComparer.Ordinal); + + var factory = GetModelBinderFactory("Hello"); + var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var mockValidator = new Mock(MockBehavior.Strict); + mockValidator + .Setup(o => o.Validate( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())); + + 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] public async Task BindActionArgumentsAsync_DoesNotCallValidator_ForControllerProperties_IfModelBinderFails() { @@ -487,15 +603,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal .Setup(b => b.BindModelAsync(It.IsAny())) .Returns(Task.CompletedTask); - var mockValidator = CreateMockValidator(); + var mockValidator = new Mock(MockBehavior.Strict); + mockValidator.Setup(o => o.Validate(It.IsAny())); 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 var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, - TestModelMetadataProvider.CreateDefaultProvider(), + modelMetadataProvider, actionDescriptor); await binderDelegate(controllerContext, controller, arguments); @@ -525,14 +649,20 @@ namespace Microsoft.AspNetCore.Mvc.Internal var arguments = new Dictionary(StringComparer.Ordinal); 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 var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, - TestModelMetadataProvider.CreateDefaultProvider(), + modelMetadataProvider, actionDescriptor); await binderDelegate(controllerContext, controller, arguments); @@ -562,13 +692,20 @@ namespace Microsoft.AspNetCore.Mvc.Internal var expected = new List { "Hello", "World", "!!" }; 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 var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, - TestModelMetadataProvider.CreateDefaultProvider(), + modelMetadataProvider, actionDescriptor); await binderDelegate(controllerContext, controller, arguments); @@ -598,8 +735,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal var binder = new StubModelBinder(ModelBindingResult.Success(model: null)); 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. controller.NonNullableProperty = -1; @@ -608,7 +751,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, - TestModelMetadataProvider.CreateDefaultProvider(), + modelMetadataProvider, actionDescriptor); await binderDelegate(controllerContext, controller, arguments); @@ -636,8 +779,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal var binder = new StubModelBinder(ModelBindingResult.Success(model: null)); 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. controller.NullableProperty = -1; @@ -646,7 +795,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, - TestModelMetadataProvider.CreateDefaultProvider(), + modelMetadataProvider, actionDescriptor); await binderDelegate(controllerContext, controller, arguments); @@ -695,7 +844,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal var binder = new StubModelBinder(ModelBindingResult.Success(model: null)); 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. controller.NullableProperty = -1; @@ -704,7 +860,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, - TestModelMetadataProvider.CreateDefaultProvider(), + modelMetadataProvider, actionDescriptor); await binderDelegate(controllerContext, controller, arguments); @@ -754,7 +910,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal var binder = new StubModelBinder(ModelBindingResult.Success(model: null)); 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. controller.NullableProperty = -1; @@ -763,7 +926,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, - TestModelMetadataProvider.CreateDefaultProvider(), + modelMetadataProvider, actionDescriptor); await binderDelegate(controllerContext, controller, arguments); @@ -837,13 +1000,20 @@ namespace Microsoft.AspNetCore.Mvc.Internal var arguments = new Dictionary(StringComparer.Ordinal); 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 var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, - TestModelMetadataProvider.CreateDefaultProvider(), + modelMetadataProvider, actionDescriptor); await binderDelegate(controllerContext, controller, arguments); @@ -908,13 +1078,20 @@ namespace Microsoft.AspNetCore.Mvc.Internal var factory = GetModelBinderFactory(binder); 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 var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, - TestModelMetadataProvider.CreateDefaultProvider(), + modelMetadataProvider, actionDescriptor); await binderDelegate(controllerContext, controller, arguments); @@ -1004,7 +1181,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal var parameterBinder = new Mock( new EmptyModelMetadataProvider(), factory, - modelValidatorProvider); + new DefaultObjectValidator(modelMetadataProvider, new[] { modelValidatorProvider }), + NullLoggerFactory.Instance); parameterBinder.Setup(p => p.BindModelAsync( It.IsAny(), It.IsAny(), @@ -1092,6 +1270,27 @@ namespace Microsoft.AspNetCore.Mvc.Internal }; } + private static IModelValidatorProvider GetModelValidatorProvider(IModelValidator validator = null) + { + if (validator == null) + { + validator = Mock.Of(); + } + + var validatorProvider = new Mock(); + validatorProvider + .Setup(p => p.CreateValidators(It.IsAny())) + .Callback(context => + { + foreach (var result in context.Results) + { + result.Validator = validator; + result.IsReusable = true; + } + }); + return validatorProvider.Object; + } + private static ModelBinderFactory GetModelBinderFactory(object model = null) { var binder = new Mock(); @@ -1118,12 +1317,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal private static ParameterBinder GetParameterBinder( IModelBinderFactory factory = null, - IModelValidator validator = null, - IModelMetadataProvider modelMetadataProvider = null) + IObjectModelValidator validator = null, + IModelMetadataProvider modelMetadataProvider = null, + IModelValidatorProvider modelValidatorProvider = null) { if (validator == null) { - validator = CreateMockValidator().Object; + validator = CreateObjectValidator(); } if (factory == null) @@ -1131,32 +1331,32 @@ namespace Microsoft.AspNetCore.Mvc.Internal factory = TestModelBinderFactory.CreateDefault(); } - var validatorProvider = new Mock(); - validatorProvider - .Setup(p => p.CreateValidators(It.IsAny())) - .Callback(context => - { - foreach (var result in context.Results) - { - result.Validator = validator; - result.IsReusable = true; - } - }); + if (modelValidatorProvider == null) + { + modelValidatorProvider = Mock.Of(); + } + var metadataProvider = modelMetadataProvider ?? TestModelMetadataProvider.CreateDefaultProvider(); + var objectModelValidator = new DefaultObjectValidator( + metadataProvider, + new[] { modelValidatorProvider }); return new ParameterBinder( - modelMetadataProvider ?? TestModelMetadataProvider.CreateDefaultProvider(), + metadataProvider, factory, - validatorProvider.Object, + objectModelValidator, NullLoggerFactory.Instance); } - private static Mock CreateMockValidator() + private static IObjectModelValidator CreateObjectValidator() { - var mockValidator = new Mock(MockBehavior.Strict); + var mockValidator = new Mock(MockBehavior.Strict); mockValidator .Setup(o => o.Validate( - It.IsAny())); - return mockValidator; + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())); + return mockValidator.Object; } // 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(); + } + } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MiddlewareFilterTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MiddlewareFilterTest.cs index f4fac6c6dd..9e8ac6011e 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MiddlewareFilterTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MiddlewareFilterTest.cs @@ -448,7 +448,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal : base( new EmptyModelMetadataProvider(), TestModelBinderFactory.CreateDefault(), - Mock.Of(), + Mock.Of(), NullLoggerFactory.Instance) { _actionParameters = actionParameters; diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/ParameterBinderTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/ParameterBinderTest.cs index 222075de82..85a0e02723 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/ParameterBinderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/ParameterBinderTest.cs @@ -98,7 +98,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding var parameterBinder = new ParameterBinder( metadataProvider, factory.Object, - CreateMockValidatorProvider(), + Mock.Of< IObjectModelValidator>(), NullLoggerFactory.Instance); var controllerContext = GetControllerContext(); @@ -149,7 +149,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding var argumentBinder = new ParameterBinder( metadataProvider, factory.Object, - CreateMockValidatorProvider(), + Mock.Of(), NullLoggerFactory.Instance); var valueProvider = new SimpleValueProvider @@ -364,10 +364,33 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding return new ParameterBinder( mockModelMetadataProvider.Object, mockModelBinderFactory.Object, - CreateMockValidatorProvider(validator), + new DefaultObjectValidator( + mockModelMetadataProvider.Object, + new[] { GetModelValidatorProvider(validator) }), NullLoggerFactory.Instance); } + private static IModelValidatorProvider GetModelValidatorProvider(IModelValidator validator = null) + { + if (validator == null) + { + validator = Mock.Of(); + } + + var validatorProvider = new Mock(); + validatorProvider + .Setup(p => p.CreateValidators(It.IsAny())) + .Callback(context => + { + foreach (var result in context.Results) + { + result.Validator = validator; + result.IsReusable = true; + } + }); + return validatorProvider.Object; + } + private static ParameterBinder CreateBackCompatParameterBinder( ModelMetadata modelMetadata, IObjectModelValidator validator) diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ModelBindingTestHelper.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ModelBindingTestHelper.cs index d5e069c864..030e723481 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ModelBindingTestHelper.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ModelBindingTestHelper.cs @@ -68,7 +68,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests return new ParameterBinder( metadataProvider, new ModelBinderFactory(metadataProvider, options, serviceProvider), - new CompositeModelValidatorProvider(GetModelValidatorProviders(options)), + new DefaultObjectValidator( + metadataProvider, + new[] { new CompositeModelValidatorProvider(GetModelValidatorProviders(options)) }), NullLoggerFactory.Instance); } @@ -90,7 +92,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests return new ParameterBinder( metadataProvider, new ModelBinderFactory(metadataProvider, options, services), - new CompositeModelValidatorProvider(GetModelValidatorProviders(options)), + new DefaultObjectValidator( + metadataProvider, + new[] { new CompositeModelValidatorProvider(GetModelValidatorProviders(options)) }), NullLoggerFactory.Instance); } diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs index d912f32d9a..e672a1e346 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs @@ -490,7 +490,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var parameterBinder = new ParameterBinder( modelMetadataProvider, TestModelBinderFactory.CreateDefault(), - Mock.Of(), + Mock.Of(), NullLoggerFactory.Instance); return new PageActionInvokerProvider( diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs index b3bb51a9d1..12c4029fdc 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs @@ -1239,10 +1239,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal factory = TestModelBinderFactory.CreateDefault(); } + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); return new ParameterBinder( - TestModelMetadataProvider.CreateDefaultProvider(), + metadataProvider, factory, - validator, + new DefaultObjectValidator(metadataProvider, new[] { validator }), NullLoggerFactory.Instance); } diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageBinderFactoryTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageBinderFactoryTest.cs index 7c1bd73036..531f223d85 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageBinderFactoryTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageBinderFactoryTest.cs @@ -8,6 +8,7 @@ using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; @@ -35,7 +36,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var binder = new ParameterBinder( modelMetadataProvider, modelBinderFactory, - Mock.Of(), + Mock.Of(), NullLoggerFactory.Instance); // Act @@ -60,7 +61,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var binder = new ParameterBinder( modelMetadataProvider, modelBinderFactory, - Mock.Of(), + Mock.Of(), NullLoggerFactory.Instance); // Act @@ -84,7 +85,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var binder = new ParameterBinder( modelMetadataProvider, modelBinderFactory, - Mock.Of(), + Mock.Of(), NullLoggerFactory.Instance); // Act @@ -109,7 +110,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var binder = new ParameterBinder( modelMetadataProvider, modelBinderFactory, - Mock.Of(), + Mock.Of(), NullLoggerFactory.Instance); // Act @@ -133,7 +134,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var binder = new ParameterBinder( modelMetadataProvider, modelBinderFactory, - Mock.Of(), + Mock.Of(), NullLoggerFactory.Instance); // Act @@ -158,7 +159,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var binder = new ParameterBinder( modelMetadataProvider, modelBinderFactory, - Mock.Of(), + Mock.Of(), NullLoggerFactory.Instance); // Act @@ -533,12 +534,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelBinderFactory = TestModelBinderFactory.CreateDefault(); - var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider(); var binder = new ParameterBinder( modelMetadataProvider, modelBinderFactory, - validatorProvider, + new DefaultObjectValidator( + modelMetadataProvider, + new[] { TestModelValidatorProvider.CreateDefaultProvider() }), NullLoggerFactory.Instance); var factory = PageBinderFactory.CreatePropertyBinder(binder, modelMetadataProvider, modelBinderFactory, actionDescriptor); @@ -657,9 +659,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); 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( parameterBinder, @@ -748,7 +755,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal : base( TestModelMetadataProvider.CreateDefaultProvider(), TestModelBinderFactory.CreateDefault(), - Mock.Of(), + Mock.Of(), NullLoggerFactory.Instance) { _args = args;