[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:
parent
66f626b828
commit
98d749d03c
|
|
@ -4,6 +4,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
|
|
@ -11,6 +12,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
where TBinderMarker : IValueBinderMarker
|
where TBinderMarker : IValueBinderMarker
|
||||||
{
|
{
|
||||||
private readonly IDictionary<string, object> _values;
|
private readonly IDictionary<string, object> _values;
|
||||||
|
private PrefixContainer _prefixContainer;
|
||||||
|
|
||||||
public DictionaryBasedValueProvider(IDictionary<string, object> values)
|
public DictionaryBasedValueProvider(IDictionary<string, object> values)
|
||||||
{
|
{
|
||||||
|
|
@ -19,7 +21,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
public override Task<bool> ContainsPrefixAsync(string key)
|
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)
|
public override Task<ValueProviderResult> GetValueAsync([NotNull] string key)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
var client = server.CreateClient();
|
var client = server.CreateClient();
|
||||||
|
|
||||||
// Provide all three values, it should bind based on the attribute on the action method.
|
// 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));
|
string.Format("http://localhost/CompositeTest/{0}/valueFromRoute?param=valueFromQuery", actionName));
|
||||||
var nameValueCollection = new List<KeyValuePair<string, string>>
|
var nameValueCollection = new List<KeyValuePair<string, string>>
|
||||||
{
|
{
|
||||||
|
|
@ -107,6 +107,31 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Equal(12, person.Age);
|
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]
|
[Fact]
|
||||||
public async Task ModelBindCancellationTokenParameteres()
|
public async Task ModelBindCancellationTokenParameteres()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
public class DictionaryBasedValueProviderTestss
|
public class DictionaryBasedValueProviderTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetValueProvider_ReturnsNull_WhenKeyIsNotFound()
|
public async Task GetValueProvider_ReturnsNull_WhenKeyIsNotFound()
|
||||||
|
|
@ -63,6 +63,67 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
Assert.Null(result.AttemptedValue);
|
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]
|
[Fact]
|
||||||
public async Task ContainsPrefixAsync_ReturnsFalse_IfKeyIsNotPresent()
|
public async Task ContainsPrefixAsync_ReturnsFalse_IfKeyIsNotPresent()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,18 @@ namespace ModelBindingWebSite.Controllers
|
||||||
return wrapper.CancellationToken == ActionContext.HttpContext.RequestAborted;
|
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()
|
private Dictionary<string, string> CreateValidationDictionary()
|
||||||
{
|
{
|
||||||
var result = new Dictionary<string, string>();
|
var result = new Dictionary<string, string>();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue