[Fixes #1389] Can't bind complex type data from route parameters.

Changed DictionaryBasedValueProvider to do a prefix check instead of just checking if the underlying
dictionary contains the key.
This commit is contained in:
jacalvar 2014-10-17 13:13:14 -07:00
parent 66f626b828
commit 98d749d03c
4 changed files with 114 additions and 3 deletions

View File

@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
@ -11,6 +12,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
where TBinderMarker : IValueBinderMarker
{
private readonly IDictionary<string, object> _values;
private PrefixContainer _prefixContainer;
public DictionaryBasedValueProvider(IDictionary<string, object> values)
{
@ -19,7 +21,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public override Task<bool> ContainsPrefixAsync(string key)
{
return Task.FromResult(_values.ContainsKey(key));
var prefixContainer = GetOrCreatePrefixContainer();
return Task.FromResult(prefixContainer.ContainsPrefix(key));
}
private PrefixContainer GetOrCreatePrefixContainer()
{
if (_prefixContainer == null)
{
_prefixContainer = new PrefixContainer(_values.Keys);
}
return _prefixContainer;
}
public override Task<ValueProviderResult> GetValueAsync([NotNull] string key)

View File

@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var client = server.CreateClient();
// Provide all three values, it should bind based on the attribute on the action method.
var request = new HttpRequestMessage(HttpMethod.Post,
var request = new HttpRequestMessage(HttpMethod.Post,
string.Format("http://localhost/CompositeTest/{0}/valueFromRoute?param=valueFromQuery", actionName));
var nameValueCollection = new List<KeyValuePair<string, string>>
{
@ -107,6 +107,31 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal(12, person.Age);
}
[Theory]
[InlineData("http://localhost/Home/ActionWithPersonFromUrlWithPrefix/Javier/26")]
[InlineData("http://localhost/Home/ActionWithPersonFromUrlWithoutPrefix/Javier/26")]
public async Task CanBind_ComplexData_FromRouteData(string url)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
// Act
var response = await
client.GetAsync(url);
//Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
Assert.NotNull(body);
var person = JsonConvert.DeserializeObject<Person>(body);
Assert.NotNull(person);
Assert.Equal("Javier", person.Name);
Assert.Equal(26, person.Age);
}
[Fact]
public async Task ModelBindCancellationTokenParameteres()
{

View File

@ -8,7 +8,7 @@ using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class DictionaryBasedValueProviderTestss
public class DictionaryBasedValueProviderTests
{
[Fact]
public async Task GetValueProvider_ReturnsNull_WhenKeyIsNotFound()
@ -63,6 +63,67 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Null(result.AttemptedValue);
}
[Theory]
[InlineData("foo")]
[InlineData("bar")]
[InlineData("bar.baz")]
public async Task ContainsPrefixAsync_ReturnsTrue_ForKnownPrefixes(string prefix)
{
// Arrange
var values = new Dictionary<string, object>
{
{ "foo", 1 },
{ "bar.baz", 1 },
};
var valueProvider = new DictionaryBasedValueProvider<TestValueBinderMarker>(values);
// Act
var result = await valueProvider.ContainsPrefixAsync(prefix);
// Assert
Assert.True(result);
}
[Theory]
[InlineData("bar", "1")]
[InlineData("bar.baz", "2")]
public async Task GetValueAsync_ReturnsCorrectValue_ForKnownKeys(string prefix, string expectedValue)
{
// Arrange
var values = new Dictionary<string, object>
{
{ "bar", 1 },
{ "bar.baz", 2 },
};
var valueProvider = new DictionaryBasedValueProvider<TestValueBinderMarker>(values);
// Act
var result = await valueProvider.GetValueAsync(prefix);
// Assert
Assert.Equal(expectedValue, (string)result.AttemptedValue);
}
[Fact]
public async Task GetValueAsync_DoesNotReturnAValue_ForAKeyPrefix()
{
// Arrange
var values = new Dictionary<string, object>
{
{ "bar.baz", 2 },
};
var valueProvider = new DictionaryBasedValueProvider<TestValueBinderMarker>(values);
// Act
var result = await valueProvider.GetValueAsync("bar");
// Assert
Assert.Null(result);
}
[Fact]
public async Task ContainsPrefixAsync_ReturnsFalse_IfKeyIsNotPresent()
{

View File

@ -37,6 +37,18 @@ namespace ModelBindingWebSite.Controllers
return wrapper.CancellationToken == ActionContext.HttpContext.RequestAborted;
}
[HttpGet("Home/ActionWithPersonFromUrlWithPrefix/{person.name}/{person.age}")]
public Person ActionWithPersonFromUrlWithPrefix([FromRoute] Person person)
{
return person;
}
[HttpGet("Home/ActionWithPersonFromUrlWithoutPrefix/{name}/{age}")]
public Person ActionWithPersonFromUrlWithoutPrefix([FromRoute] Person person)
{
return person;
}
private Dictionary<string, string> CreateValidationDictionary()
{
var result = new Dictionary<string, string>();