Remove IExcludeTypeFilter

- Removes IExcludeTypeFilter
- Replaced with a property 'ValidateChildren' on ModelMetadata
- Teach ValidationVisitor to respect 'ValidateChildren' for enumerable
  types.
This commit is contained in:
Ryan Nowak 2015-12-08 18:22:10 -08:00
parent 774ee05508
commit 96de1dbe4b
37 changed files with 514 additions and 534 deletions

View File

@ -289,6 +289,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </summary>
public abstract string TemplateHint { get; }
/// <summary>
/// Gets a value that indicates whether properties or elements of the model should be validated.
/// </summary>
public abstract bool ValidateChildren { get; }
/// <summary>
/// Gets a collection of metadata items for validators.
/// </summary>

View File

@ -134,12 +134,7 @@ namespace Microsoft.Extensions.DependencyInjection
var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Value;
return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders);
}));
services.TryAdd(ServiceDescriptor.Singleton<IObjectModelValidator>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Value;
var modelMetadataProvider = serviceProvider.GetRequiredService<IModelMetadataProvider>();
return new DefaultObjectValidator(options.ValidationExcludeFilters, modelMetadataProvider);
}));
services.TryAddSingleton<IObjectModelValidator, DefaultObjectValidator>();
//
// Random Infrastructure

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.Formatters;
@ -63,13 +64,11 @@ namespace Microsoft.AspNet.Mvc.Internal
options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider());
// Add types to be excluded from Validation
options.ValidationExcludeFilters.Add(new SimpleTypesExcludeFilter());
options.ValidationExcludeFilters.Add(typeof(Type));
// Any 'known' types that we bind should be marked as excluded from validation.
options.ValidationExcludeFilters.Add(typeof(System.Threading.CancellationToken));
options.ValidationExcludeFilters.Add(typeof(IFormFile));
options.ValidationExcludeFilters.Add(typeof(IFormCollection));
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(Type)));
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(Uri)));
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(CancellationToken)));
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(IFormFile)));
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(IFormCollection)));
}
}
}

View File

@ -29,6 +29,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
private bool? _isReadOnly;
private bool? _isRequired;
private ModelPropertyCollection _properties;
private bool? _validateChildren;
private ReadOnlyCollection<object> _validatorMetadata;
/// <summary>
@ -491,6 +492,31 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
}
}
/// <inheritdoc />
public override bool ValidateChildren
{
get
{
if (!_validateChildren.HasValue)
{
if (ValidationMetadata.ValidateChildren.HasValue)
{
_validateChildren = ValidationMetadata.ValidateChildren.Value;
}
else if (IsComplexType || IsEnumerableType)
{
_validateChildren = true;
}
else
{
_validateChildren = false;
}
}
return _validateChildren.Value;
}
}
/// <inheritdoc />
public override IReadOnlyList<object> ValidatorMetadata
{

View File

@ -18,6 +18,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
/// </summary>
public bool? IsRequired { get; set; }
/// <summary>
/// Gets or sets a value that indicates whether children of the model should be validated. If <c>null</c>
/// then <see cref="ModelMetadata.ValidateChildren"/> will be <c>true</c> if either of
/// <see cref="ModelMetadata.IsComplexType"/> or <see cref="ModelMetadata.IsEnumerableType"/> is <c>true</c>;
/// <c>false</c> otherwise.
/// </summary>
public bool? ValidateChildren { get; set; }
/// <summary>
/// Gets a list of metadata items for validators.
/// </summary>

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
@ -11,31 +10,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
/// </summary>
public class DefaultObjectValidator : IObjectModelValidator
{
private readonly IList<IExcludeTypeValidationFilter> _excludeFilters;
private readonly IModelMetadataProvider _modelMetadataProvider;
/// <summary>
/// Initializes a new instance of <see cref="DefaultObjectValidator"/>.
/// </summary>
/// <param name="excludeFilters"><see cref="IExcludeTypeValidationFilter"/>s that determine
/// types to exclude from validation.</param>
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
public DefaultObjectValidator(
IList<IExcludeTypeValidationFilter> excludeFilters,
IModelMetadataProvider modelMetadataProvider)
{
if (excludeFilters == null)
{
throw new ArgumentNullException(nameof(excludeFilters));
}
if (modelMetadataProvider == null)
{
throw new ArgumentNullException(nameof(modelMetadataProvider));
}
_modelMetadataProvider = modelMetadataProvider;
_excludeFilters = excludeFilters;
}
/// <inheritdoc />
@ -59,7 +48,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
var visitor = new ValidationVisitor(
actionContext,
validatorProvider,
_excludeFilters,
validationState);
var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType());

View File

@ -1,45 +0,0 @@
// 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.Reflection;
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Provides an implementation of <see cref="IExcludeTypeValidationFilter"/> which can filter
/// based on a type.
/// </summary>
public class DefaultTypeBasedExcludeFilter : IExcludeTypeValidationFilter
{
/// <summary>
/// Creates a new instance of <see cref="DefaultTypeBasedExcludeFilter"/>.
/// </summary>
/// <param name="type">The type which needs to be excluded.</param>
public DefaultTypeBasedExcludeFilter(Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
ExcludedType = type;
}
/// <summary>
/// Gets the type which is excluded from validation.
/// </summary>
public Type ExcludedType { get; }
/// <inheritdoc />
public bool IsTypeExcluded(Type propertyType)
{
if (propertyType == null)
{
throw new ArgumentNullException(nameof(propertyType));
}
return ExcludedType.GetTypeInfo().IsAssignableFrom(propertyType.GetTypeInfo());
}
}
}

View File

@ -1,60 +0,0 @@
// 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.Reflection;
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Provides an implementation of <see cref="IExcludeTypeValidationFilter"/> which can filter
/// based on a type full name.
/// </summary>
public class DefaultTypeNameBasedExcludeFilter : IExcludeTypeValidationFilter
{
/// <summary>
/// Creates a new instance of <see cref="DefaultTypeNameBasedExcludeFilter"/>
/// </summary>
/// <param name="typeFullName">Fully qualified name of the type which needs to be excluded.</param>
public DefaultTypeNameBasedExcludeFilter(string typeFullName)
{
if (typeFullName == null)
{
throw new ArgumentNullException(nameof(typeFullName));
}
ExcludedTypeName = typeFullName;
}
/// <summary>
/// Gets the type full name which is excluded from validation.
/// </summary>
public string ExcludedTypeName { get; }
/// <inheritdoc />
public bool IsTypeExcluded(Type propertyType)
{
if (propertyType == null)
{
throw new ArgumentNullException(nameof(propertyType));
}
return CheckIfTypeNameMatches(propertyType);
}
private bool CheckIfTypeNameMatches(Type type)
{
if (type == null)
{
return false;
}
if (string.Equals(type.FullName, ExcludedTypeName, StringComparison.Ordinal))
{
return true;
}
return CheckIfTypeNameMatches(type.GetTypeInfo().BaseType);
}
}
}

View File

@ -1,43 +0,0 @@
// 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.ObjectModel;
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class ExcludeTypeValidationFilterCollection : Collection<IExcludeTypeValidationFilter>
{
/// <summary>
/// Adds an <see cref="IExcludeTypeValidationFilter"/> that excludes the properties of
/// the <see cref="Type"/> specified and its derived types from validaton.
/// </summary>
/// <param name="type"><see cref="Type"/> which should be excluded from validation.</param>
public void Add(Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var typeBasedExcludeFilter = new DefaultTypeBasedExcludeFilter(type);
Add(typeBasedExcludeFilter);
}
/// <summary>
/// Adds an <see cref="IExcludeTypeValidationFilter"/> that excludes the properties of
/// the <see cref="Type"/> specified and its derived types from validaton.
/// </summary>
/// <param name="typeFullName">Full name of the type which should be excluded from validation.</param>
public void Add(string typeFullName)
{
if (typeFullName == null)
{
throw new ArgumentNullException(nameof(typeFullName));
}
var filter = new DefaultTypeNameBasedExcludeFilter(typeFullName);
Add(filter);
}
}
}

View File

@ -1,20 +0,0 @@
// 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;
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Provides an interface which is used to determine if <see cref="Type"/>s are excluded from model validation.
/// </summary>
public interface IExcludeTypeValidationFilter
{
/// <summary>
/// Determines if the given type is excluded from validation.
/// </summary>
/// <param name="type">The <see cref="Type"/> for which the check is to be performed.</param>
/// <returns>True if the type is to be excluded. False otherwise.</returns>
bool IsTypeExcluded(Type type);
}
}

View File

@ -1,64 +0,0 @@
// 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.Reflection;
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// Identifies the simple types that the default model binding validation will exclude.
/// </summary>
public class SimpleTypesExcludeFilter : IExcludeTypeValidationFilter
{
/// <summary>
/// Returns true if the given type will be excluded from the default model validation.
/// </summary>
public bool IsTypeExcluded(Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
Type[] actualTypes;
if (type.GetTypeInfo().IsGenericType &&
type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
{
actualTypes = type.GenericTypeArguments;
}
else
{
actualTypes = new Type[] { type };
}
foreach (var actualType in actualTypes)
{
var underlyingType = Nullable.GetUnderlyingType(actualType) ?? actualType;
if (!IsSimpleType(underlyingType))
{
return false;
}
}
return true;
}
/// <summary>
/// Returns true if the given type is the underlying types that <see cref="IsTypeExcluded"/> will exclude.
/// </summary>
protected virtual bool IsSimpleType(Type type)
{
return type.GetTypeInfo().IsPrimitive ||
type.Equals(typeof(decimal)) ||
type.Equals(typeof(string)) ||
type.Equals(typeof(DateTime)) ||
type.Equals(typeof(Guid)) ||
type.Equals(typeof(DateTimeOffset)) ||
type.Equals(typeof(TimeSpan)) ||
type.Equals(typeof(Uri));
}
}
}

View File

@ -0,0 +1,109 @@
// 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.Diagnostics;
using System.Reflection;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
/// <summary>
/// An <see cref="IValidationMetadataProvider"/> which configures <see cref="ModelMetadata.ValidateChildren"/> to
/// <c>false</c> for matching types.
/// </summary>
public class ValidationExcludeFilter : IValidationMetadataProvider
{
/// <summary>
/// Creates a new <see cref="ValidationExcludeFilter"/> for the given <paramref name="type"/>.
/// </summary>
/// <param name="type">
/// The <see cref="Type"/>. This <see cref="Type"/> and all assignable values will have
/// <see cref="ModelMetadata.ValidateChildren"/> set to <c>false</c>.
/// </param>
public ValidationExcludeFilter(Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
Type = type;
}
/// <summary>
/// Creates a new <see cref="ValidationExcludeFilter"/> for the given <paramref name="fullTypeName"/>.
/// </summary>
/// <param name="fullTypeName">
/// The type full name. This type and all of its subclasses will have
/// <see cref="ModelMetadata.ValidateChildren"/> set to <c>false</c>.
/// </param>
public ValidationExcludeFilter(string fullTypeName)
{
if (fullTypeName == null)
{
throw new ArgumentNullException(nameof(fullTypeName));
}
FullTypeName = fullTypeName;
}
/// <summary>
/// Gets the <see cref="Type"/> for which to suppress validation of children.
/// </summary>
public Type Type { get; }
/// <summary>
/// Gets the full name of a type for which to suppress validation of children.
/// </summary>
public string FullTypeName { get; }
/// <inheritdoc />
public void GetValidationMetadata(ValidationMetadataProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (Type != null)
{
if (Type.GetTypeInfo().IsAssignableFrom(context.Key.ModelType.GetTypeInfo()))
{
context.ValidationMetadata.ValidateChildren = false;
}
return;
}
if (FullTypeName != null)
{
if (IsMatchingName(context.Key.ModelType))
{
context.ValidationMetadata.ValidateChildren = false;
}
return;
}
Debug.Fail("We shouldn't get here.");
}
private bool IsMatchingName(Type type)
{
Debug.Assert(FullTypeName != null);
if (type == null)
{
return false;
}
if (string.Equals(type.FullName, FullTypeName, StringComparison.Ordinal))
{
return true;
}
return IsMatchingName(type.GetTypeInfo().BaseType);
}
}
}

View File

@ -14,7 +14,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
public class ValidationVisitor
{
private readonly IModelValidatorProvider _validatorProvider;
private readonly IList<IExcludeTypeValidationFilter> _excludeFilters;
private readonly ActionContext _actionContext;
private readonly ModelStateDictionary _modelState;
private readonly ValidationStateDictionary _validationState;
@ -33,12 +32,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
/// </summary>
/// <param name="actionContext">The <see cref="ActionContext"/> associated with the current request.</param>
/// <param name="validatorProvider">The <see cref="IModelValidatorProvider"/>.</param>
/// <param name="excludeFilters">The list of <see cref="IExcludeTypeValidationFilter"/>.</param>
/// <param name="validationState">The <see cref="ValidationStateDictionary"/>.</param>
public ValidationVisitor(
ActionContext actionContext,
IModelValidatorProvider validatorProvider,
IList<IExcludeTypeValidationFilter> excludeFilters,
ValidationStateDictionary validationState)
{
if (actionContext == null)
@ -51,14 +48,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
throw new ArgumentNullException(nameof(validatorProvider));
}
if (excludeFilters == null)
{
throw new ArgumentNullException(nameof(excludeFilters));
}
_actionContext = actionContext;
_validatorProvider = validatorProvider;
_excludeFilters = excludeFilters;
_validationState = validationState;
_modelState = actionContext.ModelState;
@ -191,11 +182,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
var isValid = true;
if (_model != null)
if (_model != null && _metadata.ValidateChildren)
{
var strategy = _strategy ?? DefaultCollectionValidationStrategy.Instance;
isValid = VisitChildren(strategy);
}
else if (_model != null)
{
// Suppress validation for the entries matching this prefix. This will temporarily set
// the current node to 'skipped' but we're going to visit it right away, so subsequent
// code will set it to 'valid' or 'invalid'
SuppressValidation(_key);
}
// Double-checking HasReachedMaxErrors just in case this model has no elements.
if (isValid && !_modelState.HasReachedMaxErrors)
@ -210,14 +208,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
var isValid = true;
if (_model != null && ShouldValidateProperties(_metadata))
if (_model != null && _metadata.ValidateChildren)
{
var strategy = _strategy ?? DefaultComplexObjectValidationStrategy.Instance;
isValid = VisitChildren(strategy);
}
else if (_model != null)
{
// Suppress validation for the prefix, but we still want to validate the object.
// Suppress validation for the entries matching this prefix. This will temporarily set
// the current node to 'skipped' but we're going to visit it right away, so subsequent
// code will set it to 'valid' or 'invalid'
SuppressValidation(_key);
}
@ -286,20 +286,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
}
}
private bool ShouldValidateProperties(ModelMetadata metadata)
{
var count = _excludeFilters.Count;
for (var i = 0; i < _excludeFilters.Count; i++)
{
if (_excludeFilters[i].IsTypeExcluded(metadata.UnderlyingOrModelType))
{
return false;
}
}
return true;
}
private ValidationStateEntry GetValidationEntry(object model)
{
if (model == null || _validationState == null)

View File

@ -31,7 +31,6 @@ namespace Microsoft.AspNet.Mvc
ModelBindingMessageProvider = new ModelBindingMessageProvider();
ModelMetadataDetailsProviders = new List<IMetadataDetailsProvider>();
ModelValidatorProviders = new List<IModelValidatorProvider>();
ValidationExcludeFilters = new ExcludeTypeValidationFilterCollection();
ValueProviderFactories = new List<IValueProviderFactory>();
}
@ -125,11 +124,6 @@ namespace Microsoft.AspNet.Mvc
/// </summary>
public bool RespectBrowserAcceptHeader { get; set; }
/// <summary>
/// Gets a collection of <see cref="IExcludeTypeValidationFilter"/>s that are used by this application.
/// </summary>
public ExcludeTypeValidationFilterCollection ValidationExcludeFilters { get; }
/// <summary>
/// Gets a list of <see cref="IValueProviderFactory"/> used by this application.
/// </summary>

View File

@ -1,6 +1,7 @@
// 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 Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.OptionsModel;
using Microsoft.Net.Http.Headers;
@ -30,7 +31,7 @@ namespace Microsoft.AspNet.Mvc.Formatters.Json.Internal
options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("application/json"));
options.ValidationExcludeFilters.Add(typeof(JToken));
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(JToken)));
}
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.OptionsModel;
namespace Microsoft.AspNet.Mvc.Formatters.Xml.Internal
@ -31,8 +32,8 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml.Internal
options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
options.InputFormatters.Add(new XmlDataContractSerializerInputFormatter());
options.ValidationExcludeFilters.Add(typeFullName: "System.Xml.Linq.XObject");
options.ValidationExcludeFilters.Add(typeFullName: "System.Xml.XmlNode");
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter("System.Xml.Linq.XObject"));
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter("System.Xml.XmlNode"));
}
}
}

View File

@ -3,6 +3,7 @@
using System.Net.Http;
using System.Net.Http.Formatting;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.OptionsModel;
@ -32,8 +33,8 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
// Add a formatter to write out an HttpResponseMessage to the response
options.OutputFormatters.Insert(0, new HttpResponseMessageOutputFormatter());
options.ValidationExcludeFilters.Add(typeof(HttpRequestMessage));
options.ValidationExcludeFilters.Add(typeof(HttpResponseMessage));
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(HttpRequestMessage)));
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(HttpResponseMessage)));
}
public void Configure(WebApiCompatShimOptions options)

View File

@ -568,6 +568,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
}
public override bool ValidateChildren
{
get
{
throw new NotImplementedException();
}
}
public override IReadOnlyList<object> ValidatorMetadata
{
get

View File

@ -2112,7 +2112,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
new IInputFormatter[0],
new DefaultControllerActionArgumentBinder(
metadataProvider,
new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider)),
new DefaultObjectValidator(metadataProvider)),
new IModelBinder[] { binder.Object },
new IModelValidatorProvider[0],
new IValueProviderFactory[0],

View File

@ -251,7 +251,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
services.Setup(s => s.GetService(typeof(IModelMetadataProvider)))
.Returns(metadataProvider);
services.Setup(s => s.GetService(typeof(IObjectModelValidator)))
.Returns(new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider));
.Returns(new DefaultObjectValidator(metadataProvider));
return services.Object;
}

View File

@ -6,6 +6,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Xml;
using Moq;
using Xunit;
@ -605,6 +606,103 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
Assert.False(isReadOnly);
}
[Theory]
[InlineData(typeof(int))] // Primitive
[InlineData(typeof(int?))] // Nullable
[InlineData(typeof(Guid))] // TypeConverter
[InlineData(typeof(Guid?))] // Nullable + TypeConverter
[InlineData(typeof(string))]
public void ValidateChildren_SimpleTypes(Type modelType)
{
// Arrange
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var validateChildren = metadata.ValidateChildren;
// Assert
Assert.False(validateChildren);
}
[Theory]
[InlineData(typeof(int[]))]
[InlineData(typeof(List<decimal>))]
[InlineData(typeof(IEnumerable))]
[InlineData(typeof(Dictionary<string, string>))]
[InlineData(typeof(KeyValuePair<string, string>))]
[InlineData(typeof(KeyValuePair<string, string>?))]
[InlineData(typeof(TypeWithProperties))]
[InlineData(typeof(List<TypeWithProperties>))]
public void ValidateChildren_ComplexAndEnumerableTypes(Type modelType)
{
// Arrange
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(TypeWithProperties));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var validateChildren = metadata.ValidateChildren;
// Assert
Assert.True(validateChildren);
}
[Fact]
public void ValidateChildren_OverrideTrue()
{
// Arrange
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(int));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
cache.ValidationMetadata = new ValidationMetadata()
{
ValidateChildren = true,
};
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var validateChildren = metadata.ValidateChildren;
// Assert
Assert.True(validateChildren);
}
[Fact]
public void ValidateChildren_OverrideFalse()
{
// Arrange
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(XmlDocument));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
cache.ValidationMetadata = new ValidationMetadata()
{
ValidateChildren = false,
};
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var validateChildren = metadata.ValidateChildren;
// Assert
Assert.False(validateChildren);
}
private void ActionMethod(string input)
{
}

View File

@ -95,7 +95,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
GetCompositeBinder(binders),
valueProvider,
new List<IInputFormatter>(),
new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], modelMetadataProvider),
new DefaultObjectValidator(modelMetadataProvider),
validator);
// Assert
@ -136,7 +136,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
GetCompositeBinder(binders),
valueProvider,
new List<IInputFormatter>(),
new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider),
new DefaultObjectValidator(metadataProvider),
validator);
// Assert
@ -221,7 +221,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
GetCompositeBinder(binders),
valueProvider,
new List<IInputFormatter>(),
new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider),
new DefaultObjectValidator(metadataProvider),
validator,
includePredicate);
@ -305,7 +305,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
GetCompositeBinder(binders),
valueProvider,
new List<IInputFormatter>(),
new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider),
new DefaultObjectValidator(metadataProvider),
validator,
m => m.IncludedProperty,
m => m.MyProperty);
@ -357,7 +357,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
GetCompositeBinder(binders),
valueProvider,
new List<IInputFormatter>(),
new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider),
new DefaultObjectValidator(metadataProvider),
validator);
// Assert
@ -568,9 +568,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
GetCompositeBinder(binders),
valueProvider,
new List<IInputFormatter>(),
new DefaultObjectValidator(
new IExcludeTypeValidationFilter[0],
metadataProvider),
new DefaultObjectValidator(metadataProvider),
validator,
includePredicate);
@ -645,9 +643,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
GetCompositeBinder(binders),
valueProvider,
new List<IInputFormatter>(),
new DefaultObjectValidator(
new IExcludeTypeValidationFilter[0],
metadataProvider),
new DefaultObjectValidator(metadataProvider),
validator);
// Assert
@ -679,9 +675,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
GetCompositeBinder(binder.Object),
Mock.Of<IValueProvider>(),
new List<IInputFormatter>(),
new DefaultObjectValidator(
new IExcludeTypeValidationFilter[0],
metadataProvider),
new DefaultObjectValidator(metadataProvider),
Mock.Of<IModelValidatorProvider>(),
includePredicate));

View File

@ -8,6 +8,7 @@ using System.Linq;
#if DNXCORE50
using System.Reflection;
#endif
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Testing;
using Moq;
using Xunit;
@ -655,13 +656,40 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Profession"), error.ErrorMessage);
}
public static TheoryData<object, Type> ValidCollectionData
{
get
{
return new TheoryData<object, Type>()
{
{ new int[] { 1, 2, 3 }, typeof(int[]) },
{ new string[] { "Foo", "Bar", "Baz" }, typeof(string[]) },
{ new List<string> { "Foo", "Bar", "Baz" }, typeof(IList<string>)},
{ new HashSet<string> { "Foo", "Bar", "Baz" }, typeof(string[]) },
{
new List<DateTime>
{
DateTime.Parse("1/1/14"),
DateTime.Parse("2/1/14"),
DateTime.Parse("3/1/14"),
},
typeof(ICollection<DateTime>)
},
{
new HashSet<Uri>
{
new Uri("http://example.com/1"),
new Uri("http://example.com/2"),
new Uri("http://example.com/3"),
},
typeof(HashSet<Uri>)
},
};
}
}
[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>))]
[MemberData(nameof(ValidCollectionData))]
public void Validate_IndexedCollectionTypes_Valid(object model, Type type)
{
// Arrange
@ -670,7 +698,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
var modelState = actionContext.ModelState;
var validationState = new ValidationStateDictionary();
var validator = CreateValidator(new SimpleTypesExcludeFilter());
var validator = CreateValidator();
modelState.Add("items[0]", new ModelStateEntry());
modelState.Add("items[1]", new ModelStateEntry());
@ -712,7 +740,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
var modelState = actionContext.ModelState;
var validationState = new ValidationStateDictionary();
var validator = CreateValidator(new SimpleTypesExcludeFilter());
var validator = CreateValidator();
var model = new Dictionary<string, string>()
{
@ -734,19 +762,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
AssertKeysEqual(modelState, "items[0].Key", "items[0].Value", "items[1].Key", "items[1].Value");
var entry = modelState["items[0].Key"];
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
Assert.Empty(entry.Errors);
entry = modelState["items[0].Value"];
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
Assert.Empty(entry.Errors);
entry = modelState["items[1].Key"];
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
Assert.Empty(entry.Errors);
entry = modelState["items[1].Value"];
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
Assert.Empty(entry.Errors);
}
@ -857,7 +885,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
}
[Fact]
public void Validate_ForExcludedType_PropertiesMarkedAsSkipped()
public void Validate_ForExcludedComplexType_PropertiesMarkedAsSkipped()
{
// Arrange
var validatorProvider = CreateValidatorProvider();
@ -890,6 +918,37 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
Assert.Empty(entry.Errors);
}
[Fact]
public void Validate_ForExcludedCollectionType_PropertiesMarkedAsSkipped()
{
// Arrange
var validatorProvider = CreateValidatorProvider();
var actionContext = new ActionContext();
var modelState = actionContext.ModelState;
var validationState = new ValidationStateDictionary();
var validator = CreateValidator(typeof(List<string>));
var model = new List<string>()
{
"15",
};
modelState.SetModelValue("userIds[0]", "15", "15");
validationState.Add(model, new ValidationStateEntry() { Key = "userIds", });
// Act
validator.Validate(actionContext, validatorProvider, validationState, "userIds", model);
// Assert
Assert.Equal(ModelValidationState.Valid, modelState.ValidationState);
AssertKeysEqual(modelState, "userIds[0]");
var entry = modelState["userIds[0]"];
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
Assert.Empty(entry.Errors);
}
private static IModelValidatorProvider CreateValidatorProvider()
{
return TestModelValidatorProvider.CreateDefaultProvider();
@ -897,23 +956,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
private static DefaultObjectValidator CreateValidator(Type excludedType)
{
var excludeFilters = new List<IExcludeTypeValidationFilter>();
var excludeFilters = new List<ValidationExcludeFilter>();
if (excludedType != null)
{
var excludeFilter = new Mock<IExcludeTypeValidationFilter>();
excludeFilter
.Setup(o => o.IsTypeExcluded(It.IsAny<Type>()))
.Returns<Type>(t => t.IsAssignableFrom(excludedType));
excludeFilters.Add(excludeFilter.Object);
excludeFilters.Add(new ValidationExcludeFilter(excludedType));
}
return new DefaultObjectValidator(excludeFilters, TestModelMetadataProvider.CreateDefaultProvider());
var provider = TestModelMetadataProvider.CreateDefaultProvider(excludeFilters.ToArray());
return new DefaultObjectValidator(provider);
}
private static DefaultObjectValidator CreateValidator(params IExcludeTypeValidationFilter[] excludeFilters)
private static DefaultObjectValidator CreateValidator(params IMetadataDetailsProvider[] providers)
{
return new DefaultObjectValidator(excludeFilters, TestModelMetadataProvider.CreateDefaultProvider());
var provider = TestModelMetadataProvider.CreateDefaultProvider(providers);
return new DefaultObjectValidator(provider);
}
private static void AssertKeysEqual(ModelStateDictionary modelState, params string[] keys)

View File

@ -1,45 +0,0 @@
// 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 Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class ExcludeTypeValidationFilterCollectionTest
{
[Fact]
public void AddFilter_ByType()
{
// Arrange
var type = typeof(BaseType);
var collection = new ExcludeTypeValidationFilterCollection();
// Act
collection.Add(type);
// Assert
var filter = Assert.IsType<DefaultTypeBasedExcludeFilter>(Assert.Single(collection));
Assert.Equal(type, filter.ExcludedType);
}
[Fact]
public void AddFilter_ByTypeName()
{
// Arrange
var type = typeof(BaseType);
var collection = new ExcludeTypeValidationFilterCollection();
// Act
collection.Add(type.FullName);
// Assert
var filter = Assert.IsType<DefaultTypeNameBasedExcludeFilter>(Assert.Single(collection));
Assert.Equal(type.FullName, filter.ExcludedTypeName);
}
private class BaseType
{
}
}
}

View File

@ -1,89 +0,0 @@
// 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 Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class SimpleTypeExcludeFilterTest
{
[Theory]
[MemberData(nameof(ExcludedTypes))]
public void SimpleTypeExcludeFilter_ExcludedTypes(Type type)
{
// Arrange
var filter = new SimpleTypesExcludeFilter();
// Act & Assert
Assert.True(filter.IsTypeExcluded(type));
}
[Theory]
[MemberData(nameof(IncludedTypes))]
public void SimpleTypeExcludeFilter_IncludedTypes(Type type)
{
// Arrange
var filter = new SimpleTypesExcludeFilter();
// Act & Assert
Assert.False(filter.IsTypeExcluded(type));
}
private class TestType
{
}
public static TheoryData<Type> ExcludedTypes
{
get
{
return new TheoryData<Type>()
{
// Simple types
typeof(int),
typeof(DateTime),
// Nullable types
typeof(int?),
// KeyValue types
typeof(KeyValuePair<string, string>)
};
}
}
public static TheoryData<Type> IncludedTypes
{
get
{
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(SortedSet<TestType>),
typeof(Dictionary<int, TestType>),
typeof(Dictionary<TestType, int>),
typeof(Dictionary<TestType, TestType>),
typeof(KeyValuePair<string, TestType>)
};
}
}
}
}

View File

@ -129,14 +129,12 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[low].Key").Value;
Assert.Equal("key0", entry.AttemptedValue);
Assert.Equal("key0", entry.RawValue);
// Key and Value are skipped if they have simple types.
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[low].Value").Value;
Assert.Equal("10", entry.AttemptedValue);
Assert.Equal("10", entry.RawValue);
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
}
[Theory]

View File

@ -104,9 +104,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
binding.ModelBindingMessageProvider.MissingKeyOrValueAccessor = () => $"Hurts when nothing is provided.";
}));
var argumentBinder = new DefaultControllerActionArgumentBinder(
metadataProvider,
ModelBindingTestHelper.GetObjectValidator());
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(metadataProvider);
var parameter = new ParameterDescriptor
{
Name = "parameter",
@ -188,9 +186,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// A real details provider could customize message based on BindingMetadataProviderContext.
binding.ModelBindingMessageProvider.MissingKeyOrValueAccessor = () => $"Hurts when nothing is provided.";
}));
var argumentBinder = new DefaultControllerActionArgumentBinder(
metadataProvider,
ModelBindingTestHelper.GetObjectValidator());
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(metadataProvider);
var parameter = new ParameterDescriptor
{

View File

@ -42,20 +42,28 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
public static DefaultControllerActionArgumentBinder GetArgumentBinder(MvcOptions options = null)
{
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
return new DefaultControllerActionArgumentBinder(
metadataProvider,
GetObjectValidator(options));
if (options == null)
{
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
return GetArgumentBinder(metadataProvider);
}
else
{
var metadataProvider = TestModelMetadataProvider.CreateProvider(options.ModelMetadataDetailsProviders);
return GetArgumentBinder(metadataProvider);
}
}
public static IObjectModelValidator GetObjectValidator(MvcOptions options = null)
public static DefaultControllerActionArgumentBinder GetArgumentBinder(IModelMetadataProvider metadataProvider)
{
options = options ?? new TestMvcOptions().Value;
options.MaxModelValidationErrors = 5;
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
return new DefaultObjectValidator(
options.ValidationExcludeFilters,
metadataProvider);
return new DefaultControllerActionArgumentBinder(
metadataProvider,
GetObjectValidator(metadataProvider));
}
public static IObjectModelValidator GetObjectValidator(IModelMetadataProvider metadataProvider)
{
return new DefaultObjectValidator(metadataProvider);
}
private static HttpContext GetHttpContext(

View File

@ -1424,9 +1424,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
binding.ModelBindingMessageProvider.MissingBindRequiredValueAccessor =
name => $"Hurts when '{ name }' is not provided.";
}));
var argumentBinder = new DefaultControllerActionArgumentBinder(
metadataProvider,
ModelBindingTestHelper.GetObjectValidator());
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(metadataProvider);
var parameter = new ParameterDescriptor()
{
Name = "parameter",

View File

@ -312,9 +312,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
binding.ModelBindingMessageProvider.ValueMustNotBeNullAccessor =
value => $"Hurts when '{ value }' is provided.";
}));
var argumentBinder = new DefaultControllerActionArgumentBinder(
metadataProvider,
ModelBindingTestHelper.GetObjectValidator());
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(metadataProvider);
var parameter = new ParameterDescriptor
{
Name = "Parameter1",

View File

@ -1109,7 +1109,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
operationContext.ModelBinder,
operationContext.ValueProvider,
operationContext.InputFormatters,
ModelBindingTestHelper.GetObjectValidator(),
ModelBindingTestHelper.GetObjectValidator(operationContext.MetadataProvider),
operationContext.ValidatorProvider);
}
}

View File

@ -9,6 +9,8 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Newtonsoft.Json.Linq;
using Xunit;
namespace Microsoft.AspNet.Mvc.IntegrationTests
@ -1129,7 +1131,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
},
options =>
{
options.ValidationExcludeFilters.Add(typeof(Address));
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(Address)));
testOptions = options;
});
@ -1167,6 +1169,55 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
}
[Fact]
public async Task FromBody_JToken_ExcludedFromValidation()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(new TestMvcOptions().Value);
var parameter = new ParameterDescriptor
{
Name = "Parameter1",
BindingInfo = new BindingInfo
{
BinderModelName = "CustomParameter",
BindingSource = BindingSource.Body
},
ParameterType = typeof(JToken)
};
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(
request =>
{
request.Body = new MemoryStream(Encoding.UTF8.GetBytes("{ message: \"Hello\" }"));
request.ContentType = "application/json";
});
var httpContext = operationContext.HttpContext;
var modelState = operationContext.ActionContext.ModelState;
// We need to add another model state entry which should get marked as skipped so
// we can prove that the JObject was skipped.
modelState.SetModelValue("message", "Hello", "Hello");
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, operationContext);
// Assert
Assert.True(modelBindingResult.IsModelSet);
Assert.NotNull(modelBindingResult.Model);
var message = Assert.IsType<JObject>(modelBindingResult.Model).GetValue("message").Value<string>();
Assert.Equal("Hello", message);
Assert.True(modelState.IsValid);
Assert.Equal(2, modelState.Count);
var entry = Assert.Single(modelState, kvp => kvp.Key == string.Empty);
Assert.Equal(ModelValidationState.Valid, entry.Value.ValidationState);
entry = Assert.Single(modelState, kvp => kvp.Key == "message");
Assert.Equal(ModelValidationState.Skipped, entry.Value.ValidationState);
}
private static void AssertRequiredError(string key, ModelError error)
{
// Mono issue - https://github.com/aspnet/External/issues/19

View File

@ -19,6 +19,9 @@ using Moq;
using Newtonsoft.Json.Linq;
using Xunit;
using Microsoft.Extensions.Logging;
using System.Threading;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
namespace Microsoft.AspNet.Mvc
{
@ -134,7 +137,7 @@ namespace Microsoft.AspNet.Mvc
}
[Fact]
public void Setup_SetsUpExcludeFromValidationDelegates()
public void Setup_SetsUpMetadataDetailsProviders()
{
// Arrange & Act
var options = GetOptions<MvcOptions>(services =>
@ -144,48 +147,40 @@ namespace Microsoft.AspNet.Mvc
});
// Assert
Assert.Equal(8, options.ValidationExcludeFilters.Count);
var providers = options.ModelMetadataDetailsProviders;
Assert.Equal(12, providers.Count);
var i = 0;
// Verify if the delegates registered by default exclude the given types.
Assert.IsType(typeof(SimpleTypesExcludeFilter), options.ValidationExcludeFilters[i++]);
Assert.IsType<DefaultBindingMetadataProvider>(providers[i++]);
Assert.IsType<DefaultValidationMetadataProvider>(providers[i++]);
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), options.ValidationExcludeFilters[i]);
var typeFilter
= Assert.IsType<DefaultTypeBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
Assert.Equal(typeFilter.ExcludedType, typeof(Type));
var excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
Assert.Equal(typeof(Type), excludeFilter.Type);
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), options.ValidationExcludeFilters[i]);
var cancellationTokenFilter
= Assert.IsType<DefaultTypeBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
Assert.Equal(cancellationTokenFilter.ExcludedType, typeof(System.Threading.CancellationToken));
excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
Assert.Equal(typeof(Uri), excludeFilter.Type);
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), options.ValidationExcludeFilters[i]);
var formFileFilter
= Assert.IsType<DefaultTypeBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
Assert.Equal(formFileFilter.ExcludedType, typeof(Http.IFormFile));
excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
Assert.Equal(typeof(CancellationToken), excludeFilter.Type);
Assert.IsType(
typeof(DefaultTypeBasedExcludeFilter),
options.ValidationExcludeFilters[i]);
var formCollectionFilter
= Assert.IsType<DefaultTypeBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
Assert.Equal(formCollectionFilter.ExcludedType, typeof(Http.IFormCollection));
excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
Assert.Equal(typeof(IFormFile), excludeFilter.Type);
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), options.ValidationExcludeFilters[i]);
var jTokenFilter
= Assert.IsType<DefaultTypeBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
Assert.Equal(jTokenFilter.ExcludedType, typeof(JToken));
excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
Assert.Equal(typeof(IFormCollection), excludeFilter.Type);
Assert.IsType(typeof(DefaultTypeNameBasedExcludeFilter), options.ValidationExcludeFilters[i]);
var xObjectFilter
= Assert.IsType<DefaultTypeNameBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
Assert.Equal(xObjectFilter.ExcludedTypeName, typeof(XObject).FullName);
Assert.IsType<DataAnnotationsMetadataProvider>(providers[i++]);
Assert.IsType(typeof(DefaultTypeNameBasedExcludeFilter), options.ValidationExcludeFilters[i]);
var xmlNodeFilter =
Assert.IsType<DefaultTypeNameBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
Assert.Equal(xmlNodeFilter.ExcludedTypeName, "System.Xml.XmlNode");
excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
Assert.Equal(typeof(JToken), excludeFilter.Type);
Assert.IsType<DataMemberRequiredBindingMetadataProvider>(providers[i++]);
excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
Assert.Equal(typeof(XObject).FullName, excludeFilter.FullTypeName);
excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
Assert.Equal("System.Xml.XmlNode", excludeFilter.FullTypeName);
}
[Fact]

View File

@ -26,6 +26,34 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return new DefaultModelMetadataProvider(compositeDetailsProvider);
}
public static IModelMetadataProvider CreateDefaultProvider(IList<IMetadataDetailsProvider> providers)
{
var detailsProviders = new List<IMetadataDetailsProvider>()
{
new DefaultBindingMetadataProvider(CreateMessageProvider()),
new DefaultValidationMetadataProvider(),
new DataAnnotationsMetadataProvider(),
new DataMemberRequiredBindingMetadataProvider(),
};
detailsProviders.AddRange(providers);
var compositeDetailsProvider = new DefaultCompositeMetadataDetailsProvider(detailsProviders);
return new DefaultModelMetadataProvider(compositeDetailsProvider);
}
public static IModelMetadataProvider CreateProvider(IList<IMetadataDetailsProvider> providers)
{
var detailsProviders = new List<IMetadataDetailsProvider>();
if (providers != null)
{
detailsProviders.AddRange(providers);
}
var compositeDetailsProvider = new DefaultCompositeMetadataDetailsProvider(detailsProviders);
return new DefaultModelMetadataProvider(compositeDetailsProvider);
}
private readonly TestModelMetadataDetailsProvider _detailsProvider;
public TestModelMetadataProvider()

View File

@ -1873,7 +1873,7 @@ namespace Microsoft.AspNet.Mvc.Test
{
ControllerContext = controllerContext,
MetadataProvider = metadataProvider,
ObjectValidator = new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider),
ObjectValidator = new DefaultObjectValidator(metadataProvider),
TempData = tempData,
ViewData = viewData,
};

View File

@ -3,6 +3,7 @@
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.DependencyInjection;
namespace FormatterWebSite
@ -13,8 +14,8 @@ namespace FormatterWebSite
{
services.AddMvc(options =>
{
options.ValidationExcludeFilters.Add(typeof(Developer));
options.ValidationExcludeFilters.Add(typeof(Supplier));
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(Developer)));
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(Supplier)));
options.InputFormatters.Add(new StringInputFormatter());
})

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.DependencyInjection;
using ModelBindingWebSite.Models;
using ModelBindingWebSite.Services;
@ -20,7 +21,7 @@ namespace ModelBindingWebSite
m.MaxModelValidationErrors = 8;
m.ModelBinders.Insert(0, new TestBindingSourceModelBinder());
m.ValidationExcludeFilters.Add(typeof(Address));
m.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(Address)));
// ModelMetadataController relies on additional values AdditionalValuesMetadataProvider provides.
m.ModelMetadataDetailsProviders.Add(new AdditionalValuesMetadataProvider());