Array or List in query string does not get parsed #7712 (#7967)

- exclude collections when detecting complex types in `ApiBehaviorApplicationModelProvider`
- add test cases
This commit is contained in:
kishanAnem 2018-06-26 20:43:46 +02:00 committed by Doug Bunting
parent d46948da1d
commit 5e20c313d9
No known key found for this signature in database
GPG Key ID: 888B4EB7822B32E9
2 changed files with 109 additions and 5 deletions

View File

@ -193,7 +193,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var metadata = _modelMetadataProvider.GetMetadataForProperty(
controllerModel.ControllerType,
property.PropertyInfo.Name);
if (metadata.IsComplexType)
if (metadata.IsComplexType && !metadata.IsCollectionType)
{
property.BindingInfo.BinderModelName = string.Empty;
}
@ -254,9 +254,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private bool IsComplexTypeParameter(ParameterModel parameter)
{
// No need for information from attributes on the parameter. Just use its type.
return _modelMetadataProvider
.GetMetadataForType(parameter.ParameterInfo.ParameterType)
.IsComplexType;
var metadata = _modelMetadataProvider
.GetMetadataForType(parameter.ParameterInfo.ParameterType);
return metadata.IsComplexType && !metadata.IsCollectionType;
}
}
}

View File

@ -2,6 +2,7 @@
// 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.ComponentModel;
using System.Linq;
using System.Reflection;
@ -619,6 +620,75 @@ Environment.NewLine + "int b";
Assert.Equal("gps", bindingInfo.BinderModelName);
}
[Fact]
public void PreservesBindingSourceInference_ForFromQueryParameterOnCollectionType()
{
// Arrange
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var actionName = nameof(ParameterBindingController.FromQueryOnCollectionType);
var context = GetContext(typeof(ParameterBindingController), modelMetadataProvider);
var provider = GetProvider();
// Act
provider.OnProvidersExecuting(context);
// Assert
var controller = Assert.Single(context.Result.Controllers);
var action = Assert.Single(controller.Actions, a => a.ActionName == actionName);
var parameter = Assert.Single(action.Parameters);
var bindingInfo = parameter.BindingInfo;
Assert.NotNull(bindingInfo);
Assert.Same(BindingSource.Query, bindingInfo.BindingSource);
Assert.Null(bindingInfo.BinderModelName);
}
[Fact]
public void PreservesBindingSourceInference_ForFromQueryOnArrayType()
{
// Arrange
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var actionName = nameof(ParameterBindingController.FromQueryOnArrayType);
var context = GetContext(typeof(ParameterBindingController), modelMetadataProvider);
var provider = GetProvider();
// Act
provider.OnProvidersExecuting(context);
// Assert
var controller = Assert.Single(context.Result.Controllers);
var action = Assert.Single(controller.Actions, a => a.ActionName == actionName);
var parameter = Assert.Single(action.Parameters);
var bindingInfo = parameter.BindingInfo;
Assert.NotNull(bindingInfo);
Assert.Same(BindingSource.Query, bindingInfo.BindingSource);
Assert.Null(bindingInfo.BinderModelName);
}
[Fact]
public void PreservesBindingSourceInference_FromQueryOnArrayTypeWithCustomName()
{
// Arrange
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var actionName = nameof(ParameterBindingController.FromQueryOnArrayTypeWithCustomName);
var context = GetContext(typeof(ParameterBindingController), modelMetadataProvider);
var provider = GetProvider();
// Act
provider.OnProvidersExecuting(context);
// Assert
var controller = Assert.Single(context.Result.Controllers);
var action = Assert.Single(controller.Actions, a => a.ActionName == actionName);
var parameter = Assert.Single(action.Parameters);
var bindingInfo = parameter.BindingInfo;
Assert.NotNull(bindingInfo);
Assert.Same(BindingSource.Query, bindingInfo.BindingSource);
Assert.Equal("ids", bindingInfo.BinderModelName);
}
[Fact]
public void PreservesBindingSourceInference_ForFromRouteParameter_WithDefaultName()
{
@ -794,6 +864,22 @@ Environment.NewLine + "int b";
Assert.Equal(string.Empty, property.BindingInfo.BinderModelName);
}
[Fact]
public void InferBoundPropertyModelPrefixes_SetsModelPrefix_ForCollectionTypeFromValueProvider()
{
// Arrange
var controller = GetControllerModel(typeof(ControllerWithBoundCollectionProperty));
var provider = GetProvider();
// Act
provider.InferBoundPropertyModelPrefixes(controller);
// Assert
var property = Assert.Single(controller.ControllerProperties);
Assert.Null(property.BindingInfo.BinderModelName);
}
[Fact]
public void InferParameterModelPrefixes_SetsModelPrefix_ForComplexTypeFromValueProvider()
{
@ -1008,7 +1094,7 @@ Environment.NewLine + "int b";
[HttpGet("parameter-with-model-binder-attribute")]
public IActionResult ModelBinderAttribute([ModelBinder(Name = "top")] int value) => null;
[HttpGet("parameter-with-fromquery")]
public IActionResult FromQuery([FromQuery] int value) => null;
@ -1021,6 +1107,15 @@ Environment.NewLine + "int b";
[HttpGet("parameter-with-fromquery-on-complextype-and-customname")]
public IActionResult FromQueryOnComplexTypeWithCustomName([FromQuery(Name = "gps")] GpsCoordinates gpsCoordinates) => null;
[HttpGet("parameter-with-fromquery-on-collection-type")]
public IActionResult FromQueryOnCollectionType([FromQuery] ICollection<int> value) => null;
[HttpGet("parameter-with-fromquery-on-array-type")]
public IActionResult FromQueryOnArrayType([FromQuery] int[] value) => null;
[HttpGet("parameter-with-fromquery-on-array-type-customname")]
public IActionResult FromQueryOnArrayTypeWithCustomName([FromQuery(Name = "ids")] int[] value) => null;
[HttpGet("parameter-with-fromroute")]
public IActionResult FromRoute([FromRoute] int value) => null;
@ -1118,6 +1213,15 @@ Environment.NewLine + "int b";
public IActionResult SomeAction([FromQuery] TestModel test) => null;
}
[ApiController]
private class ControllerWithBoundCollectionProperty
{
[FromQuery]
public List<int> TestProperty { get; set; }
public IActionResult SomeAction([FromQuery] List<int> test) => null;
}
private class Car { }
[ApiController]