Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
[Fixes #1389] Can't bind complex type data from route parameters.
Browse files Browse the repository at this point in the history
Changed DictionaryBasedValueProvider to do a prefix check instead of just checking if the underlying
dictionary contains the key.
  • Loading branch information
javiercn committed Oct 17, 2014
1 parent 66f626b commit 98d749d
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding.Internal;

namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class DictionaryBasedValueProvider<TBinderMarker> : MarkerAwareValueProvider<TBinderMarker>
where TBinderMarker : IValueBinderMarker
{
private readonly IDictionary<string, object> _values;
private PrefixContainer _prefixContainer;

public DictionaryBasedValueProvider(IDictionary<string, object> values)
{
Expand All @@ -19,7 +21,18 @@ public DictionaryBasedValueProvider(IDictionary<string, object> values)

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)
Expand Down
27 changes: 26 additions & 1 deletion test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public async Task CompositeModelBinder_Restricts_ValueProviders(string actionNam
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>>
{
Expand Down Expand Up @@ -107,6 +107,31 @@ public async Task ParametersAreAlwaysCreated()
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()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class DictionaryBasedValueProviderTestss
public class DictionaryBasedValueProviderTests
{
[Fact]
public async Task GetValueProvider_ReturnsNull_WhenKeyIsNotFound()
Expand Down Expand Up @@ -63,6 +63,67 @@ public async Task ContainsPrefixAsync_ReturnsNullValue_IfKeyIsPresent()
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()
{
Expand Down
12 changes: 12 additions & 0 deletions test/WebSites/ModelBindingWebSite/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ public bool ActionWithCancellationTokenModel(CancellationTokenModel wrapper)
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>();
Expand Down

0 comments on commit 98d749d

Please sign in to comment.