[Fixes #2192] Remove enumerable types from excluded type in validation

This commit is contained in:
Ajay Bhargav Baaskaran 2015-03-23 11:26:23 -07:00
parent 784021cf85
commit 823e9f1516
3 changed files with 137 additions and 30 deletions

View File

@ -19,22 +19,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{ {
Type[] actualTypes; Type[] actualTypes;
var enumerable = type.ExtractGenericInterface(typeof(IEnumerable<>)); if (type.IsGenericType() &&
if (enumerable == null) type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
{ {
actualTypes = new Type[] { type }; actualTypes = type.GenericTypeArguments;
} }
else else
{ {
actualTypes = enumerable.GenericTypeArguments; actualTypes = new Type[] { type };
// The following special case is for IEnumerable<KeyValuePair<K,V>>,
// supertype of IDictionary<K,V>, and IReadOnlyDictionary<K,V>.
if (actualTypes.Length == 1
&& actualTypes[0].IsGenericType()
&& actualTypes[0].GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
{
actualTypes = actualTypes[0].GenericTypeArguments;
}
} }
foreach (var actualType in actualTypes) foreach (var actualType in actualTypes)

View File

@ -7,11 +7,11 @@ using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{ {
public class SimpleTypeExcluceFilterTest public class SimpleTypeExcludeFilterTest
{ {
[Theory] [Theory]
[MemberData(nameof(ExcludedTypes))] [MemberData(nameof(ExcludedTypes))]
public void SimpleTypeExcluceFilter_ExcludedTypes(Type type) public void SimpleTypeExcludeFilter_ExcludedTypes(Type type)
{ {
// Arrange // Arrange
var filter = new SimpleTypesExcludeFilter(); var filter = new SimpleTypesExcludeFilter();
@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
[Theory] [Theory]
[MemberData(nameof(IncludedTypes))] [MemberData(nameof(IncludedTypes))]
public void SimpleTypeExcluceFilter_IncludedTypes(Type type) public void SimpleTypeExcludeFilter_IncludedTypes(Type type)
{ {
// Arrange // Arrange
var filter = new SimpleTypesExcludeFilter(); var filter = new SimpleTypesExcludeFilter();
@ -43,24 +43,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
return new TheoryData<Type>() return new TheoryData<Type>()
{ {
// Simple types // Simple types
typeof(int[]),
typeof(int), typeof(int),
typeof(List<decimal>), typeof(DateTime),
typeof(SortedSet<int>),
// Nullable types // Nullable types
typeof(ICollection<string>), typeof(int?),
typeof(int?[]),
typeof(SortedSet<int?>),
typeof(HashSet<Uri>),
typeof(HashSet<string>),
// Value types
typeof(IList<DateTime>),
// KeyValue types // KeyValue types
typeof(Dictionary<int, string>), typeof(KeyValuePair<string, string>)
typeof(IReadOnlyDictionary<int?, char>)
}; };
} }
} }
@ -71,12 +61,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{ {
return new TheoryData<Type>() return new TheoryData<Type>()
{ {
// Enumerable types
typeof(int[]),
typeof(List<decimal>),
typeof(SortedSet<int>),
typeof(ICollection<string>),
typeof(int?[]),
typeof(SortedSet<int?>),
typeof(HashSet<Uri>),
typeof(HashSet<string>),
typeof(IList<DateTime>),
typeof(Dictionary<int, string>),
typeof(IReadOnlyDictionary<int?, char>),
// Complex types
typeof(TestType), typeof(TestType),
typeof(TestType[]), typeof(TestType[]),
typeof(SortedSet<TestType>), typeof(SortedSet<TestType>),
typeof(Dictionary<int, TestType>), typeof(Dictionary<int, TestType>),
typeof(Dictionary<TestType, int>), typeof(Dictionary<TestType, int>),
typeof(Dictionary<TestType, TestType>) typeof(Dictionary<TestType, TestType>),
typeof(KeyValuePair<string, TestType>)
}; };
} }
} }

View File

@ -472,19 +472,129 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
validator.Validate(validationContext); validator.Validate(validationContext);
// Assert // Assert
Assert.True(validationContext.ModelState.IsValid);
var modelState = validationContext.ModelState["serviceProvider.TestService"]; var modelState = validationContext.ModelState["serviceProvider.TestService"];
Assert.Empty(modelState.Errors); Assert.Empty(modelState.Errors);
Assert.Equal(modelState.ValidationState, ModelValidationState.Skipped); Assert.Equal(modelState.ValidationState, ModelValidationState.Skipped);
} }
[Theory]
[InlineData(new[] { "Foo", "Bar", "Baz" }, typeof(string[]))]
[InlineData(new[] { 1, 2, 3 }, typeof(int[]))]
[InlineData(new[] { "Foo", "Bar", "Baz" }, typeof(IList<string>))]
[InlineData(new[] { "Foo", "Bar", "Baz" }, typeof(HashSet<string>))]
[InlineData(new[] { "1/1/14", "2/2/14", "3/3/14" }, typeof(ICollection<DateTime>))]
[InlineData(new[] { "Foo", "Bar", "Baz" }, typeof(HashSet<Uri>))]
public void EnumerableType_ValidationSuccessful(object model, Type type)
{
// Arrange
var modelStateDictionary = new ModelStateDictionary();
modelStateDictionary.Add("items[0]", new ModelState());
modelStateDictionary.Add("items[1]", new ModelState());
modelStateDictionary.Add("items[2]", new ModelState());
var testValidationContext = GetModelValidationContext(
model,
type,
"items",
excludedTypes: null,
modelStateDictionary: modelStateDictionary);
var excludeTypeFilters = new List<IExcludeTypeValidationFilter>();
excludeTypeFilters.Add(new SimpleTypesExcludeFilter());
var mockValidationExcludeFiltersProvider = new Mock<IValidationExcludeFiltersProvider>();
mockValidationExcludeFiltersProvider
.SetupGet(o => o.ExcludeFilters)
.Returns(excludeTypeFilters);
testValidationContext.ExcludeFiltersProvider = mockValidationExcludeFiltersProvider.Object;
var validationContext = testValidationContext.ModelValidationContext;
var validator = new DefaultObjectValidator(
testValidationContext.ExcludeFiltersProvider,
testValidationContext.ModelMetadataProvider);
// Act
validator.Validate(validationContext);
// Assert
Assert.True(validationContext.ModelState.IsValid);
var modelState = validationContext.ModelState["items"];
Assert.Equal(modelState.ValidationState, ModelValidationState.Valid);
}
[Fact]
public void DictionaryType_ValidationSuccessful()
{
// Arrange
var modelStateDictionary = new ModelStateDictionary();
modelStateDictionary.Add("items[0].Key", new ModelState());
modelStateDictionary.Add("items[0].Value", new ModelState());
modelStateDictionary.Add("items[1].Key", new ModelState());
modelStateDictionary.Add("items[1].Value", new ModelState());
var model = new Dictionary<string, string>()
{
{ "FooKey", "FooValue" },
{ "BarKey", "BarValue" }
};
var testValidationContext = GetModelValidationContext(
model,
typeof(Dictionary<string, string>),
"items",
excludedTypes: null,
modelStateDictionary: modelStateDictionary);
var excludeTypeFilters = new List<IExcludeTypeValidationFilter>();
excludeTypeFilters.Add(new SimpleTypesExcludeFilter());
var mockValidationExcludeFiltersProvider = new Mock<IValidationExcludeFiltersProvider>();
mockValidationExcludeFiltersProvider
.SetupGet(o => o.ExcludeFilters)
.Returns(excludeTypeFilters);
testValidationContext.ExcludeFiltersProvider = mockValidationExcludeFiltersProvider.Object;
var validationContext = testValidationContext.ModelValidationContext;
var validator = new DefaultObjectValidator(
testValidationContext.ExcludeFiltersProvider,
testValidationContext.ModelMetadataProvider);
// Act
validator.Validate(validationContext);
// Assert
Assert.True(validationContext.ModelState.IsValid);
var modelState = validationContext.ModelState["items"];
Assert.Equal(modelState.ValidationState, ModelValidationState.Valid);
modelState = validationContext.ModelState["items[0].Key"];
Assert.Equal(modelState.ValidationState, ModelValidationState.Skipped);
modelState = validationContext.ModelState["items[0].Value"];
Assert.Equal(modelState.ValidationState, ModelValidationState.Skipped);
modelState = validationContext.ModelState["items[1].Key"];
Assert.Equal(modelState.ValidationState, ModelValidationState.Skipped);
modelState = validationContext.ModelState["items[1].Value"];
Assert.Equal(modelState.ValidationState, ModelValidationState.Skipped);
}
private TestModelValidationContext GetModelValidationContext( private TestModelValidationContext GetModelValidationContext(
object model, object model,
Type type, Type type,
string key = "", string key = "",
List<Type> excludedTypes = null) List<Type> excludedTypes = null)
{ {
var modelStateDictionary = new ModelStateDictionary(); return GetModelValidationContext(model, type, key, excludedTypes, new ModelStateDictionary());
}
private TestModelValidationContext GetModelValidationContext(
object model,
Type type,
string key,
List<Type> excludedTypes,
ModelStateDictionary modelStateDictionary)
{
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var excludedValidationTypesPredicate = new List<IExcludeTypeValidationFilter>(); var excludedValidationTypesPredicate = new List<IExcludeTypeValidationFilter>();