Fixes 2304:
The fix splits client validation and model validation into two separate hierarchies. Introduced ClientModelValidatorProvider in MvcOptions, which can be iterated to produce IClientModelValidators. As a result of this, HtmlGenerator code can be free of ActionBindingContext and directly consumes options. This also means that we do not modify the client validations during resource filters.
This commit is contained in:
parent
eaa58bc913
commit
c67236141b
|
|
@ -32,6 +32,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
ValidationExcludeFilters = new List<IExcludeTypeValidationFilter>();
|
||||
ModelMetadataDetailsProviders = new List<IMetadataDetailsProvider>();
|
||||
ModelValidatorProviders = new List<IModelValidatorProvider>();
|
||||
ClientModelValidatorProviders = new List<IClientModelValidatorProvider>();
|
||||
CacheProfiles = new Dictionary<string, CacheProfile>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +115,11 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// </summary>
|
||||
public IList<IModelValidatorProvider> ModelValidatorProviders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IClientModelValidatorProvider"/> instances.
|
||||
/// </summary>
|
||||
public IList<IClientModelValidatorProvider> ClientModelValidatorProviders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of descriptors that represent <see cref="Rendering.IViewEngine"/> used
|
||||
/// by this application.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ using Microsoft.AspNet.Mvc.ModelBinding;
|
|||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNet.Mvc.Rendering.Expressions;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using Microsoft.Framework.WebEncoders;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Rendering
|
||||
|
|
@ -25,7 +26,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
typeof(DefaultHtmlGenerator).GetTypeInfo().GetDeclaredMethod(nameof(ConvertEnumFromString));
|
||||
|
||||
private readonly AntiForgery _antiForgery;
|
||||
private readonly IScopedInstance<ActionBindingContext> _bindingContextAccessor;
|
||||
private readonly IClientModelValidatorProvider _clientModelValidatorProvider;
|
||||
private readonly IModelMetadataProvider _metadataProvider;
|
||||
private readonly IUrlHelper _urlHelper;
|
||||
private readonly IHtmlEncoder _htmlEncoder;
|
||||
|
|
@ -33,15 +34,22 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DefaultHtmlGenerator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="antiForgery">The <see cref="AntiForgery"/> instance which is used to generate anti-forgery
|
||||
/// tokens.</param>
|
||||
/// <param name="optionsAccessor">The accessor for <see cref="MvcOptions"/>.</param>
|
||||
/// <param name="metadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
|
||||
/// <param name="urlHelper">The <see cref="IUrlHelper"/>.</param>
|
||||
/// <param name="htmlEncoder">The <see cref="IHtmlEncoder"/>.</param>
|
||||
public DefaultHtmlGenerator(
|
||||
[NotNull] AntiForgery antiForgery,
|
||||
[NotNull] IScopedInstance<ActionBindingContext> bindingContextAccessor,
|
||||
[NotNull] IOptions<MvcOptions> optionsAccessor,
|
||||
[NotNull] IModelMetadataProvider metadataProvider,
|
||||
[NotNull] IUrlHelper urlHelper,
|
||||
[NotNull] IHtmlEncoder htmlEncoder)
|
||||
{
|
||||
_antiForgery = antiForgery;
|
||||
_bindingContextAccessor = bindingContextAccessor;
|
||||
var clientValidatorProviders = optionsAccessor.Options.ClientModelValidatorProviders;
|
||||
_clientModelValidatorProvider = new CompositeClientModelValidatorProvider(clientValidatorProviders);
|
||||
_metadataProvider = metadataProvider;
|
||||
_urlHelper = urlHelper;
|
||||
_htmlEncoder = htmlEncoder;
|
||||
|
|
@ -715,7 +723,6 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
ModelExplorer modelExplorer,
|
||||
string expression)
|
||||
{
|
||||
var validatorProvider = _bindingContextAccessor.Value.ValidatorProvider;
|
||||
modelExplorer = modelExplorer ??
|
||||
ExpressionMetadataProvider.FromStringExpression(expression, viewContext.ViewData, _metadataProvider);
|
||||
var validationContext = new ClientModelValidationContext(
|
||||
|
|
@ -723,15 +730,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
_metadataProvider,
|
||||
viewContext.HttpContext.RequestServices);
|
||||
|
||||
var validatorProviderContext = new ModelValidatorProviderContext(modelExplorer.Metadata);
|
||||
validatorProvider.GetValidators(validatorProviderContext);
|
||||
var validatorProviderContext = new ClientValidatorProviderContext(modelExplorer.Metadata);
|
||||
_clientModelValidatorProvider.GetValidators(validatorProviderContext);
|
||||
|
||||
var validators = validatorProviderContext.Validators;
|
||||
|
||||
return
|
||||
validators
|
||||
.OfType<IClientModelValidator>()
|
||||
.SelectMany(v => v.GetClientValidationRules(validationContext));
|
||||
return validators.SelectMany(v => v.GetClientValidationRules(validationContext));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -213,7 +213,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
{
|
||||
foreach (var attribute in context.Attributes.OfType<ValidationAttribute>())
|
||||
{
|
||||
context.ValidationMetadata.ValiatorMetadata.Add(attribute);
|
||||
// If another provider has already added this attribute, do not repeat it.
|
||||
// This will prevent attributes like RemoteAttribute (which implement ValidationAttribute and
|
||||
// IClientModelValidator) to be added to the ValidationMetadata twice.
|
||||
// This is to ensure we do not end up with duplication validation rules on the client side.
|
||||
if (!context.ValidationMetadata.ValidatorMetadata.Contains(attribute))
|
||||
{
|
||||
context.ValidationMetadata.ValidatorMetadata.Add(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -405,7 +405,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
{
|
||||
if (_validatorMetadata == null)
|
||||
{
|
||||
_validatorMetadata = new ReadOnlyCollection<object>(ValidationMetadata.ValiatorMetadata);
|
||||
_validatorMetadata = new ReadOnlyCollection<object>(ValidationMetadata.ValidatorMetadata);
|
||||
}
|
||||
|
||||
return _validatorMetadata;
|
||||
|
|
|
|||
|
|
@ -15,9 +15,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
/// <inheritdoc />
|
||||
public void GetValidationMetadata([NotNull] ValidationMetadataProviderContext context)
|
||||
{
|
||||
foreach (var attribute in context.Attributes.OfType<IModelValidator>())
|
||||
foreach (var attribute in context.Attributes)
|
||||
{
|
||||
context.ValidationMetadata.ValiatorMetadata.Add(attribute);
|
||||
if (attribute is IModelValidator || attribute is IClientModelValidator)
|
||||
{
|
||||
// If another provider has already added this attribute, do not repeat it.
|
||||
// This will prevent attributes like RemoteAttribute (which implement ValidationAttribute and
|
||||
// IClientModelValidator) to be added to the ValidationMetadata twice.
|
||||
// This is to ensure we do not end up with duplication validation rules on the client side.
|
||||
if (!context.ValidationMetadata.ValidatorMetadata.Contains(attribute))
|
||||
{
|
||||
context.ValidationMetadata.ValidatorMetadata.Add(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
/// <see cref="IValidationMetadataProvider"/> implementations should store metadata items
|
||||
/// in this list, to be consumed later by an <see cref="Validation.IModelValidatorProvider"/>.
|
||||
/// </remarks>
|
||||
public IList<object> ValiatorMetadata { get; } = new List<object>();
|
||||
public IList<object> ValidatorMetadata { get; } = new List<object>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// A context for <see cref="IClientModelValidatorProvider"/>.
|
||||
/// </summary>
|
||||
public class ClientValidatorProviderContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ClientValidatorProviderContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="modelMetadata">The <see cref="ModelBinding.ModelMetadata"/> for the model being validated.
|
||||
/// </param>
|
||||
public ClientValidatorProviderContext(ModelMetadata modelMetadata)
|
||||
{
|
||||
ModelMetadata = modelMetadata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ModelBinding.ModelMetadata"/>.
|
||||
/// </summary>
|
||||
public ModelMetadata ModelMetadata { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the validator metadata.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property provides convenience access to <see cref="ModelMetadata.ValidatorMetadata"/>.
|
||||
/// </remarks>
|
||||
public IReadOnlyList<object> ValidatorMetadata
|
||||
{
|
||||
get
|
||||
{
|
||||
return ModelMetadata.ValidatorMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of <see cref="IClientModelValidator"/> instances. <see cref="IClientModelValidatorProvider"/>
|
||||
/// instances should add validators to this list when
|
||||
/// <see cref="IClientModelValidatorProvider.GetValidators(ClientValidatorProviderContext)()"/>
|
||||
/// is called.
|
||||
/// </summary>
|
||||
public IList<IClientModelValidator> Validators { get; } = new List<IClientModelValidator>();
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ using Microsoft.Framework.Internal;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class CompareAttributeAdapter : DataAnnotationsModelValidator<CompareAttribute>
|
||||
public class CompareAttributeAdapter : DataAnnotationsClientModelValidator<CompareAttribute>
|
||||
{
|
||||
public CompareAttributeAdapter([NotNull] CompareAttribute attribute)
|
||||
: base(new CompareAttributeWrapper(attribute))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// Aggregate of <see cref="IClientModelValidatorProvider"/>s that delegates to its underlying providers.
|
||||
/// </summary>
|
||||
public class CompositeClientModelValidatorProvider : IClientModelValidatorProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="CompositeClientModelValidatorProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="providers">
|
||||
/// A collection of <see cref="IClientModelValidatorProvider"/> instances.
|
||||
/// </param>
|
||||
public CompositeClientModelValidatorProvider([NotNull] IEnumerable<IClientModelValidatorProvider> providers)
|
||||
{
|
||||
ValidatorProviders = new List<IClientModelValidatorProvider>(providers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IClientModelValidatorProvider"/>.
|
||||
/// </summary>
|
||||
public IReadOnlyList<IClientModelValidatorProvider> ValidatorProviders { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void GetValidators(ClientValidatorProviderContext context)
|
||||
{
|
||||
foreach (var validatorProvider in ValidatorProviders)
|
||||
{
|
||||
validatorProvider.GetValidators(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,9 +7,9 @@ using Microsoft.Framework.Internal;
|
|||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation for <see cref="ICompositeModelValidatorProvider"/>.
|
||||
/// Aggregate of <see cref="IModelValidatorProvider"/>s that delegates to its underlying providers.
|
||||
/// </summary>
|
||||
public class CompositeModelValidatorProvider : ICompositeModelValidatorProvider
|
||||
public class CompositeModelValidatorProvider : IModelValidatorProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="CompositeModelValidatorProvider"/>.
|
||||
|
|
@ -22,6 +22,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
ValidatorProviders = new List<IModelValidatorProvider>(providers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of <see cref="IModelValidatorProvider"/>.
|
||||
/// </summary>
|
||||
public IReadOnlyList<IModelValidatorProvider> ValidatorProviders { get; }
|
||||
|
||||
public void GetValidators(ModelValidatorProviderContext context)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.ComponentModel.DataAnnotations;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// An implementation of <see cref="IClientModelValidator"/> which understands data annotation attributes.
|
||||
/// </summary>
|
||||
/// <typeparam name="TAttribute">The type of the attribute.</typeparam>
|
||||
public abstract class DataAnnotationsClientModelValidator<TAttribute> : IClientModelValidator
|
||||
where TAttribute : ValidationAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new instance of <see cref="DataAnnotationsClientModelValidator{TAttribute}"/>.
|
||||
/// </summary>
|
||||
/// <param name="attribute">The <typeparamref name="TAttribute"/> instance to validate.</param>
|
||||
public DataAnnotationsClientModelValidator(TAttribute attribute)
|
||||
{
|
||||
Attribute = attribute;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the <typeparamref name="TAttribute"/> associated with this instance.
|
||||
/// </summary>
|
||||
public TAttribute Attribute
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ClientModelValidationContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message formatted using the <see cref="Attribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="modelMetadata">The <see cref="ModelMetadata"/> associated with the model annotated with
|
||||
/// <see cref="Attribute"/>.</param>
|
||||
/// <returns>Formatted error string.</returns>
|
||||
protected virtual string GetErrorMessage([NotNull] ModelMetadata modelMetadata)
|
||||
{
|
||||
return Attribute.FormatErrorMessage(modelMetadata.GetDisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// An implementation of <see cref="IClientModelValidatorProvider"/> which provides client validators
|
||||
/// for attributes which derive from <see cref="ValidationAttribute"/>. It also provides
|
||||
/// a validator for types which implement <see cref="IClientModelValidator"/>.
|
||||
/// The logic to support <see cref="IClientModelValidator"/>
|
||||
/// is implemented in <see cref="DataAnnotationsClientModelValidator{}"/>.
|
||||
/// </summary>
|
||||
public class DataAnnotationsClientModelValidatorProvider : IClientModelValidatorProvider
|
||||
{
|
||||
// A factory for validators based on ValidationAttribute.
|
||||
internal delegate IClientModelValidator
|
||||
DataAnnotationsClientModelValidationFactory(ValidationAttribute attribute);
|
||||
|
||||
private readonly Dictionary<Type, DataAnnotationsClientModelValidationFactory> _attributeFactories =
|
||||
BuildAttributeFactoriesDictionary();
|
||||
|
||||
internal Dictionary<Type, DataAnnotationsClientModelValidationFactory> AttributeFactories
|
||||
{
|
||||
get { return _attributeFactories; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void GetValidators(ClientValidatorProviderContext context)
|
||||
{
|
||||
foreach (var attribute in context.ValidatorMetadata.OfType<ValidationAttribute>())
|
||||
{
|
||||
DataAnnotationsClientModelValidationFactory factory;
|
||||
if (_attributeFactories.TryGetValue(attribute.GetType(), out factory))
|
||||
{
|
||||
context.Validators.Add(factory(attribute));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<Type, DataAnnotationsClientModelValidationFactory> BuildAttributeFactoriesDictionary()
|
||||
{
|
||||
return new Dictionary<Type, DataAnnotationsClientModelValidationFactory>()
|
||||
{
|
||||
{
|
||||
typeof(RegularExpressionAttribute),
|
||||
(attribute) => new RegularExpressionAttributeAdapter((RegularExpressionAttribute)attribute)
|
||||
},
|
||||
{
|
||||
typeof(MaxLengthAttribute),
|
||||
(attribute) => new MaxLengthAttributeAdapter((MaxLengthAttribute)attribute)
|
||||
},
|
||||
{
|
||||
typeof(MinLengthAttribute),
|
||||
(attribute) => new MinLengthAttributeAdapter((MinLengthAttribute)attribute)
|
||||
},
|
||||
{
|
||||
typeof(CompareAttribute),
|
||||
(attribute) => new CompareAttributeAdapter((CompareAttribute)attribute)
|
||||
},
|
||||
{
|
||||
typeof(RequiredAttribute),
|
||||
(attribute) => new RequiredAttributeAdapter((RequiredAttribute)attribute)
|
||||
},
|
||||
{
|
||||
typeof(RangeAttribute),
|
||||
(attribute) => new RangeAttributeAdapter((RangeAttribute)attribute)
|
||||
},
|
||||
{
|
||||
typeof(StringLengthAttribute),
|
||||
(attribute) => new StringLengthAttributeAdapter((StringLengthAttribute)attribute)
|
||||
},
|
||||
{
|
||||
typeof(CreditCardAttribute),
|
||||
(attribute) => new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "creditcard")
|
||||
},
|
||||
{
|
||||
typeof(EmailAddressAttribute),
|
||||
(attribute) => new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "email")
|
||||
},
|
||||
{
|
||||
typeof(PhoneAttribute),
|
||||
(attribute) => new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "phone")
|
||||
},
|
||||
{
|
||||
typeof(UrlAttribute),
|
||||
(attribute) => new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "url")
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ using Microsoft.Framework.Internal;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class DataAnnotationsModelValidator : IModelValidator, IClientModelValidator
|
||||
public class DataAnnotationsModelValidator : IModelValidator
|
||||
{
|
||||
public DataAnnotationsModelValidator([NotNull] ValidationAttribute attribute)
|
||||
{
|
||||
|
|
@ -61,22 +61,5 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
|
||||
return Enumerable.Empty<ModelValidationResult>();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
[NotNull] ClientModelValidationContext context)
|
||||
{
|
||||
var customValidator = Attribute as IClientModelValidator;
|
||||
if (customValidator != null)
|
||||
{
|
||||
return customValidator.GetClientValidationRules(context);
|
||||
}
|
||||
|
||||
return Enumerable.Empty<ModelClientValidationRule>();
|
||||
}
|
||||
|
||||
protected virtual string GetErrorMessage([NotNull] ModelMetadata modelMetadata)
|
||||
{
|
||||
return Attribute.FormatErrorMessage(modelMetadata.GetDisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class DataAnnotationsModelValidator<TAttribute> : DataAnnotationsModelValidator
|
||||
where TAttribute : ValidationAttribute
|
||||
{
|
||||
public DataAnnotationsModelValidator(TAttribute attribute)
|
||||
: base(attribute)
|
||||
{
|
||||
}
|
||||
|
||||
protected new TAttribute Attribute
|
||||
{
|
||||
get { return (TAttribute)base.Attribute; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,114 +5,29 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// An implementation of <see cref="IModelValidatorProvider"/> which providers validators
|
||||
/// An implementation of <see cref="IModelValidatorProvider"/> which provides validators
|
||||
/// for attributes which derive from <see cref="ValidationAttribute"/>. It also provides
|
||||
/// a validator for types which implement <see cref="IValidatableObject"/>. To support
|
||||
/// client side validation, you can either register adapters through the static methods
|
||||
/// on this class, or by having your validation attributes implement
|
||||
/// <see cref="IClientModelValidator"/>. The logic to support <see cref="IClientModelValidator"/>
|
||||
/// is implemented in <see cref="DataAnnotationsModelValidator"/>.
|
||||
/// a validator for types which implement <see cref="IValidatableObject"/>.
|
||||
/// </summary>
|
||||
public class DataAnnotationsModelValidatorProvider : IModelValidatorProvider
|
||||
{
|
||||
// A factory for validators based on ValidationAttribute.
|
||||
internal delegate IModelValidator DataAnnotationsModelValidationFactory(ValidationAttribute attribute);
|
||||
|
||||
// Factories for validation attributes
|
||||
private static readonly DataAnnotationsModelValidationFactory _defaultAttributeFactory =
|
||||
(attribute) => new DataAnnotationsModelValidator(attribute);
|
||||
|
||||
// Factories for IValidatableObject models
|
||||
private static readonly DataAnnotationsValidatableObjectAdapterFactory _defaultValidatableFactory =
|
||||
() => new ValidatableObjectAdapter();
|
||||
|
||||
private readonly Dictionary<Type, DataAnnotationsModelValidationFactory> _attributeFactories =
|
||||
BuildAttributeFactoriesDictionary();
|
||||
|
||||
// A factory for validators based on IValidatableObject
|
||||
private delegate IModelValidator DataAnnotationsValidatableObjectAdapterFactory();
|
||||
|
||||
internal Dictionary<Type, DataAnnotationsModelValidationFactory> AttributeFactories
|
||||
{
|
||||
get { return _attributeFactories; }
|
||||
}
|
||||
|
||||
public void GetValidators(ModelValidatorProviderContext context)
|
||||
{
|
||||
foreach (var attribute in context.ValidatorMetadata.OfType<ValidationAttribute>())
|
||||
{
|
||||
DataAnnotationsModelValidationFactory factory;
|
||||
if (!_attributeFactories.TryGetValue(attribute.GetType(), out factory))
|
||||
{
|
||||
factory = _defaultAttributeFactory;
|
||||
}
|
||||
|
||||
context.Validators.Add(factory(attribute));
|
||||
context.Validators.Add(new DataAnnotationsModelValidator(attribute));
|
||||
}
|
||||
|
||||
// Produce a validator if the type supports IValidatableObject
|
||||
if (typeof(IValidatableObject).IsAssignableFrom(context.ModelMetadata.ModelType))
|
||||
{
|
||||
context.Validators.Add(_defaultValidatableFactory());
|
||||
context.Validators.Add(new ValidatableObjectAdapter());
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<Type, DataAnnotationsModelValidationFactory> BuildAttributeFactoriesDictionary()
|
||||
{
|
||||
var dict = new Dictionary<Type, DataAnnotationsModelValidationFactory>();
|
||||
AddValidationAttributeAdapter(dict, typeof(RegularExpressionAttribute),
|
||||
(attribute) => new RegularExpressionAttributeAdapter((RegularExpressionAttribute)attribute));
|
||||
|
||||
AddValidationAttributeAdapter(dict, typeof(MaxLengthAttribute),
|
||||
(attribute) => new MaxLengthAttributeAdapter((MaxLengthAttribute)attribute));
|
||||
|
||||
AddValidationAttributeAdapter(dict, typeof(MinLengthAttribute),
|
||||
(attribute) => new MinLengthAttributeAdapter((MinLengthAttribute)attribute));
|
||||
|
||||
AddValidationAttributeAdapter(dict, typeof(CompareAttribute),
|
||||
(attribute) => new CompareAttributeAdapter((CompareAttribute)attribute));
|
||||
|
||||
AddValidationAttributeAdapter(dict, typeof(RequiredAttribute),
|
||||
(attribute) => new RequiredAttributeAdapter((RequiredAttribute)attribute));
|
||||
|
||||
AddValidationAttributeAdapter(dict, typeof(RangeAttribute),
|
||||
(attribute) => new RangeAttributeAdapter((RangeAttribute)attribute));
|
||||
|
||||
AddValidationAttributeAdapter(dict, typeof(StringLengthAttribute),
|
||||
(attribute) => new StringLengthAttributeAdapter((StringLengthAttribute)attribute));
|
||||
|
||||
AddDataTypeAttributeAdapter(dict, typeof(CreditCardAttribute), "creditcard");
|
||||
AddDataTypeAttributeAdapter(dict, typeof(EmailAddressAttribute), "email");
|
||||
AddDataTypeAttributeAdapter(dict, typeof(PhoneAttribute), "phone");
|
||||
AddDataTypeAttributeAdapter(dict, typeof(UrlAttribute), "url");
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
private static void AddValidationAttributeAdapter(
|
||||
Dictionary<Type, DataAnnotationsModelValidationFactory> dictionary,
|
||||
Type validationAttributeType,
|
||||
DataAnnotationsModelValidationFactory factory)
|
||||
{
|
||||
if (validationAttributeType != null)
|
||||
{
|
||||
dictionary.Add(validationAttributeType, factory);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddDataTypeAttributeAdapter(
|
||||
Dictionary<Type, DataAnnotationsModelValidationFactory> dictionary,
|
||||
Type attributeType,
|
||||
string ruleName)
|
||||
{
|
||||
AddValidationAttributeAdapter(
|
||||
dictionary,
|
||||
attributeType,
|
||||
(attribute) => new DataTypeAttributeAdapter((DataTypeAttribute)attribute, ruleName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
/// A validation adapter that is used to map <see cref="DataTypeAttribute"/>'s to a single client side validation
|
||||
/// rule.
|
||||
/// </summary>
|
||||
public class DataTypeAttributeAdapter : DataAnnotationsModelValidator
|
||||
public class DataTypeAttributeAdapter : DataAnnotationsClientModelValidator<DataTypeAttribute>
|
||||
{
|
||||
public DataTypeAttributeAdapter(DataTypeAttribute attribute,
|
||||
[NotNull] string ruleName)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// A default implementation of <see cref="IClientModelValidatorProvider"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <see cref="DefaultClientModelValidatorProvider"/> provides validators from
|
||||
/// <see cref="IClientModelValidator"/> instances in <see cref="ModelMetadata.ValidatorMetadata"/>.
|
||||
/// </remarks>
|
||||
public class DefaultClientModelValidatorProvider : IClientModelValidatorProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void GetValidators(ClientValidatorProviderContext context)
|
||||
{
|
||||
foreach (var metadata in context.ValidatorMetadata)
|
||||
{
|
||||
var validator = metadata as IClientModelValidator;
|
||||
if (validator != null)
|
||||
{
|
||||
context.Validators.Add(validator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a collection of <see cref="IClientModelValidator"/>.
|
||||
/// </summary>
|
||||
public interface IClientModelValidatorProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets set of <see cref="IClientModelValidator"/>s
|
||||
/// by updating <see cref="ClientValidatorProviderContext.Validators"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="ClientModelValidationContext"/> associated with this call.</param>
|
||||
void GetValidators(ClientValidatorProviderContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents aggregate of <see cref="IModelValidatorProvider"/>s that delegates to its underlying providers.
|
||||
/// </summary>
|
||||
public interface ICompositeModelValidatorProvider : IModelValidatorProvider
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ using Microsoft.Framework.Internal;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class MaxLengthAttributeAdapter : DataAnnotationsModelValidator<MaxLengthAttribute>
|
||||
public class MaxLengthAttributeAdapter : DataAnnotationsClientModelValidator<MaxLengthAttribute>
|
||||
{
|
||||
public MaxLengthAttributeAdapter(MaxLengthAttribute attribute)
|
||||
: base(attribute)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ using Microsoft.Framework.Internal;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class MinLengthAttributeAdapter : DataAnnotationsModelValidator<MinLengthAttribute>
|
||||
public class MinLengthAttributeAdapter : DataAnnotationsClientModelValidator<MinLengthAttribute>
|
||||
{
|
||||
public MinLengthAttributeAdapter(MinLengthAttribute attribute)
|
||||
: base(attribute)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ using Microsoft.Framework.Internal;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class RangeAttributeAdapter : DataAnnotationsModelValidator<RangeAttribute>
|
||||
public class RangeAttributeAdapter : DataAnnotationsClientModelValidator<RangeAttribute>
|
||||
{
|
||||
public RangeAttributeAdapter(RangeAttribute attribute)
|
||||
: base(attribute)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ using Microsoft.Framework.Internal;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class RegularExpressionAttributeAdapter : DataAnnotationsModelValidator<RegularExpressionAttribute>
|
||||
public class RegularExpressionAttributeAdapter : DataAnnotationsClientModelValidator<RegularExpressionAttribute>
|
||||
{
|
||||
public RegularExpressionAttributeAdapter(RegularExpressionAttribute attribute)
|
||||
: base(attribute)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ using Microsoft.Framework.Internal;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class RequiredAttributeAdapter : DataAnnotationsModelValidator<RequiredAttribute>
|
||||
public class RequiredAttributeAdapter : DataAnnotationsClientModelValidator<RequiredAttribute>
|
||||
{
|
||||
public RequiredAttributeAdapter(RequiredAttribute attribute)
|
||||
: base(attribute)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ using Microsoft.Framework.Internal;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class StringLengthAttributeAdapter : DataAnnotationsModelValidator<StringLengthAttribute>
|
||||
public class StringLengthAttributeAdapter : DataAnnotationsClientModelValidator<StringLengthAttribute>
|
||||
{
|
||||
public StringLengthAttributeAdapter(StringLengthAttribute attribute)
|
||||
: base(attribute)
|
||||
|
|
|
|||
|
|
@ -72,6 +72,10 @@ namespace Microsoft.AspNet.Mvc
|
|||
options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider());
|
||||
options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider());
|
||||
|
||||
// Set up client validators
|
||||
options.ClientModelValidatorProviders.Add(new DefaultClientModelValidatorProvider());
|
||||
options.ClientModelValidatorProviders.Add(new DataAnnotationsClientModelValidatorProvider());
|
||||
|
||||
// Add types to be excluded from Validation
|
||||
options.ValidationExcludeFilters.Add(new SimpleTypesExcludeFilter());
|
||||
options.ValidationExcludeFilters.Add(typeof(XObject));
|
||||
|
|
|
|||
|
|
@ -585,9 +585,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
htmlEncoder,
|
||||
dataOptionsAccessor.Object);
|
||||
|
||||
var optionsAccessor = new Mock<IOptions<MvcOptions>>();
|
||||
optionsAccessor
|
||||
.SetupGet(o => o.Options)
|
||||
.Returns(new MvcOptions());
|
||||
|
||||
return new DefaultHtmlGenerator(
|
||||
antiForgery,
|
||||
Mock.Of<IScopedInstance<ActionBindingContext>>(),
|
||||
optionsAccessor.Object,
|
||||
metadataProvider,
|
||||
Mock.Of<IUrlHelper>(),
|
||||
htmlEncoder);
|
||||
|
|
|
|||
|
|
@ -167,13 +167,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
var httpContext = new DefaultHttpContext();
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||
|
||||
var bindingContext = new ActionBindingContext()
|
||||
{
|
||||
ValidatorProvider = new DataAnnotationsModelValidatorProvider(),
|
||||
};
|
||||
|
||||
var bindingContextAccessor = new MockScopedInstance<ActionBindingContext>();
|
||||
bindingContextAccessor.Value = bindingContext;
|
||||
var options = new MvcOptions();
|
||||
options.ClientModelValidatorProviders.Add(new DataAnnotationsClientModelValidatorProvider());
|
||||
var optionsAccessor = new Mock<IOptions<MvcOptions>>();
|
||||
optionsAccessor
|
||||
.SetupGet(o => o.Options)
|
||||
.Returns(options);
|
||||
|
||||
var serviceProvider = new Mock<IServiceProvider>();
|
||||
serviceProvider
|
||||
|
|
@ -191,7 +190,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
{
|
||||
htmlGenerator = new DefaultHtmlGenerator(
|
||||
GetAntiForgeryInstance(),
|
||||
bindingContextAccessor,
|
||||
optionsAccessor.Object,
|
||||
provider,
|
||||
urlHelper,
|
||||
new HtmlEncoder());
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
||||
|
|
@ -619,6 +620,53 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
Assert.Equal(initialValue, context.BindingMetadata.IsReadOnly);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidationDetails_ValidatableObject_ReturnsObject()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsMetadataProvider();
|
||||
|
||||
var attribute = new TestValidationAttribute();
|
||||
var attributes = new Attribute[] { attribute };
|
||||
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
|
||||
var context = new ValidationMetadataProviderContext(key, attributes);
|
||||
|
||||
// Act
|
||||
provider.GetValidationMetadata(context);
|
||||
|
||||
// Assert
|
||||
var validatorMetadata = Assert.Single(context.ValidationMetadata.ValidatorMetadata);
|
||||
Assert.Same(attribute, validatorMetadata);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidationDetails_ValidatableObject_AlreadyInContext_Ignores()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsMetadataProvider();
|
||||
|
||||
var attribute = new TestValidationAttribute();
|
||||
var attributes = new Attribute[] { attribute };
|
||||
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
|
||||
var context = new ValidationMetadataProviderContext(key, attributes);
|
||||
context.ValidationMetadata.ValidatorMetadata.Add(attribute);
|
||||
|
||||
// Act
|
||||
provider.GetValidationMetadata(context);
|
||||
|
||||
// Assert
|
||||
var validatorMetadata = Assert.Single(context.ValidationMetadata.ValidatorMetadata);
|
||||
Assert.Same(attribute, validatorMetadata);
|
||||
}
|
||||
|
||||
private class TestValidationAttribute : ValidationAttribute, IClientModelValidator
|
||||
{
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class EmptyClass
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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 Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
||||
{
|
||||
public class DefaultValidationMetadataTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetValidationDetails_MarkedWithClientValidator_ReturnsValidator()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DefaultValidationMetadataProvider();
|
||||
|
||||
var attribute = new TestClientModelValidationAttribute();
|
||||
var attributes = new Attribute[] { attribute };
|
||||
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
|
||||
var context = new ValidationMetadataProviderContext(key, attributes);
|
||||
|
||||
// Act
|
||||
provider.GetValidationMetadata(context);
|
||||
|
||||
// Assert
|
||||
var validatorMetadata = Assert.Single(context.ValidationMetadata.ValidatorMetadata);
|
||||
Assert.Same(attribute, validatorMetadata);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidationDetails_MarkedWithModelValidator_ReturnsValidator()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DefaultValidationMetadataProvider();
|
||||
|
||||
var attribute = new TestModelValidationAttribute();
|
||||
var attributes = new Attribute[] { attribute };
|
||||
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
|
||||
var context = new ValidationMetadataProviderContext(key, attributes);
|
||||
|
||||
// Act
|
||||
provider.GetValidationMetadata(context);
|
||||
|
||||
// Assert
|
||||
var validatorMetadata = Assert.Single(context.ValidationMetadata.ValidatorMetadata);
|
||||
Assert.Same(attribute, validatorMetadata);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidationDetails_Validator_AlreadyInContext_Ignores()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DefaultValidationMetadataProvider();
|
||||
|
||||
var attribute = new TestValidationAttribute();
|
||||
var attributes = new Attribute[] { attribute };
|
||||
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
|
||||
var context = new ValidationMetadataProviderContext(key, attributes);
|
||||
context.ValidationMetadata.ValidatorMetadata.Add(attribute);
|
||||
|
||||
// Act
|
||||
provider.GetValidationMetadata(context);
|
||||
|
||||
// Assert
|
||||
var validatorMetadata = Assert.Single(context.ValidationMetadata.ValidatorMetadata);
|
||||
Assert.Same(attribute, validatorMetadata);
|
||||
}
|
||||
|
||||
private class TestModelValidationAttribute : Attribute, IModelValidator
|
||||
{
|
||||
public bool IsRequired
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class TestClientModelValidationAttribute : Attribute, IClientModelValidator
|
||||
{
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class TestValidationAttribute : Attribute, IModelValidator, IClientModelValidator
|
||||
{
|
||||
public bool IsRequired
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class DataAnnotationsClientModelValidatorProviderTest
|
||||
{
|
||||
private readonly IModelMetadataProvider _metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
||||
[Fact]
|
||||
public void GetValidators_DoesNotAddRequiredAttribute_IfAttributeIsSpecifiedExplicitly()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsClientModelValidatorProvider();
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(typeof(DummyRequiredAttributeHelperClass),
|
||||
"WithAttribute");
|
||||
|
||||
var providerContext = new ClientValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
provider.GetValidators(providerContext);
|
||||
|
||||
// Assert
|
||||
var validator = Assert.Single(providerContext.Validators);
|
||||
var adapter = Assert.IsType<RequiredAttributeAdapter>(validator);
|
||||
Assert.Equal("Custom Required Message", adapter.Attribute.ErrorMessage);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> DataAnnotationAdapters
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
new RegularExpressionAttribute("abc"),
|
||||
typeof(RegularExpressionAttributeAdapter)
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
new MaxLengthAttribute(),
|
||||
typeof(MaxLengthAttributeAdapter)
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
new MinLengthAttribute(1),
|
||||
typeof(MinLengthAttributeAdapter)
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
new RangeAttribute(1, 100),
|
||||
typeof(RangeAttributeAdapter)
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
new StringLengthAttribute(6),
|
||||
typeof(StringLengthAttributeAdapter)
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
new RequiredAttribute(),
|
||||
typeof(RequiredAttributeAdapter)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(DataAnnotationAdapters))]
|
||||
public void AdapterFactory_RegistersAdapters_ForDataAnnotationAttributes(ValidationAttribute attribute,
|
||||
Type expectedAdapterType)
|
||||
{
|
||||
// Arrange
|
||||
var adapters = new DataAnnotationsClientModelValidatorProvider().AttributeFactories;
|
||||
var adapterFactory = adapters.Single(kvp => kvp.Key == attribute.GetType()).Value;
|
||||
|
||||
// Act
|
||||
var adapter = adapterFactory(attribute);
|
||||
|
||||
// Assert
|
||||
Assert.IsType(expectedAdapterType, adapter);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> DataTypeAdapters
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new object[] { new UrlAttribute(), "url" };
|
||||
yield return new object[] { new CreditCardAttribute(), "creditcard" };
|
||||
yield return new object[] { new EmailAddressAttribute(), "email" };
|
||||
yield return new object[] { new PhoneAttribute(), "phone" };
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(DataTypeAdapters))]
|
||||
public void AdapterFactory_RegistersAdapters_ForDataTypeAttributes(ValidationAttribute attribute,
|
||||
string expectedRuleName)
|
||||
{
|
||||
// Arrange
|
||||
var adapters = new DataAnnotationsClientModelValidatorProvider().AttributeFactories;
|
||||
var adapterFactory = adapters.Single(kvp => kvp.Key == attribute.GetType()).Value;
|
||||
|
||||
// Act
|
||||
var adapter = adapterFactory(attribute);
|
||||
|
||||
// Assert
|
||||
var dataTypeAdapter = Assert.IsType<DataTypeAttributeAdapter>(adapter);
|
||||
Assert.Equal(expectedRuleName, dataTypeAdapter.RuleName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnknownValidationAttribute_IsNotAddedAsValidator()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsClientModelValidatorProvider();
|
||||
var metadata = _metadataProvider.GetMetadataForType(typeof(DummyClassWithDummyValidationAttribute));
|
||||
|
||||
var providerContext = new ClientValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
provider.GetValidators(providerContext);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(providerContext.Validators);
|
||||
}
|
||||
|
||||
private class DummyValidationAttribute : ValidationAttribute
|
||||
{
|
||||
}
|
||||
|
||||
[DummyValidation]
|
||||
private class DummyClassWithDummyValidationAttribute
|
||||
{
|
||||
}
|
||||
|
||||
private class DummyRequiredAttributeHelperClass
|
||||
{
|
||||
[Required(ErrorMessage = "Custom Required Message")]
|
||||
public int WithAttribute { get; set; }
|
||||
|
||||
public int WithoutAttribute { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -36,111 +36,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void GetValidators_DoesNotAddRequiredAttribute_ForNonNullableValueTypes_IfAttributeIsSpecifiedExplicitly()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsModelValidatorProvider();
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(typeof(DummyRequiredAttributeHelperClass),
|
||||
"WithAttribute");
|
||||
|
||||
var providerContext = new ModelValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
provider.GetValidators(providerContext);
|
||||
|
||||
// Assert
|
||||
var validator = Assert.Single(providerContext.Validators);
|
||||
var adapter = Assert.IsType<RequiredAttributeAdapter>(validator);
|
||||
Assert.Equal("Custom Required Message", adapter.Attribute.ErrorMessage);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> DataAnnotationAdapters
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
new RegularExpressionAttribute("abc"),
|
||||
typeof(RegularExpressionAttributeAdapter)
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
new MaxLengthAttribute(),
|
||||
typeof(MaxLengthAttributeAdapter)
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
new MinLengthAttribute(1),
|
||||
typeof(MinLengthAttributeAdapter)
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
new RangeAttribute(1, 100),
|
||||
typeof(RangeAttributeAdapter)
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
new StringLengthAttribute(6),
|
||||
typeof(StringLengthAttributeAdapter)
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
new RequiredAttribute(),
|
||||
typeof(RequiredAttributeAdapter)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(DataAnnotationAdapters))]
|
||||
public void AdapterFactory_RegistersAdapters_ForDataAnnotationAttributes(ValidationAttribute attribute,
|
||||
Type expectedAdapterType)
|
||||
{
|
||||
// Arrange
|
||||
var adapters = new DataAnnotationsModelValidatorProvider().AttributeFactories;
|
||||
var adapterFactory = adapters.Single(kvp => kvp.Key == attribute.GetType()).Value;
|
||||
|
||||
// Act
|
||||
var adapter = adapterFactory(attribute);
|
||||
|
||||
// Assert
|
||||
Assert.IsType(expectedAdapterType, adapter);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> DataTypeAdapters
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new object[] { new UrlAttribute(), "url" };
|
||||
yield return new object[] { new CreditCardAttribute(), "creditcard" };
|
||||
yield return new object[] { new EmailAddressAttribute(), "email" };
|
||||
yield return new object[] { new PhoneAttribute(), "phone" };
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(DataTypeAdapters))]
|
||||
public void AdapterFactory_RegistersAdapters_ForDataTypeAttributes(ValidationAttribute attribute,
|
||||
string expectedRuleName)
|
||||
{
|
||||
// Arrange
|
||||
var adapters = new DataAnnotationsModelValidatorProvider().AttributeFactories;
|
||||
var adapterFactory = adapters.Single(kvp => kvp.Key == attribute.GetType()).Value;
|
||||
|
||||
// Act
|
||||
var adapter = adapterFactory(attribute);
|
||||
|
||||
// Assert
|
||||
var dataTypeAdapter = Assert.IsType<DataTypeAttributeAdapter>(adapter);
|
||||
Assert.Equal(expectedRuleName, dataTypeAdapter.RuleName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnknownValidationAttributeGetsDefaultAdapter()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -219,55 +219,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void GetClientValidationRules_ReturnsEmptyRuleSet()
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new FileExtensionsAttribute();
|
||||
var validator = new DataAnnotationsModelValidator<FileExtensionsAttribute>(attribute);
|
||||
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(
|
||||
containerType: typeof(string),
|
||||
propertyName: nameof(string.Length));
|
||||
|
||||
var serviceCollection = new ServiceCollection();
|
||||
var requestServices = serviceCollection.BuildServiceProvider();
|
||||
|
||||
var context = new ClientModelValidationContext(metadata, _metadataProvider, requestServices);
|
||||
|
||||
// Act
|
||||
var results = validator.GetClientValidationRules(context);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(results);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetClientValidationRules_WithIClientModelValidator_CallsAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new TestableAttribute();
|
||||
var validator = new DataAnnotationsModelValidator<TestableAttribute>(attribute);
|
||||
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(
|
||||
containerType: typeof(string),
|
||||
propertyName: nameof(string.Length));
|
||||
|
||||
var serviceCollection = new ServiceCollection();
|
||||
var requestServices = serviceCollection.BuildServiceProvider();
|
||||
|
||||
var context = new ClientModelValidationContext(metadata, _metadataProvider, requestServices);
|
||||
|
||||
// Act
|
||||
var results = validator.GetClientValidationRules(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(results);
|
||||
Assert.Equal("an error", rule.ErrorMessage);
|
||||
Assert.Empty(rule.ValidationParameters);
|
||||
Assert.Equal("testable", rule.ValidationType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsRequiredTests()
|
||||
{
|
||||
|
|
@ -295,13 +246,5 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
private class TestableAttribute : ValidationAttribute, IClientModelValidator
|
||||
{
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
||||
{
|
||||
return new[] { new ModelClientValidationRule(validationType: "testable", errorMessage: "an error") };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,269 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
// Integration tests for the default configuration of ModelMetadata and Validation providers
|
||||
public class DefaultModelClientValidatorProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetValidators_ForIValidatableObject()
|
||||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForType(typeof(ValidatableObject));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
|
||||
var validator = Assert.Single(validators);
|
||||
Assert.IsType<ValidatableObjectAdapter>(validator);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidators_ClientModelValidatorAttributeOnClass()
|
||||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForType(typeof(ModelValidatorAttributeOnClass));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
|
||||
var validator = Assert.Single(validators);
|
||||
var customModelValidator = Assert.IsType<CustomModelValidatorAttribute>(validator);
|
||||
Assert.Equal("Class", customModelValidator.Tag);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidators_ClientModelValidatorAttributeOnProperty()
|
||||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ModelValidatorAttributeOnProperty),
|
||||
nameof(ModelValidatorAttributeOnProperty.Property));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
|
||||
var validator = Assert.IsType<CustomModelValidatorAttribute>(Assert.Single(validators));
|
||||
Assert.Equal("Property", validator.Tag);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidators_ClientModelValidatorAttributeOnPropertyAndClass()
|
||||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ModelValidatorAttributeOnPropertyAndClass),
|
||||
nameof(ModelValidatorAttributeOnPropertyAndClass.Property));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
|
||||
Assert.Equal(2, validators.Count);
|
||||
Assert.Single(validators, v => Assert.IsType<CustomModelValidatorAttribute>(v).Tag == "Class");
|
||||
Assert.Single(validators, v => Assert.IsType<CustomModelValidatorAttribute>(v).Tag == "Property");
|
||||
}
|
||||
|
||||
// RangeAttribute is an example of a ValidationAttribute with it's own adapter type.
|
||||
[Fact]
|
||||
public void GetValidators_ClientValidatorAttribute_SpecificAdapter()
|
||||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestClientModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(RangeAttributeOnProperty),
|
||||
nameof(RangeAttributeOnProperty.Property));
|
||||
var context = new ClientValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
|
||||
Assert.IsType<RangeAttributeAdapter>(Assert.Single(validators));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidators_ClientValidatorAttribute_DefaultAdapter()
|
||||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestClientModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(CustomValidationAttributeOnProperty),
|
||||
nameof(CustomValidationAttributeOnProperty.Property));
|
||||
var context = new ClientValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
|
||||
Assert.IsType<CustomValidationAttribute>(Assert.Single(validators));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidators_FromModelMetadataType_SingleValidator()
|
||||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestClientModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ProductViewModel),
|
||||
nameof(ProductViewModel.Id));
|
||||
var context = new ClientValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
|
||||
Assert.IsType<RangeAttributeAdapter>(Assert.Single(validators));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidators_FromModelMetadataType_MergedValidators()
|
||||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestClientModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ProductViewModel),
|
||||
nameof(ProductViewModel.Name));
|
||||
var context = new ClientValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
|
||||
Assert.Equal(2, validators.Count);
|
||||
Assert.Single(validators, v => v is RegularExpressionAttributeAdapter);
|
||||
Assert.Single(validators, v => v is StringLengthAttributeAdapter);
|
||||
}
|
||||
|
||||
private class ValidatableObject : IValidatableObject
|
||||
{
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[CustomModelValidator(Tag = "Class")]
|
||||
private class ModelValidatorAttributeOnClass
|
||||
{
|
||||
}
|
||||
|
||||
private class ModelValidatorAttributeOnProperty
|
||||
{
|
||||
[CustomModelValidator(Tag = "Property")]
|
||||
public string Property { get; set; }
|
||||
}
|
||||
|
||||
private class ModelValidatorAttributeOnPropertyAndClass
|
||||
{
|
||||
[CustomModelValidator(Tag = "Property")]
|
||||
public ModelValidatorAttributeOnClass Property { get; set; }
|
||||
}
|
||||
|
||||
private class CustomModelValidatorAttribute : Attribute, IModelValidator
|
||||
{
|
||||
public string Tag { get; set; }
|
||||
|
||||
public bool IsRequired
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class RangeAttributeOnProperty
|
||||
{
|
||||
[Range(0, 10)]
|
||||
public int Property { get; set; }
|
||||
}
|
||||
|
||||
private class CustomValidationAttribute : Attribute, IClientModelValidator
|
||||
{
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
||||
{
|
||||
return Enumerable.Empty<ModelClientValidationRule>();
|
||||
}
|
||||
}
|
||||
|
||||
private class CustomValidationAttributeOnProperty
|
||||
{
|
||||
[CustomValidation]
|
||||
public int Property { get; set; }
|
||||
}
|
||||
|
||||
private class ProductEntity
|
||||
{
|
||||
[Range(0, 10)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[RegularExpression(".*")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
[ModelMetadataType(typeof(ProductEntity))]
|
||||
private class ProductViewModel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
[StringLength(4)]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -102,12 +102,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestClientModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(RangeAttributeOnProperty),
|
||||
nameof(RangeAttributeOnProperty.Property));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
var context = new ClientValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
|
@ -144,12 +144,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestClientModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ProductViewModel),
|
||||
nameof(ProductViewModel.Id));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
var context = new ClientValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
|
@ -165,12 +165,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
|
||||
var validatorProvider = TestClientModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ProductViewModel),
|
||||
nameof(ProductViewModel.Name));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
var context = new ClientValidatorProviderContext(metadata);
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
public TestableHtmlGenerator(IModelMetadataProvider metadataProvider, IUrlHelper urlHelper)
|
||||
: this(
|
||||
metadataProvider,
|
||||
Mock.Of<IScopedInstance<ActionBindingContext>>(),
|
||||
GetOptions(),
|
||||
urlHelper,
|
||||
validationAttributes: new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
|
|
@ -36,10 +36,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
|
||||
public TestableHtmlGenerator(
|
||||
IModelMetadataProvider metadataProvider,
|
||||
IScopedInstance<ActionBindingContext> bindingContextAccessor,
|
||||
IOptions<MvcOptions> options,
|
||||
IUrlHelper urlHelper,
|
||||
IDictionary<string, object> validationAttributes)
|
||||
: base(GetAntiForgery(), bindingContextAccessor, metadataProvider, urlHelper, new HtmlEncoder())
|
||||
: base(GetAntiForgery(), options, metadataProvider, urlHelper, new HtmlEncoder())
|
||||
{
|
||||
_validationAttributes = validationAttributes;
|
||||
}
|
||||
|
|
@ -90,6 +90,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
return ValidationAttributes;
|
||||
}
|
||||
|
||||
private static IOptions<MvcOptions> GetOptions()
|
||||
{
|
||||
var mockOptions = new Mock<IOptions<MvcOptions>>();
|
||||
mockOptions
|
||||
.SetupGet(options => options.Options)
|
||||
.Returns(new MvcOptions());
|
||||
|
||||
return mockOptions.Object;
|
||||
}
|
||||
private static AntiForgery GetAntiForgery()
|
||||
{
|
||||
// AntiForgery must be passed to TestableHtmlGenerator constructor but will never be called.
|
||||
|
|
|
|||
|
|
@ -124,6 +124,22 @@ namespace Microsoft.AspNet.Mvc
|
|||
Assert.IsType<DataAnnotationsModelValidatorProvider>(mvcOptions.ModelValidatorProviders[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Setup_SetsUpClientModelValidatorProviders()
|
||||
{
|
||||
// Arrange
|
||||
var mvcOptions = new MvcOptions();
|
||||
var setup = new MvcOptionsSetup();
|
||||
|
||||
// Act
|
||||
setup.Configure(mvcOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, mvcOptions.ClientModelValidatorProviders.Count);
|
||||
Assert.IsType<DefaultClientModelValidatorProvider>(mvcOptions.ClientModelValidatorProviders[0]);
|
||||
Assert.IsType<DataAnnotationsClientModelValidatorProvider>(mvcOptions.ClientModelValidatorProviders[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Setup_IgnoresAcceptHeaderHavingWildCardMediaAndSubMediaTypes()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
internal class TestClientModelValidatorProvider : CompositeClientModelValidatorProvider
|
||||
{
|
||||
// Creates a provider with all the defaults - includes data annotations
|
||||
public static IClientModelValidatorProvider CreateDefaultProvider()
|
||||
{
|
||||
var providers = new IClientModelValidatorProvider[]
|
||||
{
|
||||
new DefaultClientModelValidatorProvider(),
|
||||
new DataAnnotationsClientModelValidatorProvider(),
|
||||
};
|
||||
|
||||
return new TestClientModelValidatorProvider(providers);
|
||||
}
|
||||
|
||||
public TestClientModelValidatorProvider(IEnumerable<IClientModelValidatorProvider> providers)
|
||||
: base(providers)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue