diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/InferParameterBindingInfoConvention.cs b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/InferParameterBindingInfoConvention.cs index 3ed892bc70..8861263faf 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/InferParameterBindingInfoConvention.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/InferParameterBindingInfoConvention.cs @@ -2,7 +2,9 @@ // 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.Linq; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Routing.Template; @@ -112,7 +114,8 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels if (property.BindingInfo != null && property.BindingInfo.BinderModelName == null && property.BindingInfo.BindingSource != null && - !property.BindingInfo.BindingSource.IsGreedy) + !property.BindingInfo.BindingSource.IsGreedy && + !IsFormFile(property.ParameterType)) { var metadata = _modelMetadataProvider.GetMetadataForProperty( controllerModel.ControllerType, @@ -133,6 +136,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels if (bindingInfo?.BindingSource != null && bindingInfo.BinderModelName == null && !bindingInfo.BindingSource.IsGreedy && + !IsFormFile(parameter.ParameterType) && IsComplexTypeParameter(parameter)) { parameter.BindingInfo.BinderModelName = string.Empty; @@ -166,5 +170,11 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels .GetMetadataForType(parameter.ParameterInfo.ParameterType); return metadata.IsComplexType && !metadata.IsCollectionType; } + + private static bool IsFormFile(Type parameterType) + { + return typeof(IFormFile).IsAssignableFrom(parameterType) || + typeof(IEnumerable).IsAssignableFrom(parameterType); + } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModels/InferParameterBindingInfoConventionTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModels/InferParameterBindingInfoConventionTest.cs index 7619c52b19..26da271e36 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModels/InferParameterBindingInfoConventionTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModels/InferParameterBindingInfoConventionTest.cs @@ -686,7 +686,7 @@ Environment.NewLine + "int b"; convention.InferBoundPropertyModelPrefixes(controller); // Assert - var property = Assert.Single(controller.ControllerProperties); + var property = Assert.Single(controller.ControllerProperties, p => p.Name == nameof(ControllerWithBoundProperty.TestProperty)); Assert.Equal(string.Empty, property.BindingInfo.BinderModelName); } @@ -720,6 +720,87 @@ Environment.NewLine + "int b"; Assert.Equal(string.Empty, parameter.BindingInfo.BinderModelName); } + [Fact] + public void InferParameterModelPrefixes_DoesNotSetModelPrefix_ForFormFileParametersAnnotatedWithFromForm() + { + // Arrange + var action = GetActionModel( + typeof(ParameterBindingController), + nameof(ParameterBindingController.FromFormFormFileParameters), + TestModelMetadataProvider.CreateDefaultProvider()); + var convention = GetConvention(); + + // Act + convention.InferParameterModelPrefixes(action); + + // Assert + Assert.Collection( + action.Parameters, + parameter => + { + Assert.Equal("p1", parameter.Name); + Assert.Null(parameter.BindingInfo.BinderModelName); + }, + parameter => + { + Assert.Equal("p2", parameter.Name); + Assert.Null(parameter.BindingInfo.BinderModelName); + }, + parameter => + { + Assert.Equal("p3", parameter.Name); + Assert.Null(parameter.BindingInfo.BinderModelName); + }); + } + + [Fact] + public void InferParameterModelPrefixes_DoesNotSetModelPrefix_ForFormFileParameters() + { + // Arrange + var action = GetActionModel( + typeof(ParameterBindingController), + nameof(ParameterBindingController.FormFileParameters), + TestModelMetadataProvider.CreateDefaultProvider()); + var convention = GetConvention(); + + // Act + convention.InferParameterModelPrefixes(action); + + // Assert + Assert.Collection( + action.Parameters, + parameter => + { + Assert.Equal("p1", parameter.Name); + Assert.Null(parameter.BindingInfo.BinderModelName); + }, + parameter => + { + Assert.Equal("p2", parameter.Name); + Assert.Null(parameter.BindingInfo.BinderModelName); + }, + parameter => + { + Assert.Equal("p3", parameter.Name); + Assert.Null(parameter.BindingInfo.BinderModelName); + }); + } + + [Fact] + public void InferBoundPropertyModelPrefixes_DoesNotSetModelPrefix_ForFormFileCollectionPropertiesAnnotatedWithFromForm() + { + // Arrange + var controller = GetControllerModel(typeof(ControllerWithBoundProperty)); + var convention = GetConvention(); + + // Act + convention.InferBoundPropertyModelPrefixes(controller); + + // Assert + var parameter = Assert.Single(controller.ControllerProperties, p => p.Name == nameof(ControllerWithBoundProperty.Files)); + Assert.Null(parameter.BindingInfo.BinderModelName); + } + private static InferParameterBindingInfoConvention GetConvention( IModelMetadataProvider modelMetadataProvider = null) { @@ -861,6 +942,10 @@ Environment.NewLine + "int b"; [HttpGet] public IActionResult ParameterWithRequestPredicateProvider([CustomRequestPredicateAndPropertyFilterProvider] int value) => null; + + public IActionResult FromFormFormFileParameters([FromForm] IFormFile p1, [FromForm] IFormFile[] p2, [FromForm] IFormFileCollection p3) => null; + + public IActionResult FormFileParameters(IFormFile p1, IFormFile[] p2, IFormFileCollection p3) => null; } [ApiController] @@ -955,6 +1040,9 @@ Environment.NewLine + "int b"; [FromQuery] public TestModel TestProperty { get; set; } + [FromForm] + public IList Files { get; set; } + public IActionResult SomeAction([FromQuery] TestModel test) => null; }