diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormCollectionModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormCollectionModelBinder.cs index f4d96087b7..8d30f3268d 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormCollectionModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormCollectionModelBinder.cs @@ -109,6 +109,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding return null; } + public bool TryGetValue(string key, out StringValues value) + { + value = default(StringValues); + return false; + } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); @@ -132,7 +138,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding IReadOnlyList IFormFileCollection.GetFiles(string name) { - throw new NotImplementedException(); + return null; } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ReadableStringCollectionValueProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormValueProvider.cs similarity index 86% rename from src/Microsoft.AspNet.Mvc.Core/ModelBinding/ReadableStringCollectionValueProvider.cs rename to src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormValueProvider.cs index fd50bd5be4..c2b13ecf82 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ReadableStringCollectionValueProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormValueProvider.cs @@ -9,23 +9,23 @@ using Microsoft.AspNet.Http; namespace Microsoft.AspNet.Mvc.ModelBinding { /// - /// An adapter for data stored in an . + /// An adapter for data stored in an . /// - public class ReadableStringCollectionValueProvider : BindingSourceValueProvider, IEnumerableValueProvider + public class FormValueProvider : BindingSourceValueProvider, IEnumerableValueProvider { private readonly CultureInfo _culture; private PrefixContainer _prefixContainer; - private IReadableStringCollection _values; + private IFormCollection _values; /// - /// Creates a provider for wrapping an existing set of key value pairs. + /// Creates a value provider for . /// /// The for the data. /// The key value pairs to wrap. /// The culture to return with ValueProviderResult instances. - public ReadableStringCollectionValueProvider( + public FormValueProvider( BindingSource bindingSource, - IReadableStringCollection values, + IFormCollection values, CultureInfo culture) : base(bindingSource) { diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormValueProviderFactory.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormValueProviderFactory.cs index da6e675d50..531b9c5c85 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormValueProviderFactory.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/FormValueProviderFactory.cs @@ -29,7 +29,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding private static async Task CreateValueProviderAsync(HttpRequest request) { - return new ReadableStringCollectionValueProvider( + return new FormValueProvider( BindingSource.Form, await request.ReadFormAsync(), CultureInfo.CurrentCulture); diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/QueryStringValueProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/QueryStringValueProvider.cs new file mode 100644 index 0000000000..4004985573 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/QueryStringValueProvider.cs @@ -0,0 +1,103 @@ +// 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 System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + /// + /// An adapter for data stored in an . + /// + public class QueryStringValueProvider : BindingSourceValueProvider, IEnumerableValueProvider + { + private readonly CultureInfo _culture; + private PrefixContainer _prefixContainer; + private IQueryCollection _values; + + /// + /// Creates a value provider for . + /// + /// The for the data. + /// The key value pairs to wrap. + /// The culture to return with ValueProviderResult instances. + public QueryStringValueProvider( + BindingSource bindingSource, + IQueryCollection values, + CultureInfo culture) + : base(bindingSource) + { + if (bindingSource == null) + { + throw new ArgumentNullException(nameof(bindingSource)); + } + + if (values == null) + { + throw new ArgumentNullException(nameof(values)); + } + + _values = values; + _culture = culture; + } + + public CultureInfo Culture + { + get + { + return _culture; + } + } + + protected PrefixContainer PrefixContainer + { + get + { + if (_prefixContainer == null) + { + _prefixContainer = new PrefixContainer(_values.Keys); + } + + return _prefixContainer; + } + } + + /// + public override bool ContainsPrefix(string prefix) + { + return PrefixContainer.ContainsPrefix(prefix); + } + + /// + public virtual IDictionary GetKeysFromPrefix(string prefix) + { + if (prefix == null) + { + throw new ArgumentNullException(nameof(prefix)); + } + + return PrefixContainer.GetKeysFromPrefix(prefix); + } + + /// + public override ValueProviderResult GetValue(string key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + var values = _values[key]; + if (values.Count == 0) + { + return ValueProviderResult.None; + } + else + { + return new ValueProviderResult(values, _culture); + } + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/QueryStringValueProviderFactory.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/QueryStringValueProviderFactory.cs index 2e7969b9e9..6538bd0be6 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/QueryStringValueProviderFactory.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/QueryStringValueProviderFactory.cs @@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding throw new ArgumentNullException(nameof(context)); } - return Task.FromResult(new ReadableStringCollectionValueProvider( + return Task.FromResult(new QueryStringValueProvider( BindingSource.Query, context.HttpContext.Request.Query, CultureInfo.InvariantCulture)); diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/CacheTagHelper.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/CacheTagHelper.cs index 526b1cb1d1..6a194bc9fe 100644 --- a/src/Microsoft.AspNet.Mvc.TagHelpers/CacheTagHelper.cs +++ b/src/Microsoft.AspNet.Mvc.TagHelpers/CacheTagHelper.cs @@ -12,6 +12,7 @@ using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.ViewFeatures; using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Primitives; namespace Microsoft.AspNet.Mvc.TagHelpers { @@ -195,9 +196,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers .Append(VaryBy); } - AddStringCollectionKey(builder, nameof(VaryByCookie), VaryByCookie, request.Cookies); - AddStringCollectionKey(builder, nameof(VaryByHeader), VaryByHeader, request.Headers); - AddStringCollectionKey(builder, nameof(VaryByQuery), VaryByQuery, request.Query); + AddStringCollectionKey(builder, nameof(VaryByCookie), VaryByCookie, request.Cookies, (c, key) => c[key]); + AddStringCollectionKey(builder, nameof(VaryByHeader), VaryByHeader, request.Headers, (c, key) => c[key]); + AddStringCollectionKey(builder, nameof(VaryByQuery), VaryByQuery, request.Query, (c, key) => c[key]); AddVaryByRouteKey(builder); if (VaryByUser) @@ -251,7 +252,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers StringBuilder builder, string keyName, string value, - IReadableStringCollection sourceCollection) + IDictionary sourceCollection) { if (!string.IsNullOrEmpty(value)) { @@ -281,11 +282,12 @@ namespace Microsoft.AspNet.Mvc.TagHelpers } } - private static void AddStringCollectionKey( + private static void AddStringCollectionKey( StringBuilder builder, string keyName, string value, - IHeaderDictionary sourceCollection) + T sourceCollection, + Func accessor) { if (!string.IsNullOrEmpty(value)) { @@ -301,7 +303,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers builder.Append(item) .Append(CacheKeyTokenSeparator) - .Append(sourceCollection[item]) + .Append(accessor(sourceCollection, item)) .Append(CacheKeyTokenSeparator); } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeValueProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeValueProviderTest.cs index 04baa0e398..299bc0cdc0 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeValueProviderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/CompositeValueProviderTest.cs @@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { protected override IEnumerableValueProvider GetEnumerableValueProvider( BindingSource bindingSource, - IDictionary values, + Dictionary values, CultureInfo culture) { var emptyValueProvider = diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryModelBinderTest.cs index b91d45d9b8..9c81b6d3af 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/DictionaryModelBinderTest.cs @@ -444,11 +444,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test var backingStore = dictionary.ToDictionary( kvp => string.Format(keyFormat, kvp.Key), kvp => (StringValues)kvp.Value); - var stringCollection = new ReadableStringCollection(backingStore); + + var formCollection = new FormCollection(backingStore); - return new ReadableStringCollectionValueProvider( + return new FormValueProvider( BindingSource.Form, - stringCollection, + formCollection, CultureInfo.InvariantCulture); } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/EnumerableValueProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/EnumerableValueProviderTest.cs index d3395fab36..01b0f43664 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/EnumerableValueProviderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/EnumerableValueProviderTest.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { public abstract class EnumerableValueProviderTest { - private static readonly IDictionary _backingStore = new Dictionary + private static readonly Dictionary _backingStore = new Dictionary { { "some", new[] { "someValue1", "someValue2" } }, { "null_value", StringValues.Empty }, @@ -279,7 +279,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding private IBindingSourceValueProvider GetBindingSourceValueProvider( BindingSource bindingSource, - IDictionary values, + Dictionary values, CultureInfo culture) { var provider = GetEnumerableValueProvider(bindingSource, values, culture) as IBindingSourceValueProvider; @@ -292,7 +292,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding protected abstract IEnumerableValueProvider GetEnumerableValueProvider( BindingSource bindingSource, - IDictionary values, + Dictionary values, CultureInfo culture); } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormCollectionModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormCollectionModelBinderTest.cs index 4bf57d7979..56adc1032d 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormCollectionModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormCollectionModelBinderTest.cs @@ -134,20 +134,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding return bindingContext; } - - private class MyFormCollection : ReadableStringCollection, IFormCollection - { - public MyFormCollection(IDictionary store) : this(store, new FormFileCollection()) - { - } - - public MyFormCollection(IDictionary store, IFormFileCollection files) : base(store) - { - Files = files; - } - - public IFormFileCollection Files { get; private set; } - } } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormValueProviderFactoryTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormValueProviderFactoryTest.cs index e6c8c36782..355957f7c4 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormValueProviderFactoryTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormValueProviderFactoryTest.cs @@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test var result = await factory.GetValueProviderAsync(context); // Assert - var valueProvider = Assert.IsType(result); + var valueProvider = Assert.IsType(result); Assert.Equal(CultureInfo.CurrentCulture, valueProvider.Culture); } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ReadableStringCollectionValueProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormValueProviderTest.cs similarity index 63% rename from test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ReadableStringCollectionValueProviderTest.cs rename to test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormValueProviderTest.cs index 6e24bbce28..bbbf144f67 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ReadableStringCollectionValueProviderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/FormValueProviderTest.cs @@ -8,15 +8,15 @@ using Microsoft.Extensions.Primitives; namespace Microsoft.AspNet.Mvc.ModelBinding { - public class ReadableStringCollectionValueProviderTest : EnumerableValueProviderTest + public class FormValueProviderTest : EnumerableValueProviderTest { protected override IEnumerableValueProvider GetEnumerableValueProvider( BindingSource bindingSource, - IDictionary values, + Dictionary values, CultureInfo culture) { - var backingStore = new ReadableStringCollection(values); - return new ReadableStringCollectionValueProvider(bindingSource, backingStore, culture); + var backingStore = new FormCollection(values); + return new FormValueProvider(bindingSource, backingStore, culture); } } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs index 009d54c431..838eca60e8 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderFactoryTest.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test { public class JQueryFormValueProviderFactoryTest { - private static readonly IDictionary _backingStore = new Dictionary + private static readonly Dictionary _backingStore = new Dictionary { { "[]", new[] { "found" } }, { "[]property1", new[] { "found" } }, @@ -116,7 +116,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test private static ValueProviderFactoryContext CreateContext( string contentType, - IDictionary formValues) + Dictionary formValues) { var context = new DefaultHttpContext(); context.Request.ContentType = contentType; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderTest.cs index 18643b1ed2..162cdbdc05 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/JQueryFormValueProviderTest.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { protected override IEnumerableValueProvider GetEnumerableValueProvider( BindingSource bindingSource, - IDictionary values, + Dictionary values, CultureInfo culture) { return new JQueryFormValueProvider(bindingSource, values, culture); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/QueryStringValueProviderFactoryTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/QueryStringValueProviderFactoryTest.cs index ecf753cb1d..1aeb3871bf 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/QueryStringValueProviderFactoryTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/QueryStringValueProviderFactoryTest.cs @@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test { // Arrange var request = new Mock(); - request.SetupGet(f => f.Query).Returns(Mock.Of()); + request.SetupGet(f => f.Query).Returns(Mock.Of()); var context = new Mock(); context.SetupGet(c => c.Items).Returns(new Dictionary()); context.SetupGet(c => c.Request).Returns(request.Object); @@ -35,7 +35,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test var result = await _factory.GetValueProviderAsync(factoryContext); // Assert - var valueProvider = Assert.IsType(result); + var valueProvider = Assert.IsType(result); Assert.Equal(CultureInfo.InvariantCulture, valueProvider.Culture); } #endif diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/QueryStringValueProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/QueryStringValueProviderTest.cs new file mode 100644 index 0000000000..deeb2e9aa9 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/QueryStringValueProviderTest.cs @@ -0,0 +1,22 @@ +// 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 System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.Http.Internal; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + public class QueryStringValueProviderTest : EnumerableValueProviderTest + { + protected override IEnumerableValueProvider GetEnumerableValueProvider( + BindingSource bindingSource, + Dictionary values, + CultureInfo culture) + { + var backingStore = new QueryCollection(values); + return new QueryStringValueProvider(bindingSource, backingStore, culture); + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs index f2be9470ee..c2d13a5a8b 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/CollectionModelBinderIntegrationTest.cs @@ -908,7 +908,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests [MemberData(nameof(CollectionTypeData))] public async Task CollectionModelBinder_BindsParameterToExpectedType( Type parameterType, - IDictionary formContent, + Dictionary formContent, Type expectedType) { // Arrange diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs index a61b67d16f..ac2d8055ec 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs @@ -438,7 +438,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests [Theory] [MemberData(nameof(PersonStoreData))] - public async Task BindParameter_FromFormData_BindsCorrectly(IDictionary personStore) + public async Task BindParameter_FromFormData_BindsCorrectly(Dictionary personStore) { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();