public async Task BindActionArgumentsAsync_SupportsRequestPredicate_ForPropertiesAndParameters_Bound() { // Arrange var actionDescriptor = GetActionDescriptor(); actionDescriptor.Parameters.Add(new ParameterDescriptor { Name = "test-parameter", BindingInfo = new BindingInfo() { BindingSource = BindingSource.Custom, // Simulates [BindProperty] on a parameter RequestPredicate = ((IRequestPredicateProvider) new BindPropertyAttribute()).RequestPredicate, }, ParameterType = typeof(string) }); actionDescriptor.BoundProperties.Add(new ParameterDescriptor { Name = nameof(TestController.NullableProperty), BindingInfo = new BindingInfo() { BindingSource = BindingSource.Custom, // Simulates [BindProperty] on a property RequestPredicate = ((IRequestPredicateProvider) new BindPropertyAttribute()).RequestPredicate, }, ParameterType = typeof(string) }); var controllerContext = GetControllerContext(actionDescriptor); controllerContext.HttpContext.Request.Method = "POST"; var controller = new TestController(); var arguments = new Dictionary <string, object>(StringComparer.Ordinal); var binder = new StubModelBinder(ModelBindingResult.Success(model: null)); var factory = GetModelBinderFactory(binder); var parameterBinder = GetParameterBinder(factory); // Some non default value. controller.NullableProperty = -1; // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, TestModelMetadataProvider.CreateDefaultProvider(), actionDescriptor); await binderDelegate(controllerContext, controller, arguments); // Assert Assert.Null(controller.NullableProperty); Assert.Contains("test-parameter", arguments.Keys); Assert.Null(arguments["test-parameter"]); }
public async Task BindActionArgumentsAsync_DoesNotCallValidator_IfModelBinderFails() { // Arrange Func <object, int> method = foo => 1; var actionDescriptor = GetActionDescriptor(); actionDescriptor.Parameters.Add( new ParameterDescriptor { Name = "foo", ParameterType = typeof(object), BindingInfo = new BindingInfo(), }); var controllerContext = GetControllerContext(actionDescriptor); var arguments = new Dictionary <string, object>(StringComparer.Ordinal); var binder = new Mock <IModelBinder>(); binder .Setup(b => b.BindModelAsync(It.IsAny <DefaultModelBindingContext>())) .Returns(Task.CompletedTask); 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 factory = GetModelBinderFactory(binder.Object); var controller = new TestController(); var parameterBinder = GetParameterBinder(factory, mockValidator.Object); // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, TestModelMetadataProvider.CreateDefaultProvider(), actionDescriptor); await binderDelegate(controllerContext, controller, arguments); // Assert mockValidator .Verify(o => o.Validate( It.IsAny <ActionContext>(), It.IsAny <ValidationStateDictionary>(), It.IsAny <string>(), It.IsAny <object>()), Times.Never()); }
public async Task BindActionArgumentsAsync_CallsValidator_IfModelBinderSucceeds() { // Arrange var actionDescriptor = GetActionDescriptor(); actionDescriptor.Parameters.Add( new ParameterDescriptor { Name = "foo", ParameterType = typeof(object), }); var controllerContext = GetControllerContext(actionDescriptor); var factory = GetModelBinderFactory("Hello"); 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 = GetParameterBinder(factory, mockValidator.Object); var controller = new TestController(); var arguments = new Dictionary <string, object>(StringComparer.Ordinal); // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, TestModelMetadataProvider.CreateDefaultProvider(), actionDescriptor); await binderDelegate(controllerContext, controller, arguments); // Assert mockValidator .Verify(o => o.Validate( It.IsAny <ActionContext>(), It.IsAny <ValidationStateDictionary>(), It.IsAny <string>(), It.IsAny <object>()), Times.Once()); }
public async Task BindActionArgumentsAsync_AddsActionArguments_IfBinderReturnsNotNull() { // Arrange Func <object, int> method = foo => 1; var actionDescriptor = GetActionDescriptor(); actionDescriptor.Parameters.Add( new ParameterDescriptor { Name = "foo", ParameterType = typeof(string), BindingInfo = new BindingInfo(), }); var value = "Hello world"; var metadataProvider = new EmptyModelMetadataProvider(); var binder = new Mock <IModelBinder>(); binder .Setup(b => b.BindModelAsync(It.IsAny <DefaultModelBindingContext>())) .Callback((ModelBindingContext context) => { context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(string)); context.Result = ModelBindingResult.Success(value); }) .Returns(Task.CompletedTask); var factory = GetModelBinderFactory(binder.Object); var parameterBinder = GetParameterBinder(factory); var controllerContext = GetControllerContext(actionDescriptor); var controller = new TestController(); var arguments = new Dictionary <string, object>(StringComparer.Ordinal); // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, TestModelMetadataProvider.CreateDefaultProvider(), actionDescriptor); await binderDelegate(controllerContext, controller, arguments); // Assert Assert.Equal(1, arguments.Count); Assert.Equal(value, arguments["foo"]); }
public (ControllerActionInvokerCacheEntry cacheEntry, IFilterMetadata[] filters) GetCachedResult(ControllerContext controllerContext) { var cache = CurrentCache; var actionDescriptor = controllerContext.ActionDescriptor; IFilterMetadata[] filters; if (!cache.Entries.TryGetValue(actionDescriptor, out var cacheEntry)) { var filterFactoryResult = FilterFactory.GetAllFilters(_filterProviders, controllerContext); filters = filterFactoryResult.Filters; var parameterDefaultValues = ParameterDefaultValues .GetParameterDefaultValues(actionDescriptor.MethodInfo); var objectMethodExecutor = ObjectMethodExecutor.Create( actionDescriptor.MethodInfo, actionDescriptor.ControllerTypeInfo, parameterDefaultValues); var controllerFactory = _controllerFactoryProvider.CreateControllerFactory(actionDescriptor); var controllerReleaser = _controllerFactoryProvider.CreateControllerReleaser(actionDescriptor); var propertyBinderFactory = ControllerBinderDelegateProvider.CreateBinderDelegate( _parameterBinder, _modelBinderFactory, _modelMetadataProvider, actionDescriptor, _mvcOptions); var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor); cacheEntry = new ControllerActionInvokerCacheEntry( filterFactoryResult.CacheableFilters, controllerFactory, controllerReleaser, propertyBinderFactory, objectMethodExecutor, actionMethodExecutor); cacheEntry = cache.Entries.GetOrAdd(actionDescriptor, cacheEntry); } else { // Filter instances from statically defined filter descriptors + from filter providers filters = FilterFactory.CreateUncachedFilters(_filterProviders, controllerContext, cacheEntry.CachedFilters); } return(cacheEntry, filters); }
public async Task BindActionArgumentsAsync_SetsNullValues_ForNullableProperties() { // Arrange var actionDescriptor = GetActionDescriptor(); actionDescriptor.BoundProperties.Add( new ParameterDescriptor { Name = "NullableProperty", BindingInfo = new BindingInfo() { BindingSource = BindingSource.Custom }, ParameterType = typeof(int?) }); var controllerContext = GetControllerContext(actionDescriptor); var controller = new TestController(); var arguments = new Dictionary <string, object>(StringComparer.Ordinal); var binder = new StubModelBinder(ModelBindingResult.Success(model: null)); var factory = GetModelBinderFactory(binder); var parameterBinder = GetParameterBinder(factory); // Some non default value. controller.NullableProperty = -1; // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, TestModelMetadataProvider.CreateDefaultProvider(), actionDescriptor); await binderDelegate(controllerContext, controller, arguments); // Assert Assert.Null(controller.NullableProperty); }
public async Task BindActionArgumentsAsync_SkipsReadOnlyControllerProperties( string propertyName, Type propertyType, Func <object, object> propertyAccessor, object inputValue, object expectedValue) { // Arrange var actionDescriptor = GetActionDescriptor(); actionDescriptor.BoundProperties.Add( new ParameterDescriptor { Name = propertyName, BindingInfo = new BindingInfo(), ParameterType = propertyType, }); var controllerContext = GetControllerContext(actionDescriptor); var controller = new TestController(); var arguments = new Dictionary <string, object>(StringComparer.Ordinal); var factory = GetModelBinderFactory(inputValue); var parameterBinder = GetParameterBinder(factory); // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, TestModelMetadataProvider.CreateDefaultProvider(), actionDescriptor); await binderDelegate(controllerContext, controller, arguments); // Assert Assert.Equal(expectedValue, propertyAccessor(controller)); Assert.Null(controller.StringProperty); Assert.Null(controller.UntouchedProperty); }
public async Task BindActionArgumentsAsync_DoesNotAddActionArguments_IfBinderDoesNotSetModel() { // Arrange var actionDescriptor = GetActionDescriptor(); actionDescriptor.Parameters.Add( new ParameterDescriptor { Name = "foo", ParameterType = typeof(object), BindingInfo = new BindingInfo(), }); var binder = new Mock <IModelBinder>(); binder .Setup(b => b.BindModelAsync(It.IsAny <DefaultModelBindingContext>())) .Returns(Task.CompletedTask); var factory = GetModelBinderFactory(binder.Object); var parameterBinder = GetParameterBinder(factory); var controllerContext = GetControllerContext(actionDescriptor); var controller = new TestController(); var arguments = new Dictionary <string, object>(StringComparer.Ordinal); // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, TestModelMetadataProvider.CreateDefaultProvider(), actionDescriptor); await binderDelegate(controllerContext, controller, arguments); // Assert Assert.Empty(arguments); }
public async Task BindActionArgumentsAsync_AddsToCollectionControllerProperties() { // Arrange var actionDescriptor = GetActionDescriptor(); actionDescriptor.BoundProperties.Add( new ParameterDescriptor { Name = nameof(TestController.CollectionProperty), BindingInfo = new BindingInfo(), ParameterType = typeof(ICollection <string>), }); var controllerContext = GetControllerContext(actionDescriptor); var controller = new TestController(); var arguments = new Dictionary <string, object>(StringComparer.Ordinal); var expected = new List <string> { "Hello", "World", "!!" }; var factory = GetModelBinderFactory(expected); var parameterBinder = GetParameterBinder(factory); // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, TestModelMetadataProvider.CreateDefaultProvider(), actionDescriptor); await binderDelegate(controllerContext, controller, arguments); // Assert Assert.Equal(expected, controller.CollectionProperty); Assert.Null(controller.StringProperty); Assert.Null(controller.UntouchedProperty); }
public async Task MultipleActionParameter_ValidModelState(List <ParameterDescriptor> parameters) { // Since validation attribute is only present on the FromBody model's property(TransferInfo's AccountId), // validation should not trigger for the parameter which is bound from Uri. // Arrange var actionDescriptor = new ControllerActionDescriptor() { BoundProperties = new List <ParameterDescriptor>(), Parameters = parameters }; var modelMetadataProvider = new EmptyModelMetadataProvider(); var modelBinderProvider = new BodyModelBinderProvider(new[] { Mock.Of <IInputFormatter>() }, Mock.Of <IHttpRequestStreamReaderFactory>()); var factory = TestModelBinderFactory.CreateDefault(modelBinderProvider); var parameterBinder = new Mock <ParameterBinder>( new EmptyModelMetadataProvider(), factory, CreateMockValidator()); parameterBinder.Setup(p => p.BindModelAsync( It.IsAny <ActionContext>(), It.IsAny <IModelBinder>(), It.IsAny <IValueProvider>(), It.IsAny <ParameterDescriptor>(), It.IsAny <ModelMetadata>(), null)) .Returns((ActionContext context, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor descriptor, ModelMetadata metadata, object v) => { ModelBindingResult result; if (descriptor.Name == "accountId") { result = ModelBindingResult.Success(10); } else if (descriptor.Name == "transferInfo") { result = ModelBindingResult.Success(new TransferInfo { AccountId = 40, Amount = 250.0 }); } else { result = ModelBindingResult.Failed(); } return(Task.FromResult(result)); }); var controllerContext = new ControllerContext { ActionDescriptor = actionDescriptor, }; var arguments = new Dictionary <string, object>(StringComparer.Ordinal); var modelState = controllerContext.ModelState; // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder.Object, factory, TestModelMetadataProvider.CreateDefaultProvider(), actionDescriptor); await binderDelegate(controllerContext, new TestController(), arguments); // Assert Assert.True(modelState.IsValid); Assert.True(arguments.TryGetValue("accountId", out var value)); var accountId = Assert.IsType <int>(value); Assert.Equal(10, accountId); Assert.True(arguments.TryGetValue("transferInfo", out value)); var transferInfo = Assert.IsType <TransferInfo>(value); Assert.NotNull(transferInfo); Assert.Equal(40, transferInfo.AccountId); Assert.Equal(250.0, transferInfo.Amount); }
public async Task BindActionArgumentsAsync_SetsMultipleControllerProperties() { // Arrange var boundPropertyTypes = new Dictionary <string, Type> { { nameof(TestController.ArrayProperty), typeof(string[]) }, // Skipped { nameof(TestController.CollectionProperty), typeof(List <string>) }, { nameof(TestController.NonCollectionProperty), typeof(Person) }, // Skipped { nameof(TestController.NullCollectionProperty), typeof(List <string>) }, // Skipped { nameof(TestController.StringProperty), typeof(string) }, }; var inputPropertyValues = new Dictionary <string, object> { { nameof(TestController.ArrayProperty), new string[] { "hello", "world" } }, { nameof(TestController.CollectionProperty), new List <string> { "hello", "world" } }, { nameof(TestController.NonCollectionProperty), new Person { Name = "Fred" } }, { nameof(TestController.NullCollectionProperty), new List <string> { "hello", "world" } }, { nameof(TestController.StringProperty), "Hello" }, }; var expectedPropertyValues = new Dictionary <string, object> { { nameof(TestController.ArrayProperty), new string[] { "goodbye" } }, { nameof(TestController.CollectionProperty), new List <string> { "hello", "world" } }, { nameof(TestController.NonCollectionProperty), new Person { Name = "Ginger" } }, { nameof(TestController.NullCollectionProperty), null }, { nameof(TestController.StringProperty), "Hello" }, }; var actionDescriptor = GetActionDescriptor(); foreach (var keyValuePair in boundPropertyTypes) { actionDescriptor.BoundProperties.Add( new ParameterDescriptor { Name = keyValuePair.Key, BindingInfo = new BindingInfo(), ParameterType = keyValuePair.Value, }); } var controllerContext = GetControllerContext(actionDescriptor); var controller = new TestController(); var arguments = new Dictionary <string, object>(StringComparer.Ordinal); var binder = new StubModelBinder(bindingContext => { // BindingContext.ModelName will be string.Empty here. This is a 'fallback to empty prefix' // because the value providers have no data. object model; if (inputPropertyValues.TryGetValue(bindingContext.FieldName, out model)) { bindingContext.Result = ModelBindingResult.Success(model); } else { bindingContext.Result = ModelBindingResult.Failed(); } }); var factory = GetModelBinderFactory(binder); controllerContext.ValueProviderFactories.Add(new SimpleValueProviderFactory()); var parameterBinder = GetParameterBinder(factory); // Act var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate( parameterBinder, factory, TestModelMetadataProvider.CreateDefaultProvider(), actionDescriptor); await binderDelegate(controllerContext, controller, arguments); // Assert Assert.Equal(new string[] { "goodbye" }, controller.ArrayProperty); // Skipped Assert.Equal(new List <string> { "hello", "world" }, controller.CollectionProperty); Assert.Equal(new Person { Name = "Ginger" }, controller.NonCollectionProperty); // Skipped Assert.Null(controller.NullCollectionProperty); // Skipped Assert.Null(controller.UntouchedProperty); // Not bound Assert.Equal("Hello", controller.StringProperty); }