[Fixes #2900] Get rid of manual body-read-state-tracking
This commit is contained in:
parent
dd737ce946
commit
e12d44b40a
|
|
@ -1,29 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents state of models which are bound using body.
|
|
||||||
/// </summary>
|
|
||||||
public enum BodyBindingState
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents if there has been no metadata found which needs to read the body during the current
|
|
||||||
/// model binding process.
|
|
||||||
/// </summary>
|
|
||||||
NotBodyBased,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents if there is a <see cref="BindingSource.Body"/> that
|
|
||||||
/// has been found during the current model binding process.
|
|
||||||
/// </summary>
|
|
||||||
FormatterBased,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents if there is a <see cref = "BindingSource.Form" /> that
|
|
||||||
/// has been found during the current model binding process.
|
|
||||||
/// </summary>
|
|
||||||
FormBased
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -13,11 +13,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class OperationBindingContext
|
public class OperationBindingContext
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Represents if there has been a body bound model found during the current model binding process.
|
|
||||||
/// </summary>
|
|
||||||
public BodyBindingState BodyBindingState { get; set; } = BodyBindingState.NotBodyBased;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="HttpContext"/> for the current request.
|
/// Gets or sets the <see cref="HttpContext"/> for the current request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingContext.OperationBindingContext.BodyBindingState =
|
|
||||||
newBindingContext.OperationBindingContext.BodyBindingState;
|
|
||||||
|
|
||||||
var bindingKey = bindingContext.ModelName;
|
var bindingKey = bindingContext.ModelName;
|
||||||
if (modelBindingResult.IsModelSet)
|
if (modelBindingResult.IsModelSet)
|
||||||
{
|
{
|
||||||
|
|
@ -159,8 +156,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
IsTopLevelObject = oldBindingContext.IsTopLevelObject,
|
IsTopLevelObject = oldBindingContext.IsTopLevelObject,
|
||||||
};
|
};
|
||||||
|
|
||||||
newBindingContext.OperationBindingContext.BodyBindingState = GetBodyBindingState(oldBindingContext);
|
|
||||||
|
|
||||||
// If the property has a specified data binding sources, we need to filter the set of value providers
|
// If the property has a specified data binding sources, we need to filter the set of value providers
|
||||||
// to just those that match. We can skip filtering when IsGreedy == true, because that can't use
|
// to just those that match. We can skip filtering when IsGreedy == true, because that can't use
|
||||||
// value providers.
|
// value providers.
|
||||||
|
|
@ -197,38 +192,5 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
return newBindingContext;
|
return newBindingContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BodyBindingState GetBodyBindingState(ModelBindingContext oldBindingContext)
|
|
||||||
{
|
|
||||||
var bindingSource = oldBindingContext.BindingSource;
|
|
||||||
|
|
||||||
var willReadBodyWithFormatter = bindingSource == BindingSource.Body;
|
|
||||||
var willReadBodyAsFormData = bindingSource == BindingSource.Form;
|
|
||||||
|
|
||||||
var currentModelNeedsToReadBody = willReadBodyWithFormatter || willReadBodyAsFormData;
|
|
||||||
var oldState = oldBindingContext.OperationBindingContext.BodyBindingState;
|
|
||||||
|
|
||||||
// We need to throw if there are multiple models which can cause body to be read multiple times.
|
|
||||||
// Reading form data multiple times is ok since we cache form data. For the models marked to read using
|
|
||||||
// formatters, multiple reads are not allowed.
|
|
||||||
if (oldState == BodyBindingState.FormatterBased && currentModelNeedsToReadBody ||
|
|
||||||
oldState == BodyBindingState.FormBased && willReadBodyWithFormatter)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(Resources.MultipleBodyParametersOrPropertiesAreNotAllowed);
|
|
||||||
}
|
|
||||||
|
|
||||||
var state = oldBindingContext.OperationBindingContext.BodyBindingState;
|
|
||||||
if (willReadBodyWithFormatter)
|
|
||||||
{
|
|
||||||
state = BodyBindingState.FormatterBased;
|
|
||||||
}
|
|
||||||
else if (willReadBodyAsFormData && oldState != BodyBindingState.FormatterBased)
|
|
||||||
{
|
|
||||||
// Only update the model binding state if we have not discovered formatter based state already.
|
|
||||||
state = BodyBindingState.FormBased;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -157,24 +157,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Equal("50", await response.Content.ReadAsStringAsync());
|
Assert.Equal("50", await response.Content.ReadAsStringAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MultipleParametersMarkedWithFromBody_Throws()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
|
||||||
var client = server.CreateClient();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var response = await client.GetAsync("http://localhost/FromAttributes/FromBodyParametersThrows");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var exception = response.GetServerException();
|
|
||||||
Assert.Equal(typeof(InvalidOperationException).FullName, exception.ExceptionType);
|
|
||||||
Assert.Equal(
|
|
||||||
"More than one parameter and/or property is bound to the HTTP request's content.",
|
|
||||||
exception.ExceptionMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ControllerPropertyAndAnActionWithoutFromBody_InvokesWithoutErrors()
|
public async Task ControllerPropertyAndAnActionWithoutFromBody_InvokesWithoutErrors()
|
||||||
{
|
{
|
||||||
|
|
@ -189,114 +171,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ControllerPropertyAndAnActionParameterWithFromBody_Throws()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
|
||||||
var client = server.CreateClient();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var response = await client.GetAsync("http://localhost/FromBodyControllerProperty/AddUser");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var exception = response.GetServerException();
|
|
||||||
Assert.Equal(typeof(InvalidOperationException).FullName, exception.ExceptionType);
|
|
||||||
Assert.Equal(
|
|
||||||
"More than one parameter and/or property is bound to the HTTP request's content.",
|
|
||||||
exception.ExceptionMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ControllerPropertyAndAModelPropertyWithFromBody_Throws()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
|
||||||
var client = server.CreateClient();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var response = await client.GetAsync("http://localhost/FromBodyControllerProperty/AddUser");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var exception = response.GetServerException();
|
|
||||||
Assert.Equal(typeof(InvalidOperationException).FullName, exception.ExceptionType);
|
|
||||||
Assert.Equal(
|
|
||||||
"More than one parameter and/or property is bound to the HTTP request's content.",
|
|
||||||
exception.ExceptionMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MultipleControllerPropertiesMarkedWithFromBody_Throws()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
|
||||||
var client = server.CreateClient();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var response = await client.GetAsync("http://localhost/MultiplePropertiesFromBody/GetUser");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var exception = response.GetServerException();
|
|
||||||
Assert.Equal(typeof(InvalidOperationException).FullName, exception.ExceptionType);
|
|
||||||
Assert.Equal(
|
|
||||||
"More than one parameter and/or property is bound to the HTTP request's content.",
|
|
||||||
exception.ExceptionMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MultipleParameterAndPropertiesMarkedWithFromBody_Throws()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
|
||||||
var client = server.CreateClient();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var response = await client.GetAsync("http://localhost/FromAttributes/FromBodyParameterAndPropertyThrows");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var exception = response.GetServerException();
|
|
||||||
Assert.Equal(typeof(InvalidOperationException).FullName, exception.ExceptionType);
|
|
||||||
Assert.Equal(
|
|
||||||
"More than one parameter and/or property is bound to the HTTP request's content.",
|
|
||||||
exception.ExceptionMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MultipleParametersMarkedWith_FromFormAndFromBody_Throws()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
|
||||||
var client = server.CreateClient();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var response = await client.GetAsync("http://localhost/FromAttributes/FormAndBody_AsParameters_Throws");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var exception = response.GetServerException();
|
|
||||||
Assert.Equal(typeof(InvalidOperationException).FullName, exception.ExceptionType);
|
|
||||||
Assert.Equal(
|
|
||||||
"More than one parameter and/or property is bound to the HTTP request's content.",
|
|
||||||
exception.ExceptionMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task MultipleParameterAndPropertiesMarkedWith_FromFormAndFromBody_Throws()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
|
||||||
var client = server.CreateClient();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var response = await client.GetAsync("http://localhost/FromAttributes/FormAndBody_Throws");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var exception = response.GetServerException();
|
|
||||||
Assert.Equal(typeof(InvalidOperationException).FullName, exception.ExceptionType);
|
|
||||||
Assert.Equal(
|
|
||||||
"More than one parameter and/or property is bound to the HTTP request's content.",
|
|
||||||
exception.ExceptionMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CanBind_MultipleParameters_UsingFromForm()
|
public async Task CanBind_MultipleParameters_UsingFromForm()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
|
|
||||||
return new OperationBindingContext()
|
return new OperationBindingContext()
|
||||||
{
|
{
|
||||||
BodyBindingState = BodyBindingState.NotBodyBased,
|
|
||||||
HttpContext = httpContext,
|
HttpContext = httpContext,
|
||||||
InputFormatters = actionBindingContext.InputFormatters,
|
InputFormatters = actionBindingContext.InputFormatters,
|
||||||
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
|
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
|
||||||
|
|
|
||||||
|
|
@ -50,22 +50,5 @@ namespace ModelBindingWebSite.Controllers
|
||||||
user.HomeAddress = defaultAddress;
|
user.HomeAddress = defaultAddress;
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FromBodyParametersThrows([FromBody] int id, [FromBody] string emp)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Customer has a FromBody Property.
|
|
||||||
public void FromBodyParameterAndPropertyThrows([FromBody] Person p, Customer customer)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FormAndBody_Throws([FromForm] Person p, Customer customer)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FormAndBody_AsParameters_Throws([FromBody] int id, [FromForm] string emp)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -21,10 +21,5 @@ namespace ModelBindingWebSite.Controllers
|
||||||
{
|
{
|
||||||
return customer;
|
return customer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will throw as a controller property and a parameter name are being read from body.
|
|
||||||
public void AddUser([FromBody] User user)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
// 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 Microsoft.AspNet.Mvc;
|
|
||||||
using ModelBindingWebSite.Models;
|
|
||||||
|
|
||||||
namespace ModelBindingWebSite.Controllers
|
|
||||||
{
|
|
||||||
public class MultiplePropertiesFromBodyController : Controller
|
|
||||||
{
|
|
||||||
[FromBody]
|
|
||||||
public User SiteUser { get; set; }
|
|
||||||
|
|
||||||
[FromBody]
|
|
||||||
public Country Country { get; set; }
|
|
||||||
|
|
||||||
public User GetUser()
|
|
||||||
{
|
|
||||||
return SiteUser;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -20,7 +20,7 @@ namespace ModelBindingWebSite
|
||||||
{
|
{
|
||||||
m.MaxModelValidationErrors = 8;
|
m.MaxModelValidationErrors = 8;
|
||||||
m.ModelBinders.Insert(0, new TestBindingSourceModelBinder());
|
m.ModelBinders.Insert(0, new TestBindingSourceModelBinder());
|
||||||
|
|
||||||
m.ValidationExcludeFilters.Add(typeof(Address));
|
m.ValidationExcludeFilters.Add(typeof(Address));
|
||||||
|
|
||||||
// ModelMetadataController relies on additional values AdditionalValuesMetadataProvider provides.
|
// ModelMetadataController relies on additional values AdditionalValuesMetadataProvider provides.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue