Allow IFormFile parameters annotated with [FromForm] to be correctly … (#8452)

* Allow IFormFile parameters annotated with [FromForm] to be correctly bound in ApiControllers

Fixes #8311
This commit is contained in:
Pranav K 2018-09-14 09:06:32 -07:00 committed by GitHub
parent 35597db277
commit 233140c33a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 100 additions and 2 deletions

View File

@ -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<IFormFile>).IsAssignableFrom(parameterType);
}
}
}

View File

@ -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<IFormFile> Files { get; set; }
public IActionResult SomeAction([FromQuery] TestModel test) => null;
}