Introducing IClientModelValidator to support client validation
* Adding support for validator adapters in DataAnnotationsModelValidatorProvider * Adding Regex and DataType validators
This commit is contained in:
parent
f34ed467e4
commit
b9b652084a
|
|
@ -70,21 +70,27 @@
|
|||
<Compile Include="Properties\Resources.Designer.cs" />
|
||||
<Compile Include="RequestContext.cs" />
|
||||
<Compile Include="Validation\AssociatedValidatorProvider.cs" />
|
||||
<Compile Include="Validation\ClientModelValidationContext.cs" />
|
||||
<Compile Include="Validation\CompositeModelValidator.cs" />
|
||||
<Compile Include="Validation\DataAnnotationsModelValidator.cs" />
|
||||
<Compile Include="Validation\DataAnnotationsModelValidatorOfTAttribute.cs" />
|
||||
<Compile Include="Validation\DataAnnotationsModelValidatorProvider.cs" />
|
||||
<Compile Include="Validation\DataMemberModelValidatorProvider.cs" />
|
||||
<Compile Include="Validation\DataTypeAttributeAdapter.cs" />
|
||||
<Compile Include="Validation\ErrorModelValidator.cs" />
|
||||
<Compile Include="Validation\FieldValidationMetadata.cs" />
|
||||
<Compile Include="Validation\IClientModelValidator.cs" />
|
||||
<Compile Include="Validation\IModelValidator.cs" />
|
||||
<Compile Include="Validation\IModelValidatorProvider.cs" />
|
||||
<Compile Include="Validation\InvalidModelValidatorProvider.cs" />
|
||||
<Compile Include="Validation\ModelClientValidationRegexRule.cs" />
|
||||
<Compile Include="Validation\ModelClientValidationRule.cs" />
|
||||
<Compile Include="Validation\ModelValidatedEventArgs.cs" />
|
||||
<Compile Include="Validation\ModelValidatingEventArgs.cs" />
|
||||
<Compile Include="Validation\ModelValidationContext.cs" />
|
||||
<Compile Include="Validation\ModelValidationNode.cs" />
|
||||
<Compile Include="Validation\ModelValidationResult.cs" />
|
||||
<Compile Include="Validation\RegularExpressionAttributeAdapter.cs" />
|
||||
<Compile Include="Validation\RequiredMemberModelValidator.cs" />
|
||||
<Compile Include="Validation\ValidatableObjectAdapter.cs" />
|
||||
<Compile Include="ValueProviders\CompositeValueProvider.cs" />
|
||||
|
|
|
|||
|
|
@ -42,6 +42,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("Common_PropertyNotFound"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type '{0}' must have a public constructor which accepts a single parameter of type '{1}'.
|
||||
/// </summary>
|
||||
internal static string DataAnnotationsModelValidatorProvider_ConstructorRequirements
|
||||
{
|
||||
get { return GetString("DataAnnotationsModelValidatorProvider_ConstructorRequirements"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type '{0}' must have a public constructor which accepts a single parameter of type '{1}'.
|
||||
/// </summary>
|
||||
internal static string FormatDataAnnotationsModelValidatorProvider_ConstructorRequirements(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("DataAnnotationsModelValidatorProvider_ConstructorRequirements"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The key is invalid JQuery syntax because it is missing a closing bracket.
|
||||
/// </summary>
|
||||
|
|
@ -218,6 +234,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return GetString("ModelBindingContext_ModelMetadataMustBeSet");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type '{0}' must derive from '{1}'.
|
||||
/// </summary>
|
||||
internal static string TypeMustDeriveFromType
|
||||
{
|
||||
get { return GetString("TypeMustDeriveFromType"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type '{0}' must derive from '{1}'.
|
||||
/// </summary>
|
||||
internal static string FormatTypeMustDeriveFromType(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TypeMustDeriveFromType"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The model object inside the metadata claimed to be compatible with '{0}', but was actually '{1}'.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -123,6 +123,9 @@
|
|||
<data name="Common_PropertyNotFound" xml:space="preserve">
|
||||
<value>The property {0}.{1} could not be found.</value>
|
||||
</data>
|
||||
<data name="DataAnnotationsModelValidatorProvider_ConstructorRequirements" xml:space="preserve">
|
||||
<value>The type '{0}' must have a public constructor which accepts a single parameter of type '{1}'.</value>
|
||||
</data>
|
||||
<data name="JQuerySyntaxMissingClosingBracket" xml:space="preserve">
|
||||
<value>The key is invalid JQuery syntax because it is missing a closing bracket.</value>
|
||||
</data>
|
||||
|
|
@ -156,6 +159,9 @@
|
|||
<data name="ModelBindingContext_ModelMetadataMustBeSet" xml:space="preserve">
|
||||
<value>The ModelMetadata property must be set before accessing this property.</value>
|
||||
</data>
|
||||
<data name="TypeMustDeriveFromType" xml:space="preserve">
|
||||
<value>The type '{0}' must derive from '{1}'.</value>
|
||||
</data>
|
||||
<data name="ValidatableObjectAdapter_IncompatibleType" xml:space="preserve">
|
||||
<value>The model object inside the metadata claimed to be compatible with '{0}', but was actually '{1}'.</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class ClientModelValidationContext
|
||||
{
|
||||
public ClientModelValidationContext([NotNull] ModelMetadata metadata)
|
||||
{
|
||||
ModelMetadata = metadata;
|
||||
}
|
||||
|
||||
public ModelMetadata ModelMetadata { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ using System.Linq;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class DataAnnotationsModelValidator : IModelValidator
|
||||
public class DataAnnotationsModelValidator : IModelValidator, IClientModelValidator
|
||||
{
|
||||
public DataAnnotationsModelValidator([NotNull] ValidationAttribute attribute)
|
||||
{
|
||||
|
|
@ -53,5 +53,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
return Enumerable.Empty<ModelValidationResult>();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
[NotNull] ClientModelValidationContext context)
|
||||
{
|
||||
return Enumerable.Empty<ModelClientValidationRule>();
|
||||
}
|
||||
|
||||
protected virtual string GetErrorMessage([NotNull] ModelMetadata modelMetadata)
|
||||
{
|
||||
return Attribute.FormatErrorMessage(modelMetadata.GetDisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class DataAnnotationsModelValidator<TAttribute> : DataAnnotationsModelValidator
|
||||
where TAttribute : ValidationAttribute
|
||||
{
|
||||
public DataAnnotationsModelValidator(TAttribute attribute)
|
||||
: base(attribute)
|
||||
{
|
||||
}
|
||||
|
||||
protected new TAttribute Attribute
|
||||
{
|
||||
get { return (TAttribute)base.Attribute; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,29 +18,25 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </summary>
|
||||
public class DataAnnotationsModelValidatorProvider : AssociatedValidatorProvider
|
||||
{
|
||||
// A factory for validators based on ValidationAttribute
|
||||
// A factory for validators based on ValidationAttribute.
|
||||
private delegate IModelValidator DataAnnotationsModelValidationFactory(ValidationAttribute attribute);
|
||||
|
||||
// A factory for validators based on IValidatableObject
|
||||
private delegate IModelValidator DataAnnotationsValidatableObjectAdapterFactory();
|
||||
|
||||
private static bool _addImplicitRequiredAttributeForValueTypes = true;
|
||||
private readonly Dictionary<Type, DataAnnotationsModelValidationFactory> _attributeFactories =
|
||||
BuildAttributeFactoriesDictionary();
|
||||
|
||||
// Factories for validation attributes
|
||||
private static DataAnnotationsModelValidationFactory DefaultAttributeFactory =
|
||||
private static readonly DataAnnotationsModelValidationFactory _defaultAttributeFactory =
|
||||
(attribute) => new DataAnnotationsModelValidator(attribute);
|
||||
|
||||
private static Dictionary<Type, DataAnnotationsModelValidationFactory> AttributeFactories =
|
||||
new Dictionary<Type, DataAnnotationsModelValidationFactory>();
|
||||
|
||||
// Factories for IValidatableObject models
|
||||
private static DataAnnotationsValidatableObjectAdapterFactory DefaultValidatableFactory =
|
||||
private static readonly DataAnnotationsValidatableObjectAdapterFactory _defaultValidatableFactory =
|
||||
() => new ValidatableObjectAdapter();
|
||||
|
||||
private static Dictionary<Type, DataAnnotationsValidatableObjectAdapterFactory> ValidatableFactories =
|
||||
new Dictionary<Type, DataAnnotationsValidatableObjectAdapterFactory>();
|
||||
|
||||
public static bool AddImplicitRequiredAttributeForValueTypes
|
||||
private static bool AddImplicitRequiredAttributeForValueTypes
|
||||
{
|
||||
get { return _addImplicitRequiredAttributeForValueTypes; }
|
||||
set { _addImplicitRequiredAttributeForValueTypes = value; }
|
||||
|
|
@ -54,9 +50,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
foreach (var attribute in attributes.OfType<ValidationAttribute>())
|
||||
{
|
||||
DataAnnotationsModelValidationFactory factory;
|
||||
if (!AttributeFactories.TryGetValue(attribute.GetType(), out factory))
|
||||
if (!_attributeFactories.TryGetValue(attribute.GetType(), out factory))
|
||||
{
|
||||
factory = DefaultAttributeFactory;
|
||||
factory = _defaultAttributeFactory;
|
||||
}
|
||||
results.Add(factory(attribute));
|
||||
}
|
||||
|
|
@ -64,15 +60,41 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
// Produce a validator if the type supports IValidatableObject
|
||||
if (typeof(IValidatableObject).IsAssignableFrom(metadata.ModelType))
|
||||
{
|
||||
DataAnnotationsValidatableObjectAdapterFactory factory;
|
||||
if (!ValidatableFactories.TryGetValue(metadata.ModelType, out factory))
|
||||
{
|
||||
factory = DefaultValidatableFactory;
|
||||
}
|
||||
results.Add(factory());
|
||||
results.Add(_defaultValidatableFactory());
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static Dictionary<Type, DataAnnotationsModelValidationFactory> BuildAttributeFactoriesDictionary()
|
||||
{
|
||||
var dict = new Dictionary<Type, DataAnnotationsModelValidationFactory>();
|
||||
AddValidationAttributeAdapter(dict, typeof(RegularExpressionAttribute),
|
||||
(attribute) => new RegularExpressionAttributeAdapter((RegularExpressionAttribute)attribute));
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
/// <summary>
|
||||
/// 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 DataTypeAttributeAdapter(DataTypeAttribute attribute,
|
||||
[NotNull] string ruleName)
|
||||
: base(attribute)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ruleName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "ruleName");
|
||||
}
|
||||
RuleName = ruleName;
|
||||
}
|
||||
|
||||
public string RuleName { get; private set; }
|
||||
|
||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
[NotNull] ClientModelValidationContext context)
|
||||
{
|
||||
var errorMessage = GetErrorMessage(context.ModelMetadata);
|
||||
return new[] { new ModelClientValidationRule(RuleName, errorMessage) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public interface IClientModelValidator
|
||||
{
|
||||
IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class ModelClientValidationRegexRule : ModelClientValidationRule
|
||||
{
|
||||
private const string ValidationType = "regex";
|
||||
private const string ValidationRuleName = "pattern";
|
||||
|
||||
public ModelClientValidationRegexRule(string errorMessage, string pattern)
|
||||
: base(ValidationType, errorMessage)
|
||||
{
|
||||
ValidationParameters.Add(ValidationRuleName, pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,24 +7,31 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
private readonly Dictionary<string, object> _validationParameters =
|
||||
new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
private string _validationType = string.Empty;
|
||||
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
public IDictionary<string, object> ValidationParameters
|
||||
public ModelClientValidationRule([NotNull] string errorMessage)
|
||||
: this(validationType: string.Empty, errorMessage: errorMessage)
|
||||
{
|
||||
get { return _validationParameters; }
|
||||
}
|
||||
|
||||
public ModelClientValidationRule([NotNull] string validationType,
|
||||
[NotNull] string errorMessage)
|
||||
{
|
||||
ValidationType = validationType;
|
||||
ErrorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public string ErrorMessage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifier of the <see cref="ModelClientValidationRule"/>. If client-side unobtrustive validation is
|
||||
/// enabled, use this <see langref="string"/> as part of the generated "data-val" attribute name. Must be
|
||||
/// unique in the set of enabled validation rules.
|
||||
/// </summary>
|
||||
public string ValidationType
|
||||
public string ValidationType { get; private set; }
|
||||
|
||||
public IDictionary<string, object> ValidationParameters
|
||||
{
|
||||
get { return _validationType; }
|
||||
set { _validationType = value ?? string.Empty; }
|
||||
get { return _validationParameters; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class RegularExpressionAttributeAdapter : DataAnnotationsModelValidator<RegularExpressionAttribute>
|
||||
{
|
||||
public RegularExpressionAttributeAdapter(RegularExpressionAttribute attribute)
|
||||
: base(attribute)
|
||||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
[NotNull] ClientModelValidationContext context)
|
||||
{
|
||||
var errorMessage = GetErrorMessage(context.ModelMetadata);
|
||||
return new[] { new ModelClientValidationRegexRule(errorMessage, Attribute.Pattern) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -75,4 +75,4 @@ namespace Microsoft.AspNet.Mvc.Razor.Host
|
|||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue