Add support for jQuery syntax in form data
- #2705 - add `JQueryFormValueProvider` and `JQueryFormValueProviderFactory` - carry some code forward from MVC 5; correct to follow current coding guidelines - refactor `ReadableStringCollectionValueProviderTest` into abstract `EnumerableValueProviderTest` - enables reuse in new `JQueryFormValueProviderTest` - also run these tests in `CompositeValueProviderTest` nits: - do not create a duplicate `CompositeValueProvider` instance in `Filter()` - correct garbled sentence in `IBindingSourceValueProvider` doc comments - simplify `FormValueProviderFactoryTest` (no need for Moq) and correct test name - correct test class / file names - `CompositeValueProviderTests` -> `CompositeValueProviderTest` - `FormValueProviderFactoryTests` -> `FormValueProviderFactoryTest`
This commit is contained in:
parent
a7d717d19c
commit
bda850187d
|
|
@ -14,8 +14,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// Represents a <see cref="IValueProvider"/> whose values come from a collection of <see cref="IValueProvider"/>s.
|
||||
/// </summary>
|
||||
public class CompositeValueProvider :
|
||||
Collection<IValueProvider>,
|
||||
IEnumerableValueProvider,
|
||||
Collection<IValueProvider>,
|
||||
IEnumerableValueProvider,
|
||||
IBindingSourceValueProvider
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// created.
|
||||
/// </returns>
|
||||
public static CompositeValueProvider Create(
|
||||
[NotNull] IEnumerable<IValueProviderFactory> factories,
|
||||
[NotNull] IEnumerable<IValueProviderFactory> factories,
|
||||
[NotNull] ValueProviderFactoryContext context)
|
||||
{
|
||||
var composite = new CompositeValueProvider();
|
||||
|
|
@ -137,6 +137,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
|
||||
if (filteredValueProviders.Count == Count)
|
||||
{
|
||||
// No need for a new CompositeValueProvider.
|
||||
return this;
|
||||
}
|
||||
|
||||
return new CompositeValueProvider(filteredValueProviders);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using Microsoft.Framework.Internal;
|
|||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
/// <summary>
|
||||
/// A value provider which is which can filter its contents based on <see cref="BindingSource"/>.
|
||||
/// A value provider which can filter its contents based on <see cref="BindingSource"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value providers are by-default included. If a model does not specify a <see cref="BindingSource"/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="IValueProvider"/> for form data stored in an <see cref="IDictionary{string, string[]}"/> and
|
||||
/// generally accessed asynchronously.
|
||||
/// </summary>
|
||||
public class JQueryFormValueProvider : BindingSourceValueProvider, IEnumerableValueProvider
|
||||
{
|
||||
private readonly Func<Task<IDictionary<string, string[]>>> _valuesFactory;
|
||||
|
||||
private PrefixContainer _prefixContainer;
|
||||
private IDictionary<string, string[]> _values;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DictionaryBasedValueProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="bindingSource">The <see cref="BindingSource"/> of the data.</param>
|
||||
/// <param name="valuesFactory">A delegate which provides the values to wrap.</param>
|
||||
/// <param name="culture">The culture to return with ValueProviderResult instances.</param>
|
||||
public JQueryFormValueProvider(
|
||||
[NotNull] BindingSource bindingSource,
|
||||
[NotNull] Func<Task<IDictionary<string, string[]>>> valuesFactory,
|
||||
CultureInfo culture)
|
||||
: base(bindingSource)
|
||||
{
|
||||
_valuesFactory = valuesFactory;
|
||||
Culture = culture;
|
||||
}
|
||||
|
||||
// Internal for testing.
|
||||
internal JQueryFormValueProvider(
|
||||
[NotNull] BindingSource bindingSource,
|
||||
[NotNull] IDictionary<string, string[]> values,
|
||||
CultureInfo culture)
|
||||
: base(bindingSource)
|
||||
{
|
||||
_values = values;
|
||||
Culture = culture;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal CultureInfo Culture { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<bool> ContainsPrefixAsync(string prefix)
|
||||
{
|
||||
var prefixContainer = await GetPrefixContainerAsync();
|
||||
return prefixContainer.ContainsPrefix(prefix);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IDictionary<string, string>> GetKeysFromPrefixAsync(string prefix)
|
||||
{
|
||||
var prefixContainer = await GetPrefixContainerAsync();
|
||||
return prefixContainer.GetKeysFromPrefix(prefix);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<ValueProviderResult> GetValueAsync(string key)
|
||||
{
|
||||
var dictionary = await GetDictionary();
|
||||
|
||||
string[] values;
|
||||
if (dictionary.TryGetValue(key, out values) && values != null && values.Length > 0)
|
||||
{
|
||||
// Success.
|
||||
if (values.Length == 1)
|
||||
{
|
||||
return new ValueProviderResult(values[0], values[0], Culture);
|
||||
}
|
||||
|
||||
return new ValueProviderResult(values, string.Join(",", values), Culture);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<IDictionary<string, string[]>> GetDictionary()
|
||||
{
|
||||
if (_values == null)
|
||||
{
|
||||
Debug.Assert(_valuesFactory != null);
|
||||
|
||||
// Initialization race is OK providing data remains read-only.
|
||||
_values = await _valuesFactory();
|
||||
}
|
||||
|
||||
return _values;
|
||||
}
|
||||
|
||||
private async Task<PrefixContainer> GetPrefixContainerAsync()
|
||||
{
|
||||
if (_prefixContainer == null)
|
||||
{
|
||||
var dictionary = await GetDictionary();
|
||||
|
||||
// Initialization race is OK providing data remains read-only and object identity is not significant.
|
||||
_prefixContainer = new PrefixContainer(dictionary.Keys);
|
||||
}
|
||||
|
||||
return _prefixContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
// 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 System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc.Core;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class JQueryFormValueProviderFactory : IValueProviderFactory
|
||||
{
|
||||
public IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context)
|
||||
{
|
||||
var request = context.HttpContext.Request;
|
||||
|
||||
if (request.HasFormContentType)
|
||||
{
|
||||
return new JQueryFormValueProvider(
|
||||
BindingSource.Form,
|
||||
() => GetValueCollectionAsync(request),
|
||||
CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static async Task<IDictionary<string, string[]>> GetValueCollectionAsync(HttpRequest request)
|
||||
{
|
||||
var formCollection = await request.ReadFormAsync();
|
||||
|
||||
var dictionary = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var entry in formCollection)
|
||||
{
|
||||
var key = NormalizeJQueryToMvc(entry.Key);
|
||||
dictionary[key] = entry.Value;
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
// This is a helper method for Model Binding over a JQuery syntax.
|
||||
// Normalize from JQuery to MVC keys. The model binding infrastructure uses MVC keys.
|
||||
// x[] --> x
|
||||
// [] --> ""
|
||||
// x[12] --> x[12]
|
||||
// x[field] --> x.field, where field is not a number
|
||||
private static string NormalizeJQueryToMvc(string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var indexOpen = key.IndexOf('[');
|
||||
if (indexOpen == -1)
|
||||
{
|
||||
|
||||
// Fast path, no normalization needed.
|
||||
// This skips string conversion and allocating the string builder.
|
||||
return key;
|
||||
}
|
||||
|
||||
var builder = new StringBuilder();
|
||||
var position = 0;
|
||||
while (position < key.Length)
|
||||
{
|
||||
if (indexOpen == -1)
|
||||
{
|
||||
// No more brackets.
|
||||
builder.Append(key, position, key.Length - position);
|
||||
break;
|
||||
}
|
||||
|
||||
builder.Append(key, position, indexOpen - position); // everything up to "["
|
||||
|
||||
// Find closing bracket.
|
||||
var indexClose = key.IndexOf(']', indexOpen);
|
||||
if (indexClose == -1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
message: Resources.FormatJQueryFormValueProviderFactory_MissingClosingBracket(key),
|
||||
paramName: "key");
|
||||
}
|
||||
|
||||
if (indexClose == indexOpen + 1)
|
||||
{
|
||||
// Empty brackets signify an array. Just remove.
|
||||
}
|
||||
else if (char.IsDigit(key[indexOpen + 1]))
|
||||
{
|
||||
// Array index. Leave unchanged.
|
||||
builder.Append(key, indexOpen, indexClose - indexOpen + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Field name. Convert to dot notation.
|
||||
builder.Append('.');
|
||||
builder.Append(key, indexOpen + 1, indexClose - indexOpen - 1);
|
||||
}
|
||||
|
||||
position = indexClose + 1;
|
||||
indexOpen = key.IndexOf('[', position);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -47,6 +47,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
options.ValueProviderFactories.Add(new RouteValueValueProviderFactory());
|
||||
options.ValueProviderFactories.Add(new QueryStringValueProviderFactory());
|
||||
options.ValueProviderFactories.Add(new FormValueProviderFactory());
|
||||
options.ValueProviderFactories.Add(new JQueryFormValueProviderFactory());
|
||||
|
||||
// Set up metadata providers
|
||||
options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider());
|
||||
|
|
|
|||
|
|
@ -1034,6 +1034,22 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("Common_PropertyNotFound"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The key '{0}' is invalid JQuery syntax because it is missing a closing bracket.
|
||||
/// </summary>
|
||||
internal static string JQueryFormValueProviderFactory_MissingClosingBracket
|
||||
{
|
||||
get { return GetString("JQueryFormValueProviderFactory_MissingClosingBracket"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The key '{0}' is invalid JQuery syntax because it is missing a closing bracket.
|
||||
/// </summary>
|
||||
internal static string FormatJQueryFormValueProviderFactory_MissingClosingBracket(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("JQueryFormValueProviderFactory_MissingClosingBracket"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A value is required.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
|
|
@ -26,36 +26,36 @@
|
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
|
@ -318,6 +318,9 @@
|
|||
<data name="Common_PropertyNotFound" xml:space="preserve">
|
||||
<value>The property {0}.{1} could not be found.</value>
|
||||
</data>
|
||||
<data name="JQueryFormValueProviderFactory_MissingClosingBracket" xml:space="preserve">
|
||||
<value>The key '{0}' is invalid JQuery syntax because it is missing a closing bracket.</value>
|
||||
</data>
|
||||
<data name="KeyValuePair_BothKeyAndValueMustBePresent" xml:space="preserve">
|
||||
<value>A value is required.</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -2,17 +2,41 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if DNX451
|
||||
|
||||
using System;
|
||||
#endif
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
#if DNX451
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
#endif
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class CompositeValueProviderTests
|
||||
public class CompositeValueProviderTest : EnumerableValueProviderTest
|
||||
{
|
||||
protected override IEnumerableValueProvider GetEnumerableValueProvider(
|
||||
BindingSource bindingSource,
|
||||
IDictionary<string, string[]> values,
|
||||
CultureInfo culture)
|
||||
{
|
||||
var emptyValueProvider =
|
||||
new JQueryFormValueProvider(bindingSource, new Dictionary<string, string[]>(), culture);
|
||||
var valueProvider = new JQueryFormValueProvider(bindingSource, values, culture);
|
||||
|
||||
return new CompositeValueProvider(new[] { emptyValueProvider, valueProvider });
|
||||
}
|
||||
|
||||
protected override void CheckFilterExcludeResult(IValueProvider result)
|
||||
{
|
||||
// CompositeValueProvider returns an empty instance rather than null. CompositeModelBinder and
|
||||
// MutableObjectModelBinder depend on this empty instance.
|
||||
var compositeProvider = Assert.IsType<CompositeValueProvider>(result);
|
||||
Assert.Empty(compositeProvider);
|
||||
}
|
||||
|
||||
#if DNX451
|
||||
[Fact]
|
||||
public async Task GetKeysFromPrefixAsync_ReturnsResultFromFirstValueProviderThatReturnsValues()
|
||||
{
|
||||
|
|
@ -125,6 +149,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
// 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 System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public abstract class EnumerableValueProviderTest
|
||||
{
|
||||
private static readonly IDictionary<string, string[]> _backingStore = new Dictionary<string, string[]>
|
||||
{
|
||||
{ "some", new[] { "someValue1", "someValue2" } },
|
||||
{ "null_value", null },
|
||||
{ "prefix.name", new[] { "someOtherValue" } },
|
||||
{ "prefix.null_value", null },
|
||||
{ "prefix.property1.property", null },
|
||||
{ "prefix.property2[index]", null },
|
||||
{ "prefix[index1]", null },
|
||||
{ "prefix[index1].property1", null },
|
||||
{ "prefix[index1].property2", null },
|
||||
{ "prefix[index2].property", null },
|
||||
{ "[index]", null },
|
||||
{ "[index].property", null },
|
||||
{ "[index][anotherIndex]", null },
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public async Task ContainsPrefixAsync_WithEmptyCollection_ReturnsFalseForEmptyPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var backingStore = new Dictionary<string, string[]>();
|
||||
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, backingStore, culture: null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.ContainsPrefixAsync(string.Empty);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForEmptyPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.ContainsPrefixAsync(string.Empty);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForKnownPrefixes()
|
||||
{
|
||||
// Arrange
|
||||
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
|
||||
|
||||
// Act & Assert
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("some"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("prefix"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("prefix.name"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("[index]"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("prefix[index1]"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsFalseForUnknownPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.ContainsPrefixAsync("biff");
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetKeysFromPrefixAsync_EmptyPrefix_ReturnsAllPrefixes()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new Dictionary<string, string>
|
||||
{
|
||||
{ "index", "[index]" },
|
||||
{ "null_value", "null_value" },
|
||||
{ "prefix", "prefix" },
|
||||
{ "some", "some" },
|
||||
};
|
||||
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetKeysFromPrefixAsync(string.Empty);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, result.OrderBy(kvp => kvp.Key));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetKeysFromPrefixAsync_UnknownPrefix_ReturnsEmptyDictionary()
|
||||
{
|
||||
// Arrange
|
||||
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetKeysFromPrefixAsync("abc");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetKeysFromPrefixAsync_KnownPrefix_ReturnsMatchingItems()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new Dictionary<string, string>
|
||||
{
|
||||
{ "name", "prefix.name" },
|
||||
{ "null_value", "prefix.null_value" },
|
||||
{ "property1", "prefix.property1" },
|
||||
{ "property2", "prefix.property2" },
|
||||
{ "index1", "prefix[index1]" },
|
||||
{ "index2", "prefix[index2]" },
|
||||
};
|
||||
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetKeysFromPrefixAsync("prefix");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetKeysFromPrefixAsync_IndexPrefix_ReturnsMatchingItems()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new Dictionary<string, string>
|
||||
{
|
||||
{ "property", "[index].property" },
|
||||
{ "anotherIndex", "[index][anotherIndex]" }
|
||||
};
|
||||
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetKeysFromPrefixAsync("[index]");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetValueAsync_SingleValue()
|
||||
{
|
||||
// Arrange
|
||||
var culture = new CultureInfo("fr-FR");
|
||||
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetValueAsync("prefix.name");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("someOtherValue", result.RawValue);
|
||||
Assert.Equal("someOtherValue", result.AttemptedValue);
|
||||
Assert.Equal(culture, result.Culture);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetValueAsync_MultiValue()
|
||||
{
|
||||
// Arrange
|
||||
var culture = new CultureInfo("fr-FR");
|
||||
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetValueAsync("some");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(new[] { "someValue1", "someValue2" }, (IList<string>)result.RawValue);
|
||||
Assert.Equal("someValue1,someValue2", result.AttemptedValue);
|
||||
Assert.Equal(culture, result.Culture);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("null_value")]
|
||||
[InlineData("prefix.null_value")]
|
||||
public async Task GetValue_NullValue(string key)
|
||||
{
|
||||
// Arrange
|
||||
var culture = new CultureInfo("fr-FR");
|
||||
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetValueAsync(key);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetValueAsync_NullMultipleValue()
|
||||
{
|
||||
// Arrange
|
||||
var backingStore = new Dictionary<string, string[]>
|
||||
{
|
||||
{ "key", new string[] { null, null, "value" } },
|
||||
};
|
||||
var culture = new CultureInfo("fr-FR");
|
||||
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, backingStore, culture);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetValueAsync("key");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { null, null, "value" }, result.RawValue as IEnumerable<string>);
|
||||
Assert.Equal(",,value", result.AttemptedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetValueAsync_ReturnsNullIfKeyNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var valueProvider = GetEnumerableValueProvider(BindingSource.Query, _backingStore, culture: null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetValueAsync("prefix");
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FilterInclude()
|
||||
{
|
||||
// Arrange
|
||||
var provider = GetBindingSourceValueProvider(BindingSource.Query, _backingStore, culture: null);
|
||||
|
||||
var bindingSource = new BindingSource(
|
||||
BindingSource.Query.Id,
|
||||
displayName: null,
|
||||
isGreedy: true,
|
||||
isFromRequest: true);
|
||||
|
||||
// Act
|
||||
var result = provider.Filter(bindingSource);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Same(result, provider);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FilterExclude()
|
||||
{
|
||||
// Arrange
|
||||
var provider = GetBindingSourceValueProvider(BindingSource.Query, _backingStore, culture: null);
|
||||
|
||||
var bindingSource = new BindingSource(
|
||||
"Test",
|
||||
displayName: null,
|
||||
isGreedy: true,
|
||||
isFromRequest: true);
|
||||
|
||||
// Act
|
||||
var result = provider.Filter(bindingSource);
|
||||
|
||||
// Assert
|
||||
CheckFilterExcludeResult(result);
|
||||
}
|
||||
|
||||
protected virtual void CheckFilterExcludeResult(IValueProvider result)
|
||||
{
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
private IBindingSourceValueProvider GetBindingSourceValueProvider(
|
||||
BindingSource bindingSource,
|
||||
IDictionary<string, string[]> values,
|
||||
CultureInfo culture)
|
||||
{
|
||||
var provider = GetEnumerableValueProvider(bindingSource, values, culture) as IBindingSourceValueProvider;
|
||||
|
||||
// All IEnumerableValueProvider implementations also implement IBindingSourceValueProvider.
|
||||
Assert.NotNull(provider);
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
protected abstract IEnumerableValueProvider GetEnumerableValueProvider(
|
||||
BindingSource bindingSource,
|
||||
IDictionary<string, string[]> values,
|
||||
CultureInfo culture);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,15 @@
|
|||
// 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.
|
||||
|
||||
#if DNX451
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Features.Internal;
|
||||
using Moq;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||
{
|
||||
public class FormValueProviderFactoryTests
|
||||
public class FormValueProviderFactoryTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetValueProvider_ReturnsNull_WhenContentTypeIsNotFormUrlEncoded()
|
||||
|
|
@ -35,7 +30,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
[InlineData("application/x-www-form-urlencoded;charset=utf-8")]
|
||||
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq")]
|
||||
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq; charset=utf-8")]
|
||||
public void GetValueProvider_ReturnsValueProviderInstanceWithInvariantCulture(string contentType)
|
||||
public void GetValueProvider_ReturnsValueProviderInstanceWithCurrentCulture(string contentType)
|
||||
{
|
||||
// Arrange
|
||||
var context = CreateContext(contentType);
|
||||
|
|
@ -51,19 +46,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
|
||||
private static ValueProviderFactoryContext CreateContext(string contentType)
|
||||
{
|
||||
var collection = Mock.Of<IFormCollection>();
|
||||
var request = new Mock<HttpRequest>();
|
||||
request.Setup(f => f.ReadFormAsync(CancellationToken.None)).Returns(Task.FromResult(collection));
|
||||
request.SetupGet(r => r.ContentType).Returns(contentType);
|
||||
request.SetupGet(r => r.HasFormContentType).Returns(new FormFeature(request.Object).HasFormContentType);
|
||||
|
||||
var context = new Mock<HttpContext>();
|
||||
context.SetupGet(c => c.Request).Returns(request.Object);
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = contentType;
|
||||
|
||||
return new ValueProviderFactoryContext(
|
||||
context.Object,
|
||||
context,
|
||||
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
// 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 System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||
{
|
||||
public class JQueryFormValueProviderFactoryTest
|
||||
{
|
||||
private static readonly IDictionary<string, string[]> _backingStore = new Dictionary<string, string[]>
|
||||
{
|
||||
{ "[]", new[] { "found" } },
|
||||
{ "[]property1", new[] { "found" } },
|
||||
{ "property2[]", new[] { "found" } },
|
||||
{ "[]property3[]", new[] { "found" } },
|
||||
{ "property[]Value", new[] { "found" } },
|
||||
{ "[10]", new[] { "found" } },
|
||||
{ "[11]property", new[] { "found" } },
|
||||
{ "property4[10]", new[] { "found" } },
|
||||
{ "[12]property[][13]", new[] { "found" } },
|
||||
{ "[14][]property1[15]property2", new[] { "found" } },
|
||||
{ "prefix[11]property1", new[] { "found" } },
|
||||
{ "prefix[12][][property2]", new[] { "found" } },
|
||||
{ "prefix[property1][13]", new[] { "found" } },
|
||||
{ "prefix[14][][15]", new[] { "found" } },
|
||||
{ "[property5][]", new[] { "found" } },
|
||||
{ "[][property6]Value", new[] { "found" } },
|
||||
{ "prefix[property2]", new[] { "found" } },
|
||||
{ "prefix[][property]Value", new[] { "found" } },
|
||||
{ "[property7][property8]", new[] { "found" } },
|
||||
{ "[property9][][property10]Value", new[] { "found" } },
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void GetValueProvider_ReturnsNull_WhenContentTypeIsNotFormUrlEncoded()
|
||||
{
|
||||
// Arrange
|
||||
var context = CreateContext("some-content-type", formValues: null);
|
||||
var factory = new JQueryFormValueProviderFactory();
|
||||
|
||||
// Act
|
||||
var result = factory.GetValueProvider(context);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("application/x-www-form-urlencoded")]
|
||||
[InlineData("application/x-www-form-urlencoded;charset=utf-8")]
|
||||
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq")]
|
||||
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq; charset=utf-8")]
|
||||
public void GetValueProvider_ReturnsExpectedValueProviderInstanceWithCurrentCulture(string contentType)
|
||||
{
|
||||
// Arrange
|
||||
var context = CreateContext(contentType, formValues: null);
|
||||
var factory = new JQueryFormValueProviderFactory();
|
||||
|
||||
// Act
|
||||
var result = factory.GetValueProvider(context);
|
||||
|
||||
// Assert
|
||||
var valueProvider = Assert.IsType<JQueryFormValueProvider>(result);
|
||||
Assert.Equal(CultureInfo.CurrentCulture, valueProvider.Culture);
|
||||
}
|
||||
|
||||
public static TheoryData<string> SuccessDataSet
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<string>
|
||||
{
|
||||
string.Empty,
|
||||
"property1",
|
||||
"property2",
|
||||
"property3",
|
||||
"propertyValue",
|
||||
"[10]",
|
||||
"[11]property",
|
||||
"property4[10]",
|
||||
"[12]property[13]",
|
||||
"[14]property1[15]property2",
|
||||
"prefix.property1[13]",
|
||||
"prefix[14][15]",
|
||||
".property5",
|
||||
".property6Value",
|
||||
"prefix.property2",
|
||||
"prefix.propertyValue",
|
||||
".property7.property8",
|
||||
".property9.property10Value",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(SuccessDataSet))]
|
||||
public async Task GetValueProvider_ReturnsValueProvider_ContainingExpectedKeys(string key)
|
||||
{
|
||||
// Arrange
|
||||
var context = CreateContext("application/x-www-form-urlencoded", formValues: _backingStore);
|
||||
var factory = new JQueryFormValueProviderFactory();
|
||||
|
||||
// Act
|
||||
var valueProvider = factory.GetValueProvider(context);
|
||||
var result = await valueProvider.GetValueAsync(key);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(result.RawValue);
|
||||
var value = Assert.IsType<string>(result.RawValue);
|
||||
Assert.Equal("found", value);
|
||||
}
|
||||
|
||||
private static ValueProviderFactoryContext CreateContext(
|
||||
string contentType,
|
||||
IDictionary<string, string[]> formValues)
|
||||
{
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = contentType;
|
||||
if (formValues != null)
|
||||
{
|
||||
context.Request.Form = new FormCollection(formValues);
|
||||
}
|
||||
|
||||
return new ValueProviderFactoryContext(
|
||||
context,
|
||||
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class JQueryFormValueProviderTest : EnumerableValueProviderTest
|
||||
{
|
||||
protected override IEnumerableValueProvider GetEnumerableValueProvider(
|
||||
BindingSource bindingSource,
|
||||
IDictionary<string, string[]> values,
|
||||
CultureInfo culture)
|
||||
{
|
||||
return new JQueryFormValueProvider(bindingSource, values, culture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,283 +1,21 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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 System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class ReadableStringCollectionValueProviderTest
|
||||
public class ReadableStringCollectionValueProviderTest : EnumerableValueProviderTest
|
||||
{
|
||||
private static readonly IReadableStringCollection _backingStore = new ReadableStringCollection(
|
||||
new Dictionary<string, string[]>
|
||||
{
|
||||
{ "some", new[] { "someValue1", "someValue2" } },
|
||||
{ "null_value", null },
|
||||
{ "prefix.name", new[] { "someOtherValue" } },
|
||||
{ "prefix.null_value", null },
|
||||
{ "prefix.property1.property", null },
|
||||
{ "prefix.property2[index]", null },
|
||||
{ "prefix[index1]", null },
|
||||
{ "prefix[index1].property1", null },
|
||||
{ "prefix[index1].property2", null },
|
||||
{ "prefix[index2].property", null },
|
||||
{ "[index]", null },
|
||||
{ "[index].property", null },
|
||||
{ "[index][anotherIndex]", null },
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public async Task ContainsPrefixAsync_WithEmptyCollection_ReturnsFalseForEmptyPrefix()
|
||||
protected override IEnumerableValueProvider GetEnumerableValueProvider(
|
||||
BindingSource bindingSource,
|
||||
IDictionary<string, string[]> values,
|
||||
CultureInfo culture)
|
||||
{
|
||||
// Arrange
|
||||
var backingStore = new ReadableStringCollection(new Dictionary<string, string[]>());
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, backingStore, null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.ContainsPrefixAsync(string.Empty);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForEmptyPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.ContainsPrefixAsync(string.Empty);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForKnownPrefixes()
|
||||
{
|
||||
// Arrange
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
// Act & Assert
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("some"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("prefix"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("prefix.name"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("[index]"));
|
||||
Assert.True(await valueProvider.ContainsPrefixAsync("prefix[index1]"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsFalseForUnknownPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.ContainsPrefixAsync("biff");
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetKeysFromPrefixAsync_EmptyPrefix_ReturnsAllPrefixes()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new Dictionary<string, string>
|
||||
{
|
||||
{ "index", "[index]" },
|
||||
{ "null_value", "null_value" },
|
||||
{ "prefix", "prefix" },
|
||||
{ "some", "some" },
|
||||
};
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, culture: null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetKeysFromPrefixAsync(string.Empty);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, result.OrderBy(kvp => kvp.Key));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetKeysFromPrefixAsync_UnknownPrefix_ReturnsEmptyDictionary()
|
||||
{
|
||||
// Arrange
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetKeysFromPrefixAsync("abc");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetKeysFromPrefixAsync_KnownPrefix_ReturnsMatchingItems()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new Dictionary<string, string>
|
||||
{
|
||||
{ "name", "prefix.name" },
|
||||
{ "null_value", "prefix.null_value" },
|
||||
{ "property1", "prefix.property1" },
|
||||
{ "property2", "prefix.property2" },
|
||||
{ "index1", "prefix[index1]" },
|
||||
{ "index2", "prefix[index2]" },
|
||||
};
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetKeysFromPrefixAsync("prefix");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetKeysFromPrefixAsync_IndexPrefix_ReturnsMatchingItems()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new Dictionary<string, string>
|
||||
{
|
||||
{ "property", "[index].property" },
|
||||
{ "anotherIndex", "[index][anotherIndex]" }
|
||||
};
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetKeysFromPrefixAsync("[index]");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetValueAsync_SingleValue()
|
||||
{
|
||||
// Arrange
|
||||
var culture = new CultureInfo("fr-FR");
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, culture);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetValueAsync("prefix.name");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("someOtherValue", result.RawValue);
|
||||
Assert.Equal("someOtherValue", result.AttemptedValue);
|
||||
Assert.Equal(culture, result.Culture);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetValueAsync_MultiValue()
|
||||
{
|
||||
// Arrange
|
||||
var culture = new CultureInfo("fr-FR");
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, culture);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetValueAsync("some");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(new[] { "someValue1", "someValue2" }, (IList<string>)result.RawValue);
|
||||
Assert.Equal("someValue1,someValue2", result.AttemptedValue);
|
||||
Assert.Equal(culture, result.Culture);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("null_value")]
|
||||
[InlineData("prefix.null_value")]
|
||||
public async Task GetValue_NullValue(string key)
|
||||
{
|
||||
// Arrange
|
||||
var culture = new CultureInfo("fr-FR");
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, culture);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetValueAsync(key);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetValueAsync_NullMultipleValue()
|
||||
{
|
||||
// Arrange
|
||||
var backingStore = new ReadableStringCollection(
|
||||
new Dictionary<string, string[]>
|
||||
{
|
||||
{ "key", new string[] { null, null, "value" } }
|
||||
});
|
||||
var culture = new CultureInfo("fr-FR");
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, backingStore, culture);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetValueAsync("key");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { null, null, "value" }, result.RawValue as IEnumerable<string>);
|
||||
Assert.Equal(",,value", result.AttemptedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetValueAsync_ReturnsNullIfKeyNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var valueProvider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
// Act
|
||||
var result = await valueProvider.GetValueAsync("prefix");
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FilterInclude()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
var bindingSource = new BindingSource(
|
||||
BindingSource.Query.Id,
|
||||
displayName: null,
|
||||
isGreedy: true,
|
||||
isFromRequest: true);
|
||||
|
||||
// Act
|
||||
var result = provider.Filter(bindingSource);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Same(result, provider);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FilterExclude()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new ReadableStringCollectionValueProvider(BindingSource.Query, _backingStore, null);
|
||||
|
||||
var bindingSource = new BindingSource(
|
||||
"Test",
|
||||
displayName: null,
|
||||
isGreedy: true,
|
||||
isFromRequest: true);
|
||||
|
||||
// Act
|
||||
var result = provider.Filter(bindingSource);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
var backingStore = new ReadableStringCollection(values);
|
||||
return new ReadableStringCollectionValueProvider(bindingSource, backingStore, culture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,11 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -12,16 +15,6 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
{
|
||||
public class TypeConverterModelBinderIntegrationTest
|
||||
{
|
||||
private class Person
|
||||
{
|
||||
public Address Address { get; set; }
|
||||
}
|
||||
|
||||
private class Address
|
||||
{
|
||||
public int Zip { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BindProperty_WithData_WithPrefix_GetsBound()
|
||||
{
|
||||
|
|
@ -266,9 +259,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
};
|
||||
|
||||
// No Data.
|
||||
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
|
||||
{
|
||||
});
|
||||
var operationContext = ModelBindingTestHelper.GetOperationBindingContext();
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
||||
|
|
@ -284,5 +275,90 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
Assert.True(modelState.IsValid);
|
||||
Assert.Empty(modelState.Keys);
|
||||
}
|
||||
|
||||
public static TheoryData<IDictionary<string, string[]>> PersonStoreData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<IDictionary<string, string[]>>
|
||||
{
|
||||
new Dictionary<string, string[]>
|
||||
{
|
||||
{ "name", new[] { "Fred" } },
|
||||
{ "address.zip", new[] { "98052" } },
|
||||
{ "address.lines", new[] { "line 1", "line 2" } },
|
||||
},
|
||||
new Dictionary<string, string[]>
|
||||
{
|
||||
{ "address.lines[]", new[] { "line 1", "line 2" } },
|
||||
{ "address[].zip", new[] { "98052" } },
|
||||
{ "name[]", new[] { "Fred" } },
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(PersonStoreData))]
|
||||
public async Task BindParameter_FromFormData_BindsCorrectly(IDictionary<string, string[]> personStore)
|
||||
{
|
||||
// Arrange
|
||||
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
|
||||
var parameter = new ParameterDescriptor()
|
||||
{
|
||||
Name = "Parameter1",
|
||||
BindingInfo = new BindingInfo(),
|
||||
ParameterType = typeof(Person),
|
||||
};
|
||||
|
||||
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
|
||||
{
|
||||
request.Form = new FormCollection(personStore);
|
||||
});
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
||||
// Act
|
||||
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
|
||||
|
||||
// Assert
|
||||
// ModelBindingResult
|
||||
Assert.NotNull(modelBindingResult);
|
||||
Assert.True(modelBindingResult.IsModelSet);
|
||||
|
||||
// Model
|
||||
var boundPerson = Assert.IsType<Person>(modelBindingResult.Model);
|
||||
Assert.NotNull(boundPerson);
|
||||
Assert.Equal("Fred", boundPerson.Name);
|
||||
Assert.NotNull(boundPerson.Address);
|
||||
Assert.Equal(new[] { "line 1", "line 2" }, boundPerson.Address.Lines);
|
||||
Assert.Equal(98052, boundPerson.Address.Zip);
|
||||
|
||||
// ModelState
|
||||
Assert.True(modelState.IsValid);
|
||||
|
||||
Assert.Equal(new[] { "Address.Lines", "Address.Zip", "Name" }, modelState.Keys.ToArray());
|
||||
var entry = modelState["Address.Lines"];
|
||||
Assert.NotNull(entry);
|
||||
Assert.Empty(entry.Errors);
|
||||
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
||||
var result = entry.Value;
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("line 1,line 2", result.AttemptedValue);
|
||||
Assert.Equal(new[] { "line 1", "line 2" }, result.RawValue);
|
||||
}
|
||||
|
||||
private class Person
|
||||
{
|
||||
public Address Address { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
private class Address
|
||||
{
|
||||
public string[] Lines { get; set; }
|
||||
|
||||
public int Zip { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -62,10 +62,11 @@ namespace Microsoft.AspNet.Mvc
|
|||
|
||||
// Assert
|
||||
var valueProviders = options.ValueProviderFactories;
|
||||
Assert.Equal(3, valueProviders.Count);
|
||||
Assert.Equal(4, valueProviders.Count);
|
||||
Assert.IsType<RouteValueValueProviderFactory>(valueProviders[0]);
|
||||
Assert.IsType<QueryStringValueProviderFactory>(valueProviders[1]);
|
||||
Assert.IsType<FormValueProviderFactory>(valueProviders[2]);
|
||||
Assert.IsType<JQueryFormValueProviderFactory>(valueProviders[3]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -174,7 +175,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
Assert.Equal(xObjectFilter.ExcludedTypeName, typeof(XObject).FullName);
|
||||
|
||||
Assert.IsType(typeof(DefaultTypeNameBasedExcludeFilter), options.ValidationExcludeFilters[i]);
|
||||
var xmlNodeFilter =
|
||||
var xmlNodeFilter =
|
||||
Assert.IsType<DefaultTypeNameBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
|
||||
Assert.Equal(xmlNodeFilter.ExcludedTypeName, "System.Xml.XmlNode");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue