Remove IExcludeTypeFilter
- Removes IExcludeTypeFilter - Replaced with a property 'ValidateChildren' on ModelMetadata - Teach ValidationVisitor to respect 'ValidateChildren' for enumerable types.
This commit is contained in:
parent
774ee05508
commit
96de1dbe4b
|
|
@ -289,6 +289,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract string TemplateHint { get; }
|
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>
|
/// <summary>
|
||||||
/// Gets a collection of metadata items for validators.
|
/// Gets a collection of metadata items for validators.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -134,12 +134,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Value;
|
var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Value;
|
||||||
return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders);
|
return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders);
|
||||||
}));
|
}));
|
||||||
services.TryAdd(ServiceDescriptor.Singleton<IObjectModelValidator>(serviceProvider =>
|
services.TryAddSingleton<IObjectModelValidator, DefaultObjectValidator>();
|
||||||
{
|
|
||||||
var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Value;
|
|
||||||
var modelMetadataProvider = serviceProvider.GetRequiredService<IModelMetadataProvider>();
|
|
||||||
return new DefaultObjectValidator(options.ValidationExcludeFilters, modelMetadataProvider);
|
|
||||||
}));
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Random Infrastructure
|
// Random Infrastructure
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// 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;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Mvc.Core;
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
using Microsoft.AspNet.Mvc.Formatters;
|
using Microsoft.AspNet.Mvc.Formatters;
|
||||||
|
|
@ -63,13 +64,11 @@ namespace Microsoft.AspNet.Mvc.Internal
|
||||||
options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider());
|
options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider());
|
||||||
|
|
||||||
// Add types to be excluded from Validation
|
// Add types to be excluded from Validation
|
||||||
options.ValidationExcludeFilters.Add(new SimpleTypesExcludeFilter());
|
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(Type)));
|
||||||
options.ValidationExcludeFilters.Add(typeof(Type));
|
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(Uri)));
|
||||||
|
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(CancellationToken)));
|
||||||
// Any 'known' types that we bind should be marked as excluded from validation.
|
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(IFormFile)));
|
||||||
options.ValidationExcludeFilters.Add(typeof(System.Threading.CancellationToken));
|
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(IFormCollection)));
|
||||||
options.ValidationExcludeFilters.Add(typeof(IFormFile));
|
|
||||||
options.ValidationExcludeFilters.Add(typeof(IFormCollection));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -29,6 +29,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
||||||
private bool? _isReadOnly;
|
private bool? _isReadOnly;
|
||||||
private bool? _isRequired;
|
private bool? _isRequired;
|
||||||
private ModelPropertyCollection _properties;
|
private ModelPropertyCollection _properties;
|
||||||
|
private bool? _validateChildren;
|
||||||
private ReadOnlyCollection<object> _validatorMetadata;
|
private ReadOnlyCollection<object> _validatorMetadata;
|
||||||
|
|
||||||
/// <summary>
|
/// <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 />
|
/// <inheritdoc />
|
||||||
public override IReadOnlyList<object> ValidatorMetadata
|
public override IReadOnlyList<object> ValidatorMetadata
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? IsRequired { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets a list of metadata items for validators.
|
/// Gets a list of metadata items for validators.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// 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;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
|
|
@ -11,31 +10,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DefaultObjectValidator : IObjectModelValidator
|
public class DefaultObjectValidator : IObjectModelValidator
|
||||||
{
|
{
|
||||||
private readonly IList<IExcludeTypeValidationFilter> _excludeFilters;
|
|
||||||
private readonly IModelMetadataProvider _modelMetadataProvider;
|
private readonly IModelMetadataProvider _modelMetadataProvider;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of <see cref="DefaultObjectValidator"/>.
|
/// Initializes a new instance of <see cref="DefaultObjectValidator"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="excludeFilters"><see cref="IExcludeTypeValidationFilter"/>s that determine
|
|
||||||
/// types to exclude from validation.</param>
|
|
||||||
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
|
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
|
||||||
public DefaultObjectValidator(
|
public DefaultObjectValidator(
|
||||||
IList<IExcludeTypeValidationFilter> excludeFilters,
|
|
||||||
IModelMetadataProvider modelMetadataProvider)
|
IModelMetadataProvider modelMetadataProvider)
|
||||||
{
|
{
|
||||||
if (excludeFilters == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(excludeFilters));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modelMetadataProvider == null)
|
if (modelMetadataProvider == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(modelMetadataProvider));
|
throw new ArgumentNullException(nameof(modelMetadataProvider));
|
||||||
}
|
}
|
||||||
|
|
||||||
_modelMetadataProvider = modelMetadataProvider;
|
_modelMetadataProvider = modelMetadataProvider;
|
||||||
_excludeFilters = excludeFilters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -59,7 +48,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
var visitor = new ValidationVisitor(
|
var visitor = new ValidationVisitor(
|
||||||
actionContext,
|
actionContext,
|
||||||
validatorProvider,
|
validatorProvider,
|
||||||
_excludeFilters,
|
|
||||||
validationState);
|
validationState);
|
||||||
|
|
||||||
var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType());
|
var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType());
|
||||||
|
|
|
||||||
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
public class ValidationVisitor
|
public class ValidationVisitor
|
||||||
{
|
{
|
||||||
private readonly IModelValidatorProvider _validatorProvider;
|
private readonly IModelValidatorProvider _validatorProvider;
|
||||||
private readonly IList<IExcludeTypeValidationFilter> _excludeFilters;
|
|
||||||
private readonly ActionContext _actionContext;
|
private readonly ActionContext _actionContext;
|
||||||
private readonly ModelStateDictionary _modelState;
|
private readonly ModelStateDictionary _modelState;
|
||||||
private readonly ValidationStateDictionary _validationState;
|
private readonly ValidationStateDictionary _validationState;
|
||||||
|
|
@ -33,12 +32,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="actionContext">The <see cref="ActionContext"/> associated with the current request.</param>
|
/// <param name="actionContext">The <see cref="ActionContext"/> associated with the current request.</param>
|
||||||
/// <param name="validatorProvider">The <see cref="IModelValidatorProvider"/>.</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>
|
/// <param name="validationState">The <see cref="ValidationStateDictionary"/>.</param>
|
||||||
public ValidationVisitor(
|
public ValidationVisitor(
|
||||||
ActionContext actionContext,
|
ActionContext actionContext,
|
||||||
IModelValidatorProvider validatorProvider,
|
IModelValidatorProvider validatorProvider,
|
||||||
IList<IExcludeTypeValidationFilter> excludeFilters,
|
|
||||||
ValidationStateDictionary validationState)
|
ValidationStateDictionary validationState)
|
||||||
{
|
{
|
||||||
if (actionContext == null)
|
if (actionContext == null)
|
||||||
|
|
@ -51,14 +48,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
throw new ArgumentNullException(nameof(validatorProvider));
|
throw new ArgumentNullException(nameof(validatorProvider));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (excludeFilters == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(excludeFilters));
|
|
||||||
}
|
|
||||||
|
|
||||||
_actionContext = actionContext;
|
_actionContext = actionContext;
|
||||||
_validatorProvider = validatorProvider;
|
_validatorProvider = validatorProvider;
|
||||||
_excludeFilters = excludeFilters;
|
|
||||||
_validationState = validationState;
|
_validationState = validationState;
|
||||||
|
|
||||||
_modelState = actionContext.ModelState;
|
_modelState = actionContext.ModelState;
|
||||||
|
|
@ -191,11 +182,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
var isValid = true;
|
var isValid = true;
|
||||||
|
|
||||||
if (_model != null)
|
if (_model != null && _metadata.ValidateChildren)
|
||||||
{
|
{
|
||||||
var strategy = _strategy ?? DefaultCollectionValidationStrategy.Instance;
|
var strategy = _strategy ?? DefaultCollectionValidationStrategy.Instance;
|
||||||
isValid = VisitChildren(strategy);
|
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.
|
// Double-checking HasReachedMaxErrors just in case this model has no elements.
|
||||||
if (isValid && !_modelState.HasReachedMaxErrors)
|
if (isValid && !_modelState.HasReachedMaxErrors)
|
||||||
|
|
@ -210,14 +208,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
var isValid = true;
|
var isValid = true;
|
||||||
|
|
||||||
if (_model != null && ShouldValidateProperties(_metadata))
|
if (_model != null && _metadata.ValidateChildren)
|
||||||
{
|
{
|
||||||
var strategy = _strategy ?? DefaultComplexObjectValidationStrategy.Instance;
|
var strategy = _strategy ?? DefaultComplexObjectValidationStrategy.Instance;
|
||||||
isValid = VisitChildren(strategy);
|
isValid = VisitChildren(strategy);
|
||||||
}
|
}
|
||||||
else if (_model != null)
|
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);
|
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)
|
private ValidationStateEntry GetValidationEntry(object model)
|
||||||
{
|
{
|
||||||
if (model == null || _validationState == null)
|
if (model == null || _validationState == null)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ namespace Microsoft.AspNet.Mvc
|
||||||
ModelBindingMessageProvider = new ModelBindingMessageProvider();
|
ModelBindingMessageProvider = new ModelBindingMessageProvider();
|
||||||
ModelMetadataDetailsProviders = new List<IMetadataDetailsProvider>();
|
ModelMetadataDetailsProviders = new List<IMetadataDetailsProvider>();
|
||||||
ModelValidatorProviders = new List<IModelValidatorProvider>();
|
ModelValidatorProviders = new List<IModelValidatorProvider>();
|
||||||
ValidationExcludeFilters = new ExcludeTypeValidationFilterCollection();
|
|
||||||
ValueProviderFactories = new List<IValueProviderFactory>();
|
ValueProviderFactories = new List<IValueProviderFactory>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,11 +124,6 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RespectBrowserAcceptHeader { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets a list of <see cref="IValueProviderFactory"/> used by this application.
|
/// Gets a list of <see cref="IValueProviderFactory"/> used by this application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// 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 Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.OptionsModel;
|
using Microsoft.Extensions.OptionsModel;
|
||||||
using Microsoft.Net.Http.Headers;
|
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.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("application/json"));
|
||||||
|
|
||||||
options.ValidationExcludeFilters.Add(typeof(JToken));
|
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(JToken)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// 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 Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.OptionsModel;
|
using Microsoft.Extensions.OptionsModel;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Formatters.Xml.Internal
|
namespace Microsoft.AspNet.Mvc.Formatters.Xml.Internal
|
||||||
|
|
@ -31,8 +32,8 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml.Internal
|
||||||
options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
|
options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
|
||||||
options.InputFormatters.Add(new XmlDataContractSerializerInputFormatter());
|
options.InputFormatters.Add(new XmlDataContractSerializerInputFormatter());
|
||||||
|
|
||||||
options.ValidationExcludeFilters.Add(typeFullName: "System.Xml.Linq.XObject");
|
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter("System.Xml.Linq.XObject"));
|
||||||
options.ValidationExcludeFilters.Add(typeFullName: "System.Xml.XmlNode");
|
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter("System.Xml.XmlNode"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Formatting;
|
using System.Net.Http.Formatting;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.OptionsModel;
|
using Microsoft.Extensions.OptionsModel;
|
||||||
|
|
||||||
|
|
@ -32,8 +33,8 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
// Add a formatter to write out an HttpResponseMessage to the response
|
// Add a formatter to write out an HttpResponseMessage to the response
|
||||||
options.OutputFormatters.Insert(0, new HttpResponseMessageOutputFormatter());
|
options.OutputFormatters.Insert(0, new HttpResponseMessageOutputFormatter());
|
||||||
|
|
||||||
options.ValidationExcludeFilters.Add(typeof(HttpRequestMessage));
|
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(HttpRequestMessage)));
|
||||||
options.ValidationExcludeFilters.Add(typeof(HttpResponseMessage));
|
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(HttpResponseMessage)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(WebApiCompatShimOptions options)
|
public void Configure(WebApiCompatShimOptions options)
|
||||||
|
|
|
||||||
|
|
@ -568,6 +568,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool ValidateChildren
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override IReadOnlyList<object> ValidatorMetadata
|
public override IReadOnlyList<object> ValidatorMetadata
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
|
||||||
|
|
@ -2112,7 +2112,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
new IInputFormatter[0],
|
new IInputFormatter[0],
|
||||||
new DefaultControllerActionArgumentBinder(
|
new DefaultControllerActionArgumentBinder(
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider)),
|
new DefaultObjectValidator(metadataProvider)),
|
||||||
new IModelBinder[] { binder.Object },
|
new IModelBinder[] { binder.Object },
|
||||||
new IModelValidatorProvider[0],
|
new IModelValidatorProvider[0],
|
||||||
new IValueProviderFactory[0],
|
new IValueProviderFactory[0],
|
||||||
|
|
|
||||||
|
|
@ -251,7 +251,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
services.Setup(s => s.GetService(typeof(IModelMetadataProvider)))
|
services.Setup(s => s.GetService(typeof(IModelMetadataProvider)))
|
||||||
.Returns(metadataProvider);
|
.Returns(metadataProvider);
|
||||||
services.Setup(s => s.GetService(typeof(IObjectModelValidator)))
|
services.Setup(s => s.GetService(typeof(IObjectModelValidator)))
|
||||||
.Returns(new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider));
|
.Returns(new DefaultObjectValidator(metadataProvider));
|
||||||
return services.Object;
|
return services.Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Xml;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -605,6 +606,103 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
||||||
Assert.False(isReadOnly);
|
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)
|
private void ActionMethod(string input)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
GetCompositeBinder(binders),
|
GetCompositeBinder(binders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new List<IInputFormatter>(),
|
new List<IInputFormatter>(),
|
||||||
new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], modelMetadataProvider),
|
new DefaultObjectValidator(modelMetadataProvider),
|
||||||
validator);
|
validator);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -136,7 +136,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
GetCompositeBinder(binders),
|
GetCompositeBinder(binders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new List<IInputFormatter>(),
|
new List<IInputFormatter>(),
|
||||||
new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider),
|
new DefaultObjectValidator(metadataProvider),
|
||||||
validator);
|
validator);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -221,7 +221,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
GetCompositeBinder(binders),
|
GetCompositeBinder(binders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new List<IInputFormatter>(),
|
new List<IInputFormatter>(),
|
||||||
new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider),
|
new DefaultObjectValidator(metadataProvider),
|
||||||
validator,
|
validator,
|
||||||
includePredicate);
|
includePredicate);
|
||||||
|
|
||||||
|
|
@ -305,7 +305,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
GetCompositeBinder(binders),
|
GetCompositeBinder(binders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new List<IInputFormatter>(),
|
new List<IInputFormatter>(),
|
||||||
new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider),
|
new DefaultObjectValidator(metadataProvider),
|
||||||
validator,
|
validator,
|
||||||
m => m.IncludedProperty,
|
m => m.IncludedProperty,
|
||||||
m => m.MyProperty);
|
m => m.MyProperty);
|
||||||
|
|
@ -357,7 +357,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
GetCompositeBinder(binders),
|
GetCompositeBinder(binders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new List<IInputFormatter>(),
|
new List<IInputFormatter>(),
|
||||||
new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider),
|
new DefaultObjectValidator(metadataProvider),
|
||||||
validator);
|
validator);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -568,9 +568,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
GetCompositeBinder(binders),
|
GetCompositeBinder(binders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new List<IInputFormatter>(),
|
new List<IInputFormatter>(),
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(metadataProvider),
|
||||||
new IExcludeTypeValidationFilter[0],
|
|
||||||
metadataProvider),
|
|
||||||
validator,
|
validator,
|
||||||
includePredicate);
|
includePredicate);
|
||||||
|
|
||||||
|
|
@ -645,9 +643,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
GetCompositeBinder(binders),
|
GetCompositeBinder(binders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new List<IInputFormatter>(),
|
new List<IInputFormatter>(),
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(metadataProvider),
|
||||||
new IExcludeTypeValidationFilter[0],
|
|
||||||
metadataProvider),
|
|
||||||
validator);
|
validator);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -679,9 +675,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
GetCompositeBinder(binder.Object),
|
GetCompositeBinder(binder.Object),
|
||||||
Mock.Of<IValueProvider>(),
|
Mock.Of<IValueProvider>(),
|
||||||
new List<IInputFormatter>(),
|
new List<IInputFormatter>(),
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(metadataProvider),
|
||||||
new IExcludeTypeValidationFilter[0],
|
|
||||||
metadataProvider),
|
|
||||||
Mock.Of<IModelValidatorProvider>(),
|
Mock.Of<IModelValidatorProvider>(),
|
||||||
includePredicate));
|
includePredicate));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Linq;
|
||||||
#if DNXCORE50
|
#if DNXCORE50
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
#endif
|
#endif
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -655,13 +656,40 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
Assert.Equal(ValidationAttributeUtil.GetRequiredErrorMessage("Profession"), error.ErrorMessage);
|
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]
|
[Theory]
|
||||||
[InlineData(new[] { "Foo", "Bar", "Baz" }, typeof(string[]))]
|
[MemberData(nameof(ValidCollectionData))]
|
||||||
[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 Validate_IndexedCollectionTypes_Valid(object model, Type type)
|
public void Validate_IndexedCollectionTypes_Valid(object model, Type type)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -670,7 +698,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
var modelState = actionContext.ModelState;
|
var modelState = actionContext.ModelState;
|
||||||
var validationState = new ValidationStateDictionary();
|
var validationState = new ValidationStateDictionary();
|
||||||
|
|
||||||
var validator = CreateValidator(new SimpleTypesExcludeFilter());
|
var validator = CreateValidator();
|
||||||
|
|
||||||
modelState.Add("items[0]", new ModelStateEntry());
|
modelState.Add("items[0]", new ModelStateEntry());
|
||||||
modelState.Add("items[1]", new ModelStateEntry());
|
modelState.Add("items[1]", new ModelStateEntry());
|
||||||
|
|
@ -712,7 +740,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
var modelState = actionContext.ModelState;
|
var modelState = actionContext.ModelState;
|
||||||
var validationState = new ValidationStateDictionary();
|
var validationState = new ValidationStateDictionary();
|
||||||
|
|
||||||
var validator = CreateValidator(new SimpleTypesExcludeFilter());
|
var validator = CreateValidator();
|
||||||
|
|
||||||
var model = new Dictionary<string, string>()
|
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");
|
AssertKeysEqual(modelState, "items[0].Key", "items[0].Value", "items[1].Key", "items[1].Value");
|
||||||
|
|
||||||
var entry = modelState["items[0].Key"];
|
var entry = modelState["items[0].Key"];
|
||||||
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
||||||
Assert.Empty(entry.Errors);
|
Assert.Empty(entry.Errors);
|
||||||
|
|
||||||
entry = modelState["items[0].Value"];
|
entry = modelState["items[0].Value"];
|
||||||
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
||||||
Assert.Empty(entry.Errors);
|
Assert.Empty(entry.Errors);
|
||||||
|
|
||||||
entry = modelState["items[1].Key"];
|
entry = modelState["items[1].Key"];
|
||||||
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
||||||
Assert.Empty(entry.Errors);
|
Assert.Empty(entry.Errors);
|
||||||
|
|
||||||
entry = modelState["items[1].Value"];
|
entry = modelState["items[1].Value"];
|
||||||
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
||||||
Assert.Empty(entry.Errors);
|
Assert.Empty(entry.Errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -857,7 +885,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Validate_ForExcludedType_PropertiesMarkedAsSkipped()
|
public void Validate_ForExcludedComplexType_PropertiesMarkedAsSkipped()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var validatorProvider = CreateValidatorProvider();
|
var validatorProvider = CreateValidatorProvider();
|
||||||
|
|
@ -890,6 +918,37 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
Assert.Empty(entry.Errors);
|
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()
|
private static IModelValidatorProvider CreateValidatorProvider()
|
||||||
{
|
{
|
||||||
return TestModelValidatorProvider.CreateDefaultProvider();
|
return TestModelValidatorProvider.CreateDefaultProvider();
|
||||||
|
|
@ -897,23 +956,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
|
||||||
private static DefaultObjectValidator CreateValidator(Type excludedType)
|
private static DefaultObjectValidator CreateValidator(Type excludedType)
|
||||||
{
|
{
|
||||||
var excludeFilters = new List<IExcludeTypeValidationFilter>();
|
var excludeFilters = new List<ValidationExcludeFilter>();
|
||||||
if (excludedType != null)
|
if (excludedType != null)
|
||||||
{
|
{
|
||||||
var excludeFilter = new Mock<IExcludeTypeValidationFilter>();
|
excludeFilters.Add(new ValidationExcludeFilter(excludedType));
|
||||||
excludeFilter
|
|
||||||
.Setup(o => o.IsTypeExcluded(It.IsAny<Type>()))
|
|
||||||
.Returns<Type>(t => t.IsAssignableFrom(excludedType));
|
|
||||||
|
|
||||||
excludeFilters.Add(excludeFilter.Object);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
private static void AssertKeysEqual(ModelStateDictionary modelState, params string[] keys)
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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>)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -129,14 +129,12 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[low].Key").Value;
|
var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[low].Key").Value;
|
||||||
Assert.Equal("key0", entry.AttemptedValue);
|
Assert.Equal("key0", entry.AttemptedValue);
|
||||||
Assert.Equal("key0", entry.RawValue);
|
Assert.Equal("key0", entry.RawValue);
|
||||||
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
||||||
// Key and Value are skipped if they have simple types.
|
|
||||||
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
|
|
||||||
|
|
||||||
entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[low].Value").Value;
|
entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[low].Value").Value;
|
||||||
Assert.Equal("10", entry.AttemptedValue);
|
Assert.Equal("10", entry.AttemptedValue);
|
||||||
Assert.Equal("10", entry.RawValue);
|
Assert.Equal("10", entry.RawValue);
|
||||||
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
|
||||||
|
|
@ -104,9 +104,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
binding.ModelBindingMessageProvider.MissingKeyOrValueAccessor = () => $"Hurts when nothing is provided.";
|
binding.ModelBindingMessageProvider.MissingKeyOrValueAccessor = () => $"Hurts when nothing is provided.";
|
||||||
}));
|
}));
|
||||||
|
|
||||||
var argumentBinder = new DefaultControllerActionArgumentBinder(
|
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(metadataProvider);
|
||||||
metadataProvider,
|
|
||||||
ModelBindingTestHelper.GetObjectValidator());
|
|
||||||
var parameter = new ParameterDescriptor
|
var parameter = new ParameterDescriptor
|
||||||
{
|
{
|
||||||
Name = "parameter",
|
Name = "parameter",
|
||||||
|
|
@ -188,9 +186,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
// A real details provider could customize message based on BindingMetadataProviderContext.
|
// A real details provider could customize message based on BindingMetadataProviderContext.
|
||||||
binding.ModelBindingMessageProvider.MissingKeyOrValueAccessor = () => $"Hurts when nothing is provided.";
|
binding.ModelBindingMessageProvider.MissingKeyOrValueAccessor = () => $"Hurts when nothing is provided.";
|
||||||
}));
|
}));
|
||||||
var argumentBinder = new DefaultControllerActionArgumentBinder(
|
|
||||||
metadataProvider,
|
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(metadataProvider);
|
||||||
ModelBindingTestHelper.GetObjectValidator());
|
|
||||||
|
|
||||||
var parameter = new ParameterDescriptor
|
var parameter = new ParameterDescriptor
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -42,20 +42,28 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
|
|
||||||
public static DefaultControllerActionArgumentBinder GetArgumentBinder(MvcOptions options = null)
|
public static DefaultControllerActionArgumentBinder GetArgumentBinder(MvcOptions options = null)
|
||||||
{
|
{
|
||||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
if (options == null)
|
||||||
return new DefaultControllerActionArgumentBinder(
|
{
|
||||||
metadataProvider,
|
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
GetObjectValidator(options));
|
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;
|
return new DefaultControllerActionArgumentBinder(
|
||||||
options.MaxModelValidationErrors = 5;
|
metadataProvider,
|
||||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
GetObjectValidator(metadataProvider));
|
||||||
return new DefaultObjectValidator(
|
}
|
||||||
options.ValidationExcludeFilters,
|
|
||||||
metadataProvider);
|
public static IObjectModelValidator GetObjectValidator(IModelMetadataProvider metadataProvider)
|
||||||
|
{
|
||||||
|
return new DefaultObjectValidator(metadataProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpContext GetHttpContext(
|
private static HttpContext GetHttpContext(
|
||||||
|
|
|
||||||
|
|
@ -1424,9 +1424,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
binding.ModelBindingMessageProvider.MissingBindRequiredValueAccessor =
|
binding.ModelBindingMessageProvider.MissingBindRequiredValueAccessor =
|
||||||
name => $"Hurts when '{ name }' is not provided.";
|
name => $"Hurts when '{ name }' is not provided.";
|
||||||
}));
|
}));
|
||||||
var argumentBinder = new DefaultControllerActionArgumentBinder(
|
|
||||||
metadataProvider,
|
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(metadataProvider);
|
||||||
ModelBindingTestHelper.GetObjectValidator());
|
|
||||||
var parameter = new ParameterDescriptor()
|
var parameter = new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
Name = "parameter",
|
Name = "parameter",
|
||||||
|
|
|
||||||
|
|
@ -312,9 +312,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
binding.ModelBindingMessageProvider.ValueMustNotBeNullAccessor =
|
binding.ModelBindingMessageProvider.ValueMustNotBeNullAccessor =
|
||||||
value => $"Hurts when '{ value }' is provided.";
|
value => $"Hurts when '{ value }' is provided.";
|
||||||
}));
|
}));
|
||||||
var argumentBinder = new DefaultControllerActionArgumentBinder(
|
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(metadataProvider);
|
||||||
metadataProvider,
|
|
||||||
ModelBindingTestHelper.GetObjectValidator());
|
|
||||||
var parameter = new ParameterDescriptor
|
var parameter = new ParameterDescriptor
|
||||||
{
|
{
|
||||||
Name = "Parameter1",
|
Name = "Parameter1",
|
||||||
|
|
|
||||||
|
|
@ -1109,7 +1109,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
operationContext.ModelBinder,
|
operationContext.ModelBinder,
|
||||||
operationContext.ValueProvider,
|
operationContext.ValueProvider,
|
||||||
operationContext.InputFormatters,
|
operationContext.InputFormatters,
|
||||||
ModelBindingTestHelper.GetObjectValidator(),
|
ModelBindingTestHelper.GetObjectValidator(operationContext.MetadataProvider),
|
||||||
operationContext.ValidatorProvider);
|
operationContext.ValidatorProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Mvc.Abstractions;
|
using Microsoft.AspNet.Mvc.Abstractions;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.IntegrationTests
|
namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
|
|
@ -1129,7 +1131,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
},
|
},
|
||||||
options =>
|
options =>
|
||||||
{
|
{
|
||||||
options.ValidationExcludeFilters.Add(typeof(Address));
|
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(Address)));
|
||||||
testOptions = options;
|
testOptions = options;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1167,6 +1169,55 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
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)
|
private static void AssertRequiredError(string key, ModelError error)
|
||||||
{
|
{
|
||||||
// Mono issue - https://github.com/aspnet/External/issues/19
|
// Mono issue - https://github.com/aspnet/External/issues/19
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,9 @@ using Moq;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Threading;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
|
|
@ -134,7 +137,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Setup_SetsUpExcludeFromValidationDelegates()
|
public void Setup_SetsUpMetadataDetailsProviders()
|
||||||
{
|
{
|
||||||
// Arrange & Act
|
// Arrange & Act
|
||||||
var options = GetOptions<MvcOptions>(services =>
|
var options = GetOptions<MvcOptions>(services =>
|
||||||
|
|
@ -144,48 +147,40 @@ namespace Microsoft.AspNet.Mvc
|
||||||
});
|
});
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(8, options.ValidationExcludeFilters.Count);
|
var providers = options.ModelMetadataDetailsProviders;
|
||||||
|
Assert.Equal(12, providers.Count);
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
// Verify if the delegates registered by default exclude the given types.
|
Assert.IsType<DefaultBindingMetadataProvider>(providers[i++]);
|
||||||
Assert.IsType(typeof(SimpleTypesExcludeFilter), options.ValidationExcludeFilters[i++]);
|
Assert.IsType<DefaultValidationMetadataProvider>(providers[i++]);
|
||||||
|
|
||||||
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), options.ValidationExcludeFilters[i]);
|
var excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
|
||||||
var typeFilter
|
Assert.Equal(typeof(Type), excludeFilter.Type);
|
||||||
= Assert.IsType<DefaultTypeBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
|
|
||||||
Assert.Equal(typeFilter.ExcludedType, typeof(Type));
|
|
||||||
|
|
||||||
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), options.ValidationExcludeFilters[i]);
|
excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
|
||||||
var cancellationTokenFilter
|
Assert.Equal(typeof(Uri), excludeFilter.Type);
|
||||||
= Assert.IsType<DefaultTypeBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
|
|
||||||
Assert.Equal(cancellationTokenFilter.ExcludedType, typeof(System.Threading.CancellationToken));
|
|
||||||
|
|
||||||
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), options.ValidationExcludeFilters[i]);
|
excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
|
||||||
var formFileFilter
|
Assert.Equal(typeof(CancellationToken), excludeFilter.Type);
|
||||||
= Assert.IsType<DefaultTypeBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
|
|
||||||
Assert.Equal(formFileFilter.ExcludedType, typeof(Http.IFormFile));
|
|
||||||
|
|
||||||
Assert.IsType(
|
excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
|
||||||
typeof(DefaultTypeBasedExcludeFilter),
|
Assert.Equal(typeof(IFormFile), excludeFilter.Type);
|
||||||
options.ValidationExcludeFilters[i]);
|
|
||||||
var formCollectionFilter
|
|
||||||
= Assert.IsType<DefaultTypeBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
|
|
||||||
Assert.Equal(formCollectionFilter.ExcludedType, typeof(Http.IFormCollection));
|
|
||||||
|
|
||||||
Assert.IsType(typeof(DefaultTypeBasedExcludeFilter), options.ValidationExcludeFilters[i]);
|
excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
|
||||||
var jTokenFilter
|
Assert.Equal(typeof(IFormCollection), excludeFilter.Type);
|
||||||
= Assert.IsType<DefaultTypeBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
|
|
||||||
Assert.Equal(jTokenFilter.ExcludedType, typeof(JToken));
|
|
||||||
|
|
||||||
Assert.IsType(typeof(DefaultTypeNameBasedExcludeFilter), options.ValidationExcludeFilters[i]);
|
Assert.IsType<DataAnnotationsMetadataProvider>(providers[i++]);
|
||||||
var xObjectFilter
|
|
||||||
= Assert.IsType<DefaultTypeNameBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
|
|
||||||
Assert.Equal(xObjectFilter.ExcludedTypeName, typeof(XObject).FullName);
|
|
||||||
|
|
||||||
Assert.IsType(typeof(DefaultTypeNameBasedExcludeFilter), options.ValidationExcludeFilters[i]);
|
excludeFilter = Assert.IsType<ValidationExcludeFilter>(providers[i++]);
|
||||||
var xmlNodeFilter =
|
Assert.Equal(typeof(JToken), excludeFilter.Type);
|
||||||
Assert.IsType<DefaultTypeNameBasedExcludeFilter>(options.ValidationExcludeFilters[i++]);
|
|
||||||
Assert.Equal(xmlNodeFilter.ExcludedTypeName, "System.Xml.XmlNode");
|
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]
|
[Fact]
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,34 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
return new DefaultModelMetadataProvider(compositeDetailsProvider);
|
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;
|
private readonly TestModelMetadataDetailsProvider _detailsProvider;
|
||||||
|
|
||||||
public TestModelMetadataProvider()
|
public TestModelMetadataProvider()
|
||||||
|
|
|
||||||
|
|
@ -1873,7 +1873,7 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
{
|
{
|
||||||
ControllerContext = controllerContext,
|
ControllerContext = controllerContext,
|
||||||
MetadataProvider = metadataProvider,
|
MetadataProvider = metadataProvider,
|
||||||
ObjectValidator = new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider),
|
ObjectValidator = new DefaultObjectValidator(metadataProvider),
|
||||||
TempData = tempData,
|
TempData = tempData,
|
||||||
ViewData = viewData,
|
ViewData = viewData,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace FormatterWebSite
|
namespace FormatterWebSite
|
||||||
|
|
@ -13,8 +14,8 @@ namespace FormatterWebSite
|
||||||
{
|
{
|
||||||
services.AddMvc(options =>
|
services.AddMvc(options =>
|
||||||
{
|
{
|
||||||
options.ValidationExcludeFilters.Add(typeof(Developer));
|
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(Developer)));
|
||||||
options.ValidationExcludeFilters.Add(typeof(Supplier));
|
options.ModelMetadataDetailsProviders.Add(new ValidationExcludeFilter(typeof(Supplier)));
|
||||||
|
|
||||||
options.InputFormatters.Add(new StringInputFormatter());
|
options.InputFormatters.Add(new StringInputFormatter());
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// 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 Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using ModelBindingWebSite.Models;
|
using ModelBindingWebSite.Models;
|
||||||
using ModelBindingWebSite.Services;
|
using ModelBindingWebSite.Services;
|
||||||
|
|
@ -20,7 +21,7 @@ namespace ModelBindingWebSite
|
||||||
m.MaxModelValidationErrors = 8;
|
m.MaxModelValidationErrors = 8;
|
||||||
m.ModelBinders.Insert(0, new TestBindingSourceModelBinder());
|
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.
|
// ModelMetadataController relies on additional values AdditionalValuesMetadataProvider provides.
|
||||||
m.ModelMetadataDetailsProviders.Add(new AdditionalValuesMetadataProvider());
|
m.ModelMetadataDetailsProviders.Add(new AdditionalValuesMetadataProvider());
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue