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.
|
/// Represents a <see cref="IValueProvider"/> whose values come from a collection of <see cref="IValueProvider"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CompositeValueProvider :
|
public class CompositeValueProvider :
|
||||||
Collection<IValueProvider>,
|
Collection<IValueProvider>,
|
||||||
IEnumerableValueProvider,
|
IEnumerableValueProvider,
|
||||||
IBindingSourceValueProvider
|
IBindingSourceValueProvider
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
/// created.
|
/// created.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static CompositeValueProvider Create(
|
public static CompositeValueProvider Create(
|
||||||
[NotNull] IEnumerable<IValueProviderFactory> factories,
|
[NotNull] IEnumerable<IValueProviderFactory> factories,
|
||||||
[NotNull] ValueProviderFactoryContext context)
|
[NotNull] ValueProviderFactoryContext context)
|
||||||
{
|
{
|
||||||
var composite = new CompositeValueProvider();
|
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);
|
return new CompositeValueProvider(filteredValueProviders);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ using Microsoft.Framework.Internal;
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Value providers are by-default included. If a model does not specify a <see cref="BindingSource"/>
|
/// 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 RouteValueValueProviderFactory());
|
||||||
options.ValueProviderFactories.Add(new QueryStringValueProviderFactory());
|
options.ValueProviderFactories.Add(new QueryStringValueProviderFactory());
|
||||||
options.ValueProviderFactories.Add(new FormValueProviderFactory());
|
options.ValueProviderFactories.Add(new FormValueProviderFactory());
|
||||||
|
options.ValueProviderFactories.Add(new JQueryFormValueProviderFactory());
|
||||||
|
|
||||||
// Set up metadata providers
|
// Set up metadata providers
|
||||||
options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider());
|
options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider());
|
||||||
|
|
|
||||||
|
|
@ -1034,6 +1034,22 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("Common_PropertyNotFound"), p0, p1);
|
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>
|
/// <summary>
|
||||||
/// A value is required.
|
/// A value is required.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
|
||||||
Version 2.0
|
Version 2.0
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
The primary goals of this format is to allow a simple XML format
|
||||||
that is mostly human readable. The generation and parsing of the
|
that is mostly human readable. The generation and parsing of the
|
||||||
various data types are done through the TypeConverter classes
|
various data types are done through the TypeConverter classes
|
||||||
associated with the data types.
|
associated with the data types.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
... ado.net/XML headers & schema ...
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
<resheader name="version">2.0</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>
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
<comment>This is a comment</comment>
|
<comment>This is a comment</comment>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
There are any number of "resheader" rows that contain simple
|
||||||
name/value pairs.
|
name/value pairs.
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
Each data row contains a name, and value. The row also contains a
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
text/value conversion through the TypeConverter architecture.
|
text/value conversion through the TypeConverter architecture.
|
||||||
Classes that don't support this are serialized and stored with the
|
Classes that don't support this are serialized and stored with the
|
||||||
mimetype set.
|
mimetype set.
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
The mimetype is used for serialized objects, and tells the
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
read any of the formats listed below.
|
read any of the formats listed below.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
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
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
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
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
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
|
: using a System.ComponentModel.TypeConverter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
|
|
@ -318,6 +318,9 @@
|
||||||
<data name="Common_PropertyNotFound" xml:space="preserve">
|
<data name="Common_PropertyNotFound" xml:space="preserve">
|
||||||
<value>The property {0}.{1} could not be found.</value>
|
<value>The property {0}.{1} could not be found.</value>
|
||||||
</data>
|
</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">
|
<data name="KeyValuePair_BothKeyAndValueMustBePresent" xml:space="preserve">
|
||||||
<value>A value is required.</value>
|
<value>A value is required.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,41 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
#if DNX451
|
#if DNX451
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
#endif
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
#if DNX451
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
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]
|
[Fact]
|
||||||
public async Task GetKeysFromPrefixAsync_ReturnsResultFromFirstValueProviderThatReturnsValues()
|
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.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
#if DNX451
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Threading;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
using Microsoft.AspNet.Http.Features.Internal;
|
|
||||||
using Moq;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
{
|
{
|
||||||
public class FormValueProviderFactoryTests
|
public class FormValueProviderFactoryTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetValueProvider_ReturnsNull_WhenContentTypeIsNotFormUrlEncoded()
|
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("application/x-www-form-urlencoded;charset=utf-8")]
|
||||||
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq")]
|
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq")]
|
||||||
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq; charset=utf-8")]
|
[InlineData("multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq; charset=utf-8")]
|
||||||
public void GetValueProvider_ReturnsValueProviderInstanceWithInvariantCulture(string contentType)
|
public void GetValueProvider_ReturnsValueProviderInstanceWithCurrentCulture(string contentType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var context = CreateContext(contentType);
|
var context = CreateContext(contentType);
|
||||||
|
|
@ -51,19 +46,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
|
|
||||||
private static ValueProviderFactoryContext CreateContext(string contentType)
|
private static ValueProviderFactoryContext CreateContext(string contentType)
|
||||||
{
|
{
|
||||||
var collection = Mock.Of<IFormCollection>();
|
var context = new DefaultHttpContext();
|
||||||
var request = new Mock<HttpRequest>();
|
context.Request.ContentType = contentType;
|
||||||
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);
|
|
||||||
|
|
||||||
return new ValueProviderFactoryContext(
|
return new ValueProviderFactoryContext(
|
||||||
context.Object,
|
context,
|
||||||
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase));
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
using Microsoft.AspNet.Http.Internal;
|
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(
|
protected override IEnumerableValueProvider GetEnumerableValueProvider(
|
||||||
new Dictionary<string, string[]>
|
BindingSource bindingSource,
|
||||||
{
|
IDictionary<string, string[]> values,
|
||||||
{ "some", new[] { "someValue1", "someValue2" } },
|
CultureInfo culture)
|
||||||
{ "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 ReadableStringCollection(values);
|
||||||
var backingStore = new ReadableStringCollection(new Dictionary<string, string[]>());
|
return new ReadableStringCollectionValueProvider(bindingSource, backingStore, culture);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,11 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Http.Internal;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -12,16 +15,6 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
{
|
{
|
||||||
public class TypeConverterModelBinderIntegrationTest
|
public class TypeConverterModelBinderIntegrationTest
|
||||||
{
|
{
|
||||||
private class Person
|
|
||||||
{
|
|
||||||
public Address Address { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Address
|
|
||||||
{
|
|
||||||
public int Zip { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task BindProperty_WithData_WithPrefix_GetsBound()
|
public async Task BindProperty_WithData_WithPrefix_GetsBound()
|
||||||
{
|
{
|
||||||
|
|
@ -266,9 +259,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
};
|
};
|
||||||
|
|
||||||
// No Data.
|
// No Data.
|
||||||
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
|
var operationContext = ModelBindingTestHelper.GetOperationBindingContext();
|
||||||
{
|
|
||||||
});
|
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
var modelState = new ModelStateDictionary();
|
||||||
|
|
||||||
|
|
@ -284,5 +275,90 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
Assert.True(modelState.IsValid);
|
Assert.True(modelState.IsValid);
|
||||||
Assert.Empty(modelState.Keys);
|
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
|
// Assert
|
||||||
var valueProviders = options.ValueProviderFactories;
|
var valueProviders = options.ValueProviderFactories;
|
||||||
Assert.Equal(3, valueProviders.Count);
|
Assert.Equal(4, valueProviders.Count);
|
||||||
Assert.IsType<RouteValueValueProviderFactory>(valueProviders[0]);
|
Assert.IsType<RouteValueValueProviderFactory>(valueProviders[0]);
|
||||||
Assert.IsType<QueryStringValueProviderFactory>(valueProviders[1]);
|
Assert.IsType<QueryStringValueProviderFactory>(valueProviders[1]);
|
||||||
Assert.IsType<FormValueProviderFactory>(valueProviders[2]);
|
Assert.IsType<FormValueProviderFactory>(valueProviders[2]);
|
||||||
|
Assert.IsType<JQueryFormValueProviderFactory>(valueProviders[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -174,7 +175,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
Assert.Equal(xObjectFilter.ExcludedTypeName, typeof(XObject).FullName);
|
Assert.Equal(xObjectFilter.ExcludedTypeName, typeof(XObject).FullName);
|
||||||
|
|
||||||
Assert.IsType(typeof(DefaultTypeNameBasedExcludeFilter), options.ValidationExcludeFilters[i]);
|
Assert.IsType(typeof(DefaultTypeNameBasedExcludeFilter), options.ValidationExcludeFilters[i]);
|
||||||
var xmlNodeFilter =
|
var xmlNodeFilter =
|
||||||
Assert.IsType<DefaultTypeNameBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
|
Assert.IsType<DefaultTypeNameBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
|
||||||
Assert.Equal(xmlNodeFilter.ExcludedTypeName, "System.Xml.XmlNode");
|
Assert.Equal(xmlNodeFilter.ExcludedTypeName, "System.Xml.XmlNode");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue