[Fixes #2900] Get rid of manual body-read-state-tracking

This commit is contained in:
Kiran Challa 2015-08-21 13:56:18 -07:00
parent dd737ce946
commit e12d44b40a
9 changed files with 1 additions and 244 deletions

View File

@ -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
}
}

View File

@ -13,11 +13,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </summary>
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>
/// Gets or sets the <see cref="HttpContext"/> for the current request.
/// </summary>

View File

@ -64,9 +64,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return null;
}
bindingContext.OperationBindingContext.BodyBindingState =
newBindingContext.OperationBindingContext.BodyBindingState;
var bindingKey = bindingContext.ModelName;
if (modelBindingResult.IsModelSet)
{
@ -159,8 +156,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
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
// to just those that match. We can skip filtering when IsGreedy == true, because that can't use
// value providers.
@ -197,38 +192,5 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
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;
}
}
}

View File

@ -157,24 +157,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
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]
public async Task ControllerPropertyAndAnActionWithoutFromBody_InvokesWithoutErrors()
{
@ -189,114 +171,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
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]
public async Task CanBind_MultipleParameters_UsingFromForm()
{

View File

@ -39,7 +39,6 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
return new OperationBindingContext()
{
BodyBindingState = BodyBindingState.NotBodyBased,
HttpContext = httpContext,
InputFormatters = actionBindingContext.InputFormatters,
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),

View File

@ -50,22 +50,5 @@ namespace ModelBindingWebSite.Controllers
user.HomeAddress = defaultAddress;
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)
{
}
}
}

View File

@ -21,10 +21,5 @@ namespace ModelBindingWebSite.Controllers
{
return customer;
}
// Will throw as a controller property and a parameter name are being read from body.
public void AddUser([FromBody] User user)
{
}
}
}

View File

@ -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;
}
}
}

View File

@ -20,7 +20,7 @@ namespace ModelBindingWebSite
{
m.MaxModelValidationErrors = 8;
m.ModelBinders.Insert(0, new TestBindingSourceModelBinder());
m.ValidationExcludeFilters.Add(typeof(Address));
// ModelMetadataController relies on additional values AdditionalValuesMetadataProvider provides.