* Parameters into the messages
This commit is contained in:
parent
a208c95a4f
commit
3393ba43c2
|
|
@ -1,41 +1,25 @@
|
|||
// 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
|
||||
{
|
||||
public class ClientModelValidationContext
|
||||
/// <summary>
|
||||
/// The context for client-side model validation.
|
||||
/// </summary>
|
||||
public class ClientModelValidationContext : ModelValidationContextBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new instance of <see cref="ClientModelValidationContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="actionContext">The <see cref="ActionContext"/> for validation.</param>
|
||||
/// <param name="metadata">The <see cref="ModelMetadata"/> for validation.</param>
|
||||
/// <param name="metadataProvider">The <see cref="IModelMetadataProvider"/> to be used in validation.</param>
|
||||
public ClientModelValidationContext(
|
||||
ActionContext actionContext,
|
||||
ModelMetadata metadata,
|
||||
IModelMetadataProvider metadataProvider)
|
||||
: base(actionContext, metadata, metadataProvider)
|
||||
{
|
||||
if (actionContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(actionContext));
|
||||
}
|
||||
|
||||
if (metadata == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(metadata));
|
||||
}
|
||||
|
||||
if (metadataProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(metadataProvider));
|
||||
}
|
||||
|
||||
ActionContext = actionContext;
|
||||
ModelMetadata = metadata;
|
||||
MetadataProvider = metadataProvider;
|
||||
}
|
||||
|
||||
public ActionContext ActionContext { get; }
|
||||
|
||||
public ModelMetadata ModelMetadata { get; }
|
||||
|
||||
public IModelMetadataProvider MetadataProvider { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,26 +6,36 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
/// <summary>
|
||||
/// A context object for <see cref="IModelValidator"/>.
|
||||
/// </summary>
|
||||
public class ModelValidationContext
|
||||
public class ModelValidationContext : ModelValidationContextBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Mvc.ActionContext"/>
|
||||
/// Create a new instance of <see cref="ModelValidationContext"/>.
|
||||
/// </summary>
|
||||
public ActionContext ActionContext { get; set; }
|
||||
/// <param name="actionContext">The <see cref="ActionContext"/> for validation.</param>
|
||||
/// <param name="modelMetadata">The <see cref="ModelMetadata"/> for validation.</param>
|
||||
/// <param name="metadataProvider">The <see cref="IModelMetadataProvider"/> to be used in validation.</param>
|
||||
/// <param name="container">The model container.</param>
|
||||
/// <param name="model">The model to be validated.</param>
|
||||
public ModelValidationContext(
|
||||
ActionContext actionContext,
|
||||
ModelMetadata modelMetadata,
|
||||
IModelMetadataProvider metadataProvider,
|
||||
object container,
|
||||
object model)
|
||||
: base(actionContext, modelMetadata, metadataProvider)
|
||||
{
|
||||
Container = container;
|
||||
Model = model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the model object.
|
||||
/// Gets the model object.
|
||||
/// </summary>
|
||||
public object Model { get; set; }
|
||||
public object Model { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the model container object.
|
||||
/// Gets the model container object.
|
||||
/// </summary>
|
||||
public object Container { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ModelMetadata"/> associated with <see cref="Model"/>.
|
||||
/// </summary>
|
||||
public ModelMetadata Metadata { get; set; }
|
||||
public object Container { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
// 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>
|
||||
/// A common base class for <see cref="ModelValidationContext"/> and <see cref="ClientModelValidationContext"/>.
|
||||
/// </summary>
|
||||
public class ModelValidationContextBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="ModelValidationContextBase"/>.
|
||||
/// </summary>
|
||||
/// <param name="actionContext">The <see cref="ActionContext"/> for this context.</param>
|
||||
/// <param name="modelMetadata">The <see cref="ModelMetadata"/> for this model.</param>
|
||||
/// <param name="metadataProvider">The <see cref="IModelMetadataProvider"/> to be used by this context.</param>
|
||||
public ModelValidationContextBase(
|
||||
ActionContext actionContext,
|
||||
ModelMetadata modelMetadata,
|
||||
IModelMetadataProvider metadataProvider)
|
||||
{
|
||||
if (actionContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(actionContext));
|
||||
}
|
||||
|
||||
if (modelMetadata == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelMetadata));
|
||||
}
|
||||
|
||||
if (metadataProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(metadataProvider));
|
||||
}
|
||||
|
||||
ActionContext = actionContext;
|
||||
ModelMetadata = modelMetadata;
|
||||
MetadataProvider = metadataProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Mvc.ActionContext"/>.
|
||||
/// </summary>
|
||||
public ActionContext ActionContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ModelBinding.ModelMetadata"/>.
|
||||
/// </summary>
|
||||
public ModelMetadata ModelMetadata { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IModelMetadataProvider"/>.
|
||||
/// </summary>
|
||||
public IModelMetadataProvider MetadataProvider { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
}
|
||||
|
||||
public Task<IDictionary<string, object>> BindActionArgumentsAsync(
|
||||
ControllerContext context,
|
||||
ControllerContext context,
|
||||
object controller)
|
||||
{
|
||||
if (context == null)
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
var visitor = new ValidationVisitor(
|
||||
actionContext,
|
||||
validatorProvider,
|
||||
_modelMetadataProvider,
|
||||
validationState);
|
||||
|
||||
var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType());
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
public class ValidationVisitor
|
||||
{
|
||||
private readonly IModelValidatorProvider _validatorProvider;
|
||||
private readonly IModelMetadataProvider _metadataProvider;
|
||||
private readonly ActionContext _actionContext;
|
||||
private readonly ModelStateDictionary _modelState;
|
||||
private readonly ValidationStateDictionary _validationState;
|
||||
|
|
@ -26,7 +27,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
private IValidationStrategy _strategy;
|
||||
|
||||
private HashSet<object> _currentPath;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ValidationVisitor"/>.
|
||||
/// </summary>
|
||||
|
|
@ -36,6 +37,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
public ValidationVisitor(
|
||||
ActionContext actionContext,
|
||||
IModelValidatorProvider validatorProvider,
|
||||
IModelMetadataProvider metadataProvider,
|
||||
ValidationStateDictionary validationState)
|
||||
{
|
||||
if (actionContext == null)
|
||||
|
|
@ -50,6 +52,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
|
||||
_actionContext = actionContext;
|
||||
_validatorProvider = validatorProvider;
|
||||
_metadataProvider = metadataProvider;
|
||||
_validationState = validationState;
|
||||
|
||||
_modelState = actionContext.ModelState;
|
||||
|
|
@ -92,13 +95,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
var count = validators.Count;
|
||||
if (count > 0)
|
||||
{
|
||||
var context = new ModelValidationContext()
|
||||
{
|
||||
ActionContext = _actionContext,
|
||||
Container = _container,
|
||||
Model = _model,
|
||||
Metadata = _metadata,
|
||||
};
|
||||
var context = new ModelValidationContext(
|
||||
_actionContext,
|
||||
_metadata,
|
||||
_metadataProvider,
|
||||
_container,
|
||||
_model);
|
||||
|
||||
var results = new List<ModelValidationResult>();
|
||||
for (var i = 0; i < count; i++)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
// 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.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// An abstract subclass of <see cref="ValidationAttributeAdapter{TAttribute}"/> which wraps up all the required
|
||||
/// interfaces for the adapters.
|
||||
/// </summary>
|
||||
/// <typeparam name="TAttribute">The type of <see cref="ValidationAttribute"/> which is being wrapped.</typeparam>
|
||||
public abstract class AttributeAdapterBase<TAttribute> :
|
||||
ValidationAttributeAdapter<TAttribute>,
|
||||
IAttributeAdapter
|
||||
where TAttribute : ValidationAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="AttributeAdapterBase{TAttribute}"/>.
|
||||
/// </summary>
|
||||
/// <param name="attribute">The <see cref="ValidationAttribute"/> being wrapped.</param>
|
||||
/// <param name="stringLocalizer">The <see cref="IStringLocalizer"/> to be used in error generation.</param>
|
||||
public AttributeAdapterBase(TAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract string GetErrorMessage(ModelValidationContextBase validationContext);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ using Microsoft.Extensions.Localization;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class CompareAttributeAdapter : DataAnnotationsClientModelValidator<CompareAttribute>
|
||||
public class CompareAttributeAdapter : AttributeAdapterBase<CompareAttribute>
|
||||
{
|
||||
public CompareAttributeAdapter(CompareAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(new CompareAttributeWrapper(attribute), stringLocalizer)
|
||||
|
|
@ -28,19 +28,35 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var errorMessage = ((CompareAttributeWrapper)Attribute).FormatErrorMessage(context);
|
||||
var clientRule = new ModelClientValidationEqualToRule(errorMessage,
|
||||
FormatPropertyForClientValidation(Attribute.OtherProperty));
|
||||
var errorMessage = GetErrorMessage(context);
|
||||
var clientRule = new ModelClientValidationEqualToRule(errorMessage, "*." + Attribute.OtherProperty);
|
||||
return new[] { clientRule };
|
||||
}
|
||||
|
||||
private static string FormatPropertyForClientValidation(string property)
|
||||
/// <inheritdoc />
|
||||
public override string GetErrorMessage(ModelValidationContextBase validationContext)
|
||||
{
|
||||
return "*." + property;
|
||||
if (validationContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationContext));
|
||||
}
|
||||
|
||||
var displayName = validationContext.ModelMetadata.GetDisplayName();
|
||||
var otherPropertyDisplayName = CompareAttributeWrapper.GetOtherPropertyDisplayName(
|
||||
validationContext,
|
||||
Attribute);
|
||||
|
||||
((CompareAttributeWrapper)Attribute).ValidationContext = validationContext;
|
||||
|
||||
return GetErrorMessage(validationContext.ModelMetadata, displayName, otherPropertyDisplayName);
|
||||
}
|
||||
|
||||
// TODO: This entire class is needed because System.ComponentModel.DataAnnotations.CompareAttribute doesn't
|
||||
// populate OtherPropertyDisplayName until you call FormatErrorMessage.
|
||||
private sealed class CompareAttributeWrapper : CompareAttribute
|
||||
{
|
||||
public ModelValidationContextBase ValidationContext { get; set; }
|
||||
|
||||
public CompareAttributeWrapper(CompareAttribute attribute)
|
||||
: base(attribute.OtherProperty)
|
||||
{
|
||||
|
|
@ -58,34 +74,35 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
}
|
||||
}
|
||||
|
||||
public string FormatErrorMessage(ClientModelValidationContext context)
|
||||
public override string FormatErrorMessage(string name)
|
||||
{
|
||||
var displayName = context.ModelMetadata.GetDisplayName();
|
||||
var displayName = ValidationContext.ModelMetadata.GetDisplayName();
|
||||
return string.Format(CultureInfo.CurrentCulture,
|
||||
ErrorMessageString,
|
||||
displayName,
|
||||
GetOtherPropertyDisplayName(context));
|
||||
GetOtherPropertyDisplayName(ValidationContext, this));
|
||||
}
|
||||
|
||||
private string GetOtherPropertyDisplayName(ClientModelValidationContext context)
|
||||
public static string GetOtherPropertyDisplayName(
|
||||
ModelValidationContextBase validationContext,
|
||||
CompareAttribute attribute)
|
||||
{
|
||||
// The System.ComponentModel.DataAnnotations.CompareAttribute doesn't populate the
|
||||
// OtherPropertyDisplayName until after IsValid() is called. Therefore, by the time we get
|
||||
// OtherPropertyDisplayName until after IsValid() is called. Therefore, at the time we get
|
||||
// the error message for client validation, the display name is not populated and won't be used.
|
||||
var metadata = context.ModelMetadata;
|
||||
var otherPropertyDisplayName = OtherPropertyDisplayName;
|
||||
if (otherPropertyDisplayName == null && metadata.ContainerType != null)
|
||||
var otherPropertyDisplayName = attribute.OtherPropertyDisplayName;
|
||||
if (otherPropertyDisplayName == null && validationContext.ModelMetadata.ContainerType != null)
|
||||
{
|
||||
var otherProperty = context.MetadataProvider.GetMetadataForProperty(
|
||||
metadata.ContainerType,
|
||||
OtherProperty);
|
||||
var otherProperty = validationContext.MetadataProvider.GetMetadataForProperty(
|
||||
validationContext.ModelMetadata.ContainerType,
|
||||
attribute.OtherProperty);
|
||||
if (otherProperty != null)
|
||||
{
|
||||
return otherProperty.GetDisplayName();
|
||||
}
|
||||
}
|
||||
|
||||
return OtherProperty;
|
||||
return attribute.OtherProperty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
// 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 Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.OptionsModel;
|
||||
|
||||
|
|
@ -15,19 +15,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
/// 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{}"/>.
|
||||
/// is implemented in <see cref="ValidationAttributeAdapter{}"/>.
|
||||
/// </summary>
|
||||
public class DataAnnotationsClientModelValidatorProvider : IClientModelValidatorProvider
|
||||
{
|
||||
// A factory for validators based on ValidationAttribute.
|
||||
internal delegate IClientModelValidator DataAnnotationsClientModelValidationFactory(
|
||||
ValidationAttribute attribute,
|
||||
IStringLocalizer stringLocalizer);
|
||||
|
||||
private readonly Dictionary<Type, DataAnnotationsClientModelValidationFactory> _attributeFactories =
|
||||
BuildAttributeFactoriesDictionary();
|
||||
private readonly IOptions<MvcDataAnnotationsLocalizationOptions> _options;
|
||||
private readonly IStringLocalizerFactory _stringLocalizerFactory;
|
||||
private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of <see cref="DataAnnotationsClientModelValidatorProvider"/>.
|
||||
|
|
@ -35,18 +29,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
/// <param name="options">The <see cref="IOptions{MvcDataAnnotationsLocalizationOptions}"/>.</param>
|
||||
/// <param name="stringLocalizerFactory">The <see cref="IStringLocalizerFactory"/>.</param>
|
||||
public DataAnnotationsClientModelValidatorProvider(
|
||||
IValidationAttributeAdapterProvider validationAttributeAdapterProvider,
|
||||
IOptions<MvcDataAnnotationsLocalizationOptions> options,
|
||||
IStringLocalizerFactory stringLocalizerFactory)
|
||||
{
|
||||
if (validationAttributeAdapterProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationAttributeAdapterProvider));
|
||||
}
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
_validationAttributeAdapterProvider = validationAttributeAdapterProvider;
|
||||
_options = options;
|
||||
_stringLocalizerFactory = stringLocalizerFactory;
|
||||
}
|
||||
|
||||
internal Dictionary<Type, DataAnnotationsClientModelValidationFactory> AttributeFactories
|
||||
{
|
||||
get { return _attributeFactories; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void GetValidators(ClientValidatorProviderContext context)
|
||||
{
|
||||
|
|
@ -70,10 +70,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
hasRequiredAttribute |= attribute is RequiredAttribute;
|
||||
|
||||
DataAnnotationsClientModelValidationFactory factory;
|
||||
if (_attributeFactories.TryGetValue(attribute.GetType(), out factory))
|
||||
var adapter = _validationAttributeAdapterProvider.GetAttributeAdapter(attribute, stringLocalizer);
|
||||
if (adapter != null)
|
||||
{
|
||||
context.Validators.Add(factory(attribute, stringLocalizer));
|
||||
context.Validators.Add(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -83,82 +83,5 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
context.Validators.Add(new RequiredAttributeAdapter(new RequiredAttribute(), stringLocalizer));
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<Type, DataAnnotationsClientModelValidationFactory> BuildAttributeFactoriesDictionary()
|
||||
{
|
||||
return new Dictionary<Type, DataAnnotationsClientModelValidationFactory>()
|
||||
{
|
||||
{
|
||||
typeof(RegularExpressionAttribute),
|
||||
(attribute, stringLocalizer) => new RegularExpressionAttributeAdapter(
|
||||
(RegularExpressionAttribute)attribute,
|
||||
stringLocalizer)
|
||||
},
|
||||
{
|
||||
typeof(MaxLengthAttribute),
|
||||
(attribute, stringLocalizer) => new MaxLengthAttributeAdapter(
|
||||
(MaxLengthAttribute)attribute,
|
||||
stringLocalizer)
|
||||
},
|
||||
{
|
||||
typeof(MinLengthAttribute),
|
||||
(attribute, stringLocalizer) => new MinLengthAttributeAdapter(
|
||||
(MinLengthAttribute)attribute,
|
||||
stringLocalizer)
|
||||
},
|
||||
{
|
||||
typeof(CompareAttribute),
|
||||
(attribute, stringLocalizer) => new CompareAttributeAdapter(
|
||||
(CompareAttribute)attribute,
|
||||
stringLocalizer)
|
||||
},
|
||||
{
|
||||
typeof(RequiredAttribute),
|
||||
(attribute, stringLocalizer) => new RequiredAttributeAdapter(
|
||||
(RequiredAttribute)attribute,
|
||||
stringLocalizer)
|
||||
},
|
||||
{
|
||||
typeof(RangeAttribute),
|
||||
(attribute, stringLocalizer) => new RangeAttributeAdapter(
|
||||
(RangeAttribute)attribute,
|
||||
stringLocalizer)
|
||||
},
|
||||
{
|
||||
typeof(StringLengthAttribute),
|
||||
(attribute, stringLocalizer) => new StringLengthAttributeAdapter(
|
||||
(StringLengthAttribute)attribute,
|
||||
stringLocalizer)
|
||||
},
|
||||
{
|
||||
typeof(CreditCardAttribute),
|
||||
(attribute, stringLocalizer) => new DataTypeAttributeAdapter(
|
||||
(DataTypeAttribute)attribute,
|
||||
"creditcard",
|
||||
stringLocalizer)
|
||||
},
|
||||
{
|
||||
typeof(EmailAddressAttribute),
|
||||
(attribute, stringLocalizer) => new DataTypeAttributeAdapter(
|
||||
(DataTypeAttribute)attribute,
|
||||
"email",
|
||||
stringLocalizer)
|
||||
},
|
||||
{
|
||||
typeof(PhoneAttribute),
|
||||
(attribute, stringLocalizer) => new DataTypeAttributeAdapter(
|
||||
(DataTypeAttribute)attribute,
|
||||
"phone",
|
||||
stringLocalizer)
|
||||
},
|
||||
{
|
||||
typeof(UrlAttribute),
|
||||
(attribute, stringLocalizer) => new DataTypeAttributeAdapter(
|
||||
(DataTypeAttribute)attribute,
|
||||
"url",
|
||||
stringLocalizer)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,30 +5,80 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates based on the given <see cref="ValidationAttribute"/>.
|
||||
/// </summary>
|
||||
public class DataAnnotationsModelValidator : IModelValidator
|
||||
{
|
||||
private IStringLocalizer _stringLocalizer;
|
||||
private readonly IStringLocalizer _stringLocalizer;
|
||||
private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider;
|
||||
|
||||
public DataAnnotationsModelValidator(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
/// <summary>
|
||||
/// Create a new instance of <see cref="DataAnnotationsModelValidator"/>.
|
||||
/// </summary>
|
||||
/// <param name="attribute">The <see cref="ValidationAttribute"/> that defines what we're validating.</param>
|
||||
/// <param name="stringLocalizer">The <see cref="IStringLocalizer"/> used to create messages.</param>
|
||||
/// <param name="validationAttributeAdapterProvider">The <see cref="IValidationAttributeAdapterProvider"/>
|
||||
/// which <see cref="ValidationAttributeAdapter{TAttribute}"/>'s will be created from.</param>
|
||||
public DataAnnotationsModelValidator(
|
||||
IValidationAttributeAdapterProvider validationAttributeAdapterProvider,
|
||||
ValidationAttribute attribute,
|
||||
IStringLocalizer stringLocalizer)
|
||||
{
|
||||
if (validationAttributeAdapterProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationAttributeAdapterProvider));
|
||||
}
|
||||
|
||||
if (attribute == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(attribute));
|
||||
}
|
||||
|
||||
_validationAttributeAdapterProvider = validationAttributeAdapterProvider;
|
||||
Attribute = attribute;
|
||||
_stringLocalizer = stringLocalizer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The attribute being validated against.
|
||||
/// </summary>
|
||||
public ValidationAttribute Attribute { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Validates the context against the <see cref="ValidationAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="validationContext">The context being validated.</param>
|
||||
/// <returns>An enumerable of the validation results.</returns>
|
||||
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext validationContext)
|
||||
{
|
||||
var metadata = validationContext.Metadata;
|
||||
if (validationContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationContext));
|
||||
}
|
||||
if (validationContext.ModelMetadata == null)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
Resources.FormatPropertyOfTypeCannotBeNull(
|
||||
nameof(validationContext.ModelMetadata),
|
||||
typeof(ModelValidationContext)),
|
||||
nameof(validationContext));
|
||||
}
|
||||
if (validationContext.MetadataProvider == null)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
Resources.FormatPropertyOfTypeCannotBeNull(
|
||||
nameof(validationContext.MetadataProvider),
|
||||
typeof(ModelValidationContext)),
|
||||
nameof(validationContext));
|
||||
}
|
||||
|
||||
var metadata = validationContext.ModelMetadata;
|
||||
var memberName = metadata.PropertyName ?? metadata.ModelType.Name;
|
||||
var container = validationContext.Container;
|
||||
|
||||
|
|
@ -61,8 +111,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
string.IsNullOrEmpty(Attribute.ErrorMessageResourceName) &&
|
||||
Attribute.ErrorMessageResourceType == null)
|
||||
{
|
||||
var displayName = validationContext.Metadata.GetDisplayName();
|
||||
errorMessage = _stringLocalizer[Attribute.ErrorMessage, displayName];
|
||||
errorMessage = GetErrorMessage(validationContext);
|
||||
}
|
||||
|
||||
var validationResult = new ModelValidationResult(errorMemberName, errorMessage ?? result.ErrorMessage);
|
||||
|
|
@ -71,5 +120,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
|
||||
return Enumerable.Empty<ModelValidationResult>();
|
||||
}
|
||||
|
||||
private string GetErrorMessage(ModelValidationContextBase validationContext)
|
||||
{
|
||||
var adapter = _validationAttributeAdapterProvider.GetAttributeAdapter(Attribute, _stringLocalizer);
|
||||
return adapter?.GetErrorMessage(validationContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
// 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.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
#if DOTNET5_4
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
|
@ -19,16 +21,30 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
private readonly IOptions<MvcDataAnnotationsLocalizationOptions> _options;
|
||||
private readonly IStringLocalizerFactory _stringLocalizerFactory;
|
||||
private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of <see cref="DataAnnotationsModelValidatorProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="options">The <see cref="IOptions{MvcDataAnnotationsLocalizationOptions}"/>.</param>
|
||||
/// <param name="stringLocalizerFactory">The <see cref="IStringLocalizerFactory"/>.</param>
|
||||
/// <remarks><paramref name="options"/> and <paramref name="stringLocalizerFactory"/>
|
||||
/// are nullable only for testing ease.</remarks>
|
||||
public DataAnnotationsModelValidatorProvider(
|
||||
IValidationAttributeAdapterProvider validationAttributeAdapterProvider,
|
||||
IOptions<MvcDataAnnotationsLocalizationOptions> options,
|
||||
IStringLocalizerFactory stringLocalizerFactory)
|
||||
{
|
||||
if (validationAttributeAdapterProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationAttributeAdapterProvider));
|
||||
}
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
_validationAttributeAdapterProvider = validationAttributeAdapterProvider;
|
||||
_options = options;
|
||||
_stringLocalizerFactory = stringLocalizerFactory;
|
||||
}
|
||||
|
|
@ -51,7 +67,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
continue;
|
||||
}
|
||||
|
||||
var validator = new DataAnnotationsModelValidator(attribute, stringLocalizer);
|
||||
var validator = new DataAnnotationsModelValidator(
|
||||
_validationAttributeAdapterProvider,
|
||||
attribute,
|
||||
stringLocalizer);
|
||||
|
||||
// Inserts validators based on whether or not they are 'required'. We want to run
|
||||
// 'required' validators first so that we get the best possible error message.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,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 : DataAnnotationsClientModelValidator<DataTypeAttribute>
|
||||
public class DataTypeAttributeAdapter : AttributeAdapterBase<DataTypeAttribute>
|
||||
{
|
||||
public DataTypeAttributeAdapter(DataTypeAttribute attribute, string ruleName, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
|
|
@ -36,8 +36,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var errorMessage = GetErrorMessage(context.ModelMetadata);
|
||||
var errorMessage = GetErrorMessage(context);
|
||||
return new[] { new ModelClientValidationRule(RuleName, errorMessage) };
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetErrorMessage(ModelValidationContextBase validationContext)
|
||||
{
|
||||
if (validationContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationContext));
|
||||
}
|
||||
|
||||
return GetErrorMessage(
|
||||
validationContext.ModelMetadata,
|
||||
validationContext.ModelMetadata.GetDisplayName(),
|
||||
Attribute.GetDataTypeName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations.Internal;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.OptionsModel;
|
||||
|
|
@ -71,6 +73,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
{
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcDataAnnotationsMvcOptionsSetup>());
|
||||
services.TryAddSingleton<IValidationAttributeAdapterProvider, ValidationAttributeAdapterProvider>();
|
||||
}
|
||||
|
||||
// Internal for testing.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface so that adapters provide their relevent values to error messages.
|
||||
/// </summary>
|
||||
public interface IAttributeAdapter : IClientModelValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the error message.
|
||||
/// </summary>
|
||||
/// <param name="validationContext">The context to use in message creation.</param>
|
||||
/// <returns>The localized error message.</returns>
|
||||
string GetErrorMessage(ModelValidationContextBase validationContext);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// 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.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.DataAnnotations
|
||||
{
|
||||
/// <summary>
|
||||
/// Provider for supplying <see cref="IAttributeAdapter"/>'s.
|
||||
/// </summary>
|
||||
public interface IValidationAttributeAdapterProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the <see cref="IAttributeAdapter"/> for the given <see cref=" ValidationAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="attribute">The <see cref="ValidationAttribute"/> to create an <see cref="IAttributeAdapter"/>
|
||||
/// for.</param>
|
||||
/// <param name="stringLocalizer">The <see cref="IStringLocalizer"/> which will be used to create messages.
|
||||
/// </param>
|
||||
/// <returns>An <see cref="IAttributeAdapter"/> for the given <paramref name="attribute"/>.</returns>
|
||||
IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer);
|
||||
}
|
||||
}
|
||||
|
|
@ -27,9 +27,12 @@ namespace Microsoft.AspNet.Mvc.DataAnnotations.Internal
|
|||
|
||||
// This service will be registered only if AddDataAnnotationsLocalization() is added to service collection.
|
||||
var stringLocalizerFactory = serviceProvider.GetService<IStringLocalizerFactory>();
|
||||
var validationAttributeAdapterProvider = serviceProvider.GetRequiredService<IValidationAttributeAdapterProvider>();
|
||||
|
||||
options.ModelMetadataDetailsProviders.Add(new DataAnnotationsMetadataProvider());
|
||||
|
||||
options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider(
|
||||
validationAttributeAdapterProvider,
|
||||
dataAnnotationLocalizationOptions,
|
||||
stringLocalizerFactory));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class MaxLengthAttributeAdapter : DataAnnotationsClientModelValidator<MaxLengthAttribute>
|
||||
public class MaxLengthAttributeAdapter : AttributeAdapterBase<MaxLengthAttribute>
|
||||
{
|
||||
public MaxLengthAttributeAdapter(MaxLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
|
|
@ -23,8 +24,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var message = GetErrorMessage(context.ModelMetadata);
|
||||
var message = GetErrorMessage(context);
|
||||
return new[] { new ModelClientValidationMaxLengthRule(message, Attribute.Length) };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetErrorMessage(ModelValidationContextBase validationContext)
|
||||
{
|
||||
if (validationContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationContext));
|
||||
}
|
||||
|
||||
return GetErrorMessage(
|
||||
validationContext.ModelMetadata,
|
||||
validationContext.ModelMetadata.GetDisplayName(),
|
||||
Attribute.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class MinLengthAttributeAdapter : DataAnnotationsClientModelValidator<MinLengthAttribute>
|
||||
public class MinLengthAttributeAdapter : AttributeAdapterBase<MinLengthAttribute>
|
||||
{
|
||||
public MinLengthAttributeAdapter(MinLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
|
|
@ -23,8 +24,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var message = GetErrorMessage(context.ModelMetadata);
|
||||
var message = GetErrorMessage(context);
|
||||
return new[] { new ModelClientValidationMinLengthRule(message, Attribute.Length) };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetErrorMessage(ModelValidationContextBase validationContext)
|
||||
{
|
||||
if (validationContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationContext));
|
||||
}
|
||||
|
||||
return GetErrorMessage(
|
||||
validationContext.ModelMetadata,
|
||||
validationContext.ModelMetadata.GetDisplayName(),
|
||||
Attribute.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,22 @@ namespace Microsoft.AspNet.Mvc.DataAnnotations
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("NumericClientModelValidator_FieldMustBeNumber"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' property of '{1}' must not be null.
|
||||
/// </summary>
|
||||
internal static string PropertyOfTypeCannotBeNull
|
||||
{
|
||||
get { return GetString("PropertyOfTypeCannotBeNull"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' property of '{1}' must not be null.
|
||||
/// </summary>
|
||||
internal static string FormatPropertyOfTypeCannotBeNull(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("PropertyOfTypeCannotBeNull"), p0, p1);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class RangeAttributeAdapter : DataAnnotationsClientModelValidator<RangeAttribute>
|
||||
public class RangeAttributeAdapter : AttributeAdapterBase<RangeAttribute>
|
||||
{
|
||||
public RangeAttributeAdapter(RangeAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
|
|
@ -23,8 +24,28 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var errorMessage = GetErrorMessage(context.ModelMetadata);
|
||||
// TODO: Only calling this so Minimum and Maximum convert. Caused by a bug in CoreFx.
|
||||
Attribute.IsValid(null);
|
||||
|
||||
var errorMessage = GetErrorMessage(context);
|
||||
|
||||
|
||||
return new[] { new ModelClientValidationRangeRule(errorMessage, Attribute.Minimum, Attribute.Maximum) };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetErrorMessage(ModelValidationContextBase validationContext)
|
||||
{
|
||||
if (validationContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationContext));
|
||||
}
|
||||
|
||||
return GetErrorMessage(
|
||||
validationContext.ModelMetadata,
|
||||
validationContext.ModelMetadata.GetDisplayName(),
|
||||
Attribute.Minimum,
|
||||
Attribute.Maximum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,11 +4,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class RegularExpressionAttributeAdapter : DataAnnotationsClientModelValidator<RegularExpressionAttribute>
|
||||
public class RegularExpressionAttributeAdapter : AttributeAdapterBase<RegularExpressionAttribute>
|
||||
{
|
||||
public RegularExpressionAttributeAdapter(RegularExpressionAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
|
|
@ -23,8 +24,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var errorMessage = GetErrorMessage(context.ModelMetadata);
|
||||
var errorMessage = GetErrorMessage(context);
|
||||
return new[] { new ModelClientValidationRegexRule(errorMessage, Attribute.Pattern) };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetErrorMessage(ModelValidationContextBase validationContext)
|
||||
{
|
||||
if (validationContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationContext));
|
||||
}
|
||||
|
||||
return GetErrorMessage(
|
||||
validationContext.ModelMetadata,
|
||||
validationContext.ModelMetadata.GetDisplayName(),
|
||||
Attribute.Pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class RequiredAttributeAdapter : DataAnnotationsClientModelValidator<RequiredAttribute>
|
||||
public class RequiredAttributeAdapter : AttributeAdapterBase<RequiredAttribute>
|
||||
{
|
||||
public RequiredAttributeAdapter(RequiredAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
|
|
@ -23,8 +24,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var errorMessage = GetErrorMessage(context.ModelMetadata);
|
||||
var errorMessage = GetErrorMessage(context);
|
||||
return new[] { new ModelClientValidationRequiredRule(errorMessage) };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetErrorMessage(ModelValidationContextBase validationContext)
|
||||
{
|
||||
if (validationContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationContext));
|
||||
}
|
||||
|
||||
return GetErrorMessage(validationContext.ModelMetadata, validationContext.ModelMetadata.GetDisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -126,4 +126,7 @@
|
|||
<data name="NumericClientModelValidator_FieldMustBeNumber" xml:space="preserve">
|
||||
<value>The field {0} must be a number.</value>
|
||||
</data>
|
||||
<data name="PropertyOfTypeCannotBeNull" xml:space="preserve">
|
||||
<value>The '{0}' property of '{1}' must not be null.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -4,11 +4,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class StringLengthAttributeAdapter : DataAnnotationsClientModelValidator<StringLengthAttribute>
|
||||
public class StringLengthAttributeAdapter : AttributeAdapterBase<StringLengthAttribute>
|
||||
{
|
||||
public StringLengthAttributeAdapter(StringLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
|
|
@ -23,11 +24,26 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var errorMessage = GetErrorMessage(context.ModelMetadata);
|
||||
var errorMessage = GetErrorMessage(context);
|
||||
var rule = new ModelClientValidationStringLengthRule(errorMessage,
|
||||
Attribute.MinimumLength,
|
||||
Attribute.MaximumLength);
|
||||
return new[] { rule };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetErrorMessage(ModelValidationContextBase validationContext)
|
||||
{
|
||||
if (validationContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationContext));
|
||||
}
|
||||
|
||||
return GetErrorMessage(
|
||||
validationContext.ModelMetadata,
|
||||
validationContext.ModelMetadata.GetDisplayName(),
|
||||
Attribute.MinimumLength,
|
||||
Attribute.MaximumLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,16 +12,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
/// 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
|
||||
public abstract class ValidationAttributeAdapter<TAttribute> : IClientModelValidator
|
||||
where TAttribute : ValidationAttribute
|
||||
{
|
||||
private readonly IStringLocalizer _stringLocalizer;
|
||||
/// <summary>
|
||||
/// Create a new instance of <see cref="DataAnnotationsClientModelValidator{TAttribute}"/>.
|
||||
/// Create a new instance of <see cref="ValidationAttributeAdapter{TAttribute}"/>.
|
||||
/// </summary>
|
||||
/// <param name="attribute">The <typeparamref name="TAttribute"/> instance to validate.</param>
|
||||
/// <param name="stringLocalizer">The <see cref="IStringLocalizer"/>.</param>
|
||||
public DataAnnotationsClientModelValidator(TAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
public ValidationAttributeAdapter(TAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
{
|
||||
Attribute = attribute;
|
||||
_stringLocalizer = stringLocalizer;
|
||||
|
|
@ -44,25 +44,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
/// </summary>
|
||||
/// <param name="modelMetadata">The <see cref="ModelMetadata"/> associated with the model annotated with
|
||||
/// <see cref="Attribute"/>.</param>
|
||||
/// <param name="arguments">The value arguments which will be used in constructing the error message.</param>
|
||||
/// <returns>Formatted error string.</returns>
|
||||
protected virtual string GetErrorMessage(ModelMetadata modelMetadata)
|
||||
protected virtual string GetErrorMessage(ModelMetadata modelMetadata, params object[] arguments)
|
||||
{
|
||||
if (modelMetadata == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelMetadata));
|
||||
}
|
||||
|
||||
var displayName = modelMetadata.GetDisplayName();
|
||||
|
||||
if (_stringLocalizer != null &&
|
||||
!string.IsNullOrEmpty(Attribute.ErrorMessage) &&
|
||||
string.IsNullOrEmpty(Attribute.ErrorMessageResourceName) &&
|
||||
Attribute.ErrorMessageResourceType == null)
|
||||
{
|
||||
return _stringLocalizer[Attribute.ErrorMessage, displayName];
|
||||
return _stringLocalizer[Attribute.ErrorMessage, arguments];
|
||||
}
|
||||
|
||||
return Attribute.FormatErrorMessage(displayName);
|
||||
|
||||
return Attribute.FormatErrorMessage(modelMetadata.GetDisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
// 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.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IAttributeAdapter"/> for the given attribute.
|
||||
/// </summary>
|
||||
public class ValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IAttributeAdapter"/> for the given attribute.
|
||||
/// </summary>
|
||||
/// <param name="attribute">The attribute to create an adapter for.</param>
|
||||
/// <param name="stringLocalizer">The localizer to provide to the adapter.</param>
|
||||
/// <returns>An <see cref="IAttributeAdapter"/> for the given attribute.</returns>
|
||||
public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
{
|
||||
if (attribute == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(attribute));
|
||||
}
|
||||
|
||||
IAttributeAdapter adapter;
|
||||
|
||||
var type = attribute.GetType();
|
||||
|
||||
if (type == typeof(RegularExpressionAttribute))
|
||||
{
|
||||
adapter = new RegularExpressionAttributeAdapter((RegularExpressionAttribute)attribute, stringLocalizer);
|
||||
}
|
||||
else if (type == typeof(MaxLengthAttribute))
|
||||
{
|
||||
adapter = new MaxLengthAttributeAdapter((MaxLengthAttribute)attribute, stringLocalizer);
|
||||
}
|
||||
else if (type == typeof(RequiredAttribute))
|
||||
{
|
||||
adapter = new RequiredAttributeAdapter((RequiredAttribute)attribute, stringLocalizer);
|
||||
}
|
||||
else if (type == typeof(CompareAttribute))
|
||||
{
|
||||
adapter = new CompareAttributeAdapter((CompareAttribute)attribute, stringLocalizer);
|
||||
}
|
||||
else if (type == typeof(MinLengthAttribute))
|
||||
{
|
||||
adapter = new MinLengthAttributeAdapter((MinLengthAttribute)attribute, stringLocalizer);
|
||||
}
|
||||
else if (type == typeof(CreditCardAttribute))
|
||||
{
|
||||
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "creditcard", stringLocalizer);
|
||||
}
|
||||
else if (type == typeof(StringLengthAttribute))
|
||||
{
|
||||
adapter = new StringLengthAttributeAdapter((StringLengthAttribute)attribute, stringLocalizer);
|
||||
}
|
||||
else if (type == typeof(RangeAttribute))
|
||||
{
|
||||
adapter = new RangeAttributeAdapter((RangeAttribute)attribute, stringLocalizer);
|
||||
}
|
||||
else if (type == typeof(EmailAddressAttribute))
|
||||
{
|
||||
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "email", stringLocalizer);
|
||||
}
|
||||
else if (type == typeof(PhoneAttribute))
|
||||
{
|
||||
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "phone", stringLocalizer);
|
||||
}
|
||||
else if (type == typeof(UrlAttribute))
|
||||
{
|
||||
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "url", stringLocalizer);
|
||||
}
|
||||
else
|
||||
{
|
||||
adapter = null;
|
||||
}
|
||||
|
||||
return adapter;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
|
@ -29,10 +30,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures.Internal
|
|||
var dataAnnotationsLocalizationOptions =
|
||||
serviceProvider.GetRequiredService<IOptions<MvcDataAnnotationsLocalizationOptions>>();
|
||||
var stringLocalizerFactory = serviceProvider.GetService<IStringLocalizerFactory>();
|
||||
var validationAttributeAdapterProvider = serviceProvider.GetRequiredService<IValidationAttributeAdapterProvider>();
|
||||
|
||||
// Set up client validators
|
||||
options.ClientModelValidatorProviders.Add(new DefaultClientModelValidatorProvider());
|
||||
options.ClientModelValidatorProviders.Add(new DataAnnotationsClientModelValidatorProvider(
|
||||
validationAttributeAdapterProvider,
|
||||
dataAnnotationsLocalizationOptions,
|
||||
stringLocalizerFactory));
|
||||
options.ClientModelValidatorProviders.Add(new NumericClientModelValidatorProvider());
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -230,6 +231,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
ModelBinder = innerBinder ?? CreateIntBinder(),
|
||||
MetadataProvider = metataProvider,
|
||||
ValidatorProvider = new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Globalization;
|
|||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.Formatters;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNet.Routing;
|
||||
|
|
@ -72,10 +73,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
};
|
||||
|
||||
var validator = new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
var model = new MyModel();
|
||||
|
||||
|
||||
var values = new Dictionary<string, object>
|
||||
{
|
||||
{ "", null }
|
||||
|
|
@ -115,6 +117,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
};
|
||||
|
||||
var validator = new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
var model = new MyModel { MyProperty = "Old-Value" };
|
||||
|
|
@ -189,14 +192,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
};
|
||||
|
||||
var validator = new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
var model = new MyModel {
|
||||
var model = new MyModel
|
||||
{
|
||||
MyProperty = "Old-Value",
|
||||
IncludedProperty = "Old-IncludedPropertyValue",
|
||||
ExcludedProperty = "Old-ExcludedPropertyValue"
|
||||
};
|
||||
|
||||
|
||||
var values = new Dictionary<string, object>
|
||||
{
|
||||
{ "", null },
|
||||
|
|
@ -256,7 +261,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
new List<IInputFormatter>(),
|
||||
new Mock<IObjectModelValidator>(MockBehavior.Strict).Object,
|
||||
Mock.Of<IModelValidatorProvider>(),
|
||||
m => m.IncludedProperty );
|
||||
m => m.IncludedProperty);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
|
|
@ -276,6 +281,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
};
|
||||
|
||||
var validator = new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
var model = new MyModel
|
||||
|
|
@ -284,7 +290,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
IncludedProperty = "Old-IncludedPropertyValue",
|
||||
ExcludedProperty = "Old-ExcludedPropertyValue"
|
||||
};
|
||||
|
||||
|
||||
var values = new Dictionary<string, object>
|
||||
{
|
||||
{ "", null },
|
||||
|
|
@ -328,6 +334,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
};
|
||||
|
||||
var validator = new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
var model = new MyModel
|
||||
|
|
@ -336,7 +343,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
IncludedProperty = "Old-IncludedPropertyValue",
|
||||
ExcludedProperty = "Old-ExcludedPropertyValue"
|
||||
};
|
||||
|
||||
|
||||
var values = new Dictionary<string, object>
|
||||
{
|
||||
{ "", null },
|
||||
|
|
@ -533,6 +540,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
};
|
||||
|
||||
var validator = new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
var model = new MyModel
|
||||
|
|
@ -541,7 +549,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
IncludedProperty = "Old-IncludedPropertyValue",
|
||||
ExcludedProperty = "Old-ExcludedPropertyValue"
|
||||
};
|
||||
|
||||
|
||||
var values = new Dictionary<string, object>
|
||||
{
|
||||
{ "", null },
|
||||
|
|
@ -621,6 +629,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
};
|
||||
|
||||
var validator = new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
var model = new MyModel { MyProperty = "Old-Value" };
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.AspNet.Testing.xunit;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
|
|
@ -13,7 +14,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithCompareAttribute_ErrorMessageUsesDisplayName()
|
||||
public void ClientRulesWithCompareAttribute_ErrorMessageUsesDisplayName_WithoutLocalizer()
|
||||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
|
@ -37,6 +38,39 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
rule.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithCompareAttribute_ErrorMessageUsesDisplayName()
|
||||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = metadataProvider.GetMetadataForProperty(typeof(PropertyDisplayNameModel), "MyProperty");
|
||||
|
||||
var attribute = new CompareAttribute("OtherProperty");
|
||||
attribute.ErrorMessage = "CompareAttributeErrorMessage";
|
||||
|
||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
||||
var expectedProperties = new object[] { "MyPropertyDisplayName", "OtherPropertyDisplayName" };
|
||||
|
||||
var expectedMessage = "'MyPropertyDisplayName' and 'OtherPropertyDisplayName' do not match.";
|
||||
|
||||
stringLocalizer.Setup(s => s[attribute.ErrorMessage, expectedProperties])
|
||||
.Returns(new LocalizedString(attribute.ErrorMessage, expectedMessage));
|
||||
|
||||
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, metadataProvider);
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
// Mono issue - https://github.com/aspnet/External/issues/19
|
||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithCompareAttribute_ErrorMessageUsesPropertyName()
|
||||
|
|
@ -67,7 +101,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = metadataProvider.GetMetadataForProperty( typeof(PropertyNameModel), "MyProperty");
|
||||
var metadata = metadataProvider.GetMetadataForProperty(typeof(PropertyNameModel), "MyProperty");
|
||||
|
||||
var attribute = new CompareAttribute("OtherProperty")
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
|
|
@ -18,6 +19,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsClientModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
|
||||
|
|
@ -40,6 +42,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsClientModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
|
||||
|
|
@ -61,6 +64,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsClientModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
|
||||
|
|
@ -79,105 +83,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
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(
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null)
|
||||
.AttributeFactories;
|
||||
var adapterFactory = adapters.Single(kvp => kvp.Key == attribute.GetType()).Value;
|
||||
|
||||
// Act
|
||||
var adapter = adapterFactory(attribute, stringLocalizer: null);
|
||||
|
||||
// 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(
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null)
|
||||
.AttributeFactories;
|
||||
var adapterFactory = adapters.Single(kvp => kvp.Key == attribute.GetType()).Value;
|
||||
|
||||
// Act
|
||||
var adapter = adapterFactory(attribute, stringLocalizer: null);
|
||||
|
||||
// Assert
|
||||
var dataTypeAdapter = Assert.IsType<DataTypeAttributeAdapter>(adapter);
|
||||
Assert.Equal(expectedRuleName, dataTypeAdapter.RuleName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnknownValidationAttribute_IsNotAddedAsValidator()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsClientModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
var metadata = _metadataProvider.GetMetadataForType(typeof(DummyClassWithDummyValidationAttribute));
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
var mockValidatable = Mock.Of<IValidatableObject>();
|
||||
|
|
@ -36,6 +38,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
public void GetValidators_InsertsRequiredValidatorsFirst()
|
||||
{
|
||||
var provider = new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
var metadata = _metadataProvider.GetMetadataForProperty(
|
||||
|
|
@ -58,6 +61,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
var metadata = _metadataProvider.GetMetadataForType(typeof(DummyClassWithDummyValidationAttribute));
|
||||
|
|
@ -88,6 +92,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null);
|
||||
var mockValidatable = new Mock<IValidatableObject>();
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
// 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.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -21,7 +23,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
var attribute = new RequiredAttribute();
|
||||
|
||||
// Act
|
||||
var validator = new DataAnnotationsModelValidator(attribute, stringLocalizer: null);
|
||||
var validator = new DataAnnotationsModelValidator(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
attribute,
|
||||
stringLocalizer: null);
|
||||
|
||||
// Assert
|
||||
Assert.Same(attribute, validator.Attribute);
|
||||
|
|
@ -67,13 +72,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
})
|
||||
.Returns(ValidationResult.Success)
|
||||
.Verifiable();
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
|
||||
var validationContext = new ModelValidationContext()
|
||||
{
|
||||
Metadata = metadata,
|
||||
Container = container,
|
||||
Model = model,
|
||||
};
|
||||
var validator = new DataAnnotationsModelValidator(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
attribute.Object,
|
||||
stringLocalizer: null);
|
||||
var validationContext = new ModelValidationContext(
|
||||
actionContext: new ActionContext(),
|
||||
modelMetadata: metadata,
|
||||
metadataProvider: _metadataProvider,
|
||||
container: container,
|
||||
model: model);
|
||||
|
||||
// Act
|
||||
var results = validator.Validate(validationContext);
|
||||
|
|
@ -94,13 +102,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
||||
attribute.Setup(a => a.IsValid(model)).Returns(true);
|
||||
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
|
||||
var validationContext = new ModelValidationContext()
|
||||
{
|
||||
Metadata = metadata,
|
||||
Container = container,
|
||||
Model = model,
|
||||
};
|
||||
var validator = new DataAnnotationsModelValidator(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
attribute.Object,
|
||||
stringLocalizer: null);
|
||||
var validationContext = new ModelValidationContext(
|
||||
actionContext: new ActionContext(),
|
||||
modelMetadata: metadata,
|
||||
metadataProvider: _metadataProvider,
|
||||
container: container,
|
||||
model: model);
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(validationContext);
|
||||
|
|
@ -120,13 +131,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
||||
attribute.Setup(a => a.IsValid(model)).Returns(false);
|
||||
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
|
||||
var validationContext = new ModelValidationContext()
|
||||
{
|
||||
Metadata = metadata,
|
||||
Container = container,
|
||||
Model = model,
|
||||
};
|
||||
var validator = new DataAnnotationsModelValidator(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
attribute.Object,
|
||||
stringLocalizer: null);
|
||||
var validationContext = new ModelValidationContext(
|
||||
actionContext: new ActionContext(),
|
||||
modelMetadata: metadata,
|
||||
metadataProvider: _metadataProvider,
|
||||
container: container,
|
||||
model: model);
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(validationContext);
|
||||
|
|
@ -149,13 +163,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
attribute
|
||||
.Setup(p => p.IsValidPublic(It.IsAny<object>(), It.IsAny<ValidationContext>()))
|
||||
.Returns(ValidationResult.Success);
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
|
||||
var validationContext = new ModelValidationContext()
|
||||
{
|
||||
Metadata = metadata,
|
||||
Container = container,
|
||||
Model = model,
|
||||
};
|
||||
var validator = new DataAnnotationsModelValidator(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
attribute.Object,
|
||||
stringLocalizer: null);
|
||||
var validationContext = new ModelValidationContext(
|
||||
actionContext: new ActionContext(),
|
||||
modelMetadata: metadata,
|
||||
metadataProvider: _metadataProvider,
|
||||
container: container,
|
||||
model: model);
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(validationContext);
|
||||
|
|
@ -178,14 +195,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
attribute
|
||||
.Setup(p => p.IsValidPublic(It.IsAny<object>(), It.IsAny<ValidationContext>()))
|
||||
.Returns(new ValidationResult(errorMessage, memberNames: null));
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
|
||||
var validator = new DataAnnotationsModelValidator(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
attribute.Object,
|
||||
stringLocalizer: null);
|
||||
|
||||
var validationContext = new ModelValidationContext()
|
||||
{
|
||||
Metadata = metadata,
|
||||
Container = container,
|
||||
Model = model,
|
||||
};
|
||||
var validationContext = new ModelValidationContext(
|
||||
actionContext: new ActionContext(),
|
||||
modelMetadata: metadata,
|
||||
metadataProvider: _metadataProvider,
|
||||
container: container,
|
||||
model: model);
|
||||
|
||||
// Act
|
||||
var results = validator.Validate(validationContext);
|
||||
|
|
@ -210,12 +230,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
.Setup(p => p.IsValidPublic(It.IsAny<object>(), It.IsAny<ValidationContext>()))
|
||||
.Returns(new ValidationResult(errorMessage, new[] { "FirstName" }));
|
||||
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
|
||||
var validationContext = new ModelValidationContext()
|
||||
{
|
||||
Metadata = metadata,
|
||||
Model = model,
|
||||
};
|
||||
var validator = new DataAnnotationsModelValidator(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
attribute.Object,
|
||||
stringLocalizer: null);
|
||||
var validationContext = new ModelValidationContext(
|
||||
actionContext: new ActionContext(),
|
||||
modelMetadata: metadata,
|
||||
metadataProvider: _metadataProvider,
|
||||
container: null,
|
||||
model: model);
|
||||
|
||||
// Act
|
||||
var results = validator.Validate(validationContext);
|
||||
|
|
@ -238,12 +262,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
.Setup(p => p.IsValidPublic(It.IsAny<object>(), It.IsAny<ValidationContext>()))
|
||||
.Returns(new ValidationResult("Name error", new[] { "Name" }));
|
||||
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
|
||||
var validationContext = new ModelValidationContext()
|
||||
{
|
||||
Metadata = metadata,
|
||||
Model = model,
|
||||
};
|
||||
var validator = new DataAnnotationsModelValidator(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
attribute.Object,
|
||||
stringLocalizer: null);
|
||||
var validationContext = new ModelValidationContext(
|
||||
actionContext: new ActionContext(),
|
||||
modelMetadata: metadata,
|
||||
metadataProvider: _metadataProvider,
|
||||
container: null,
|
||||
model: model);
|
||||
|
||||
// Act
|
||||
var results = validator.Validate(validationContext);
|
||||
|
|
@ -259,24 +287,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
// Arrange
|
||||
var metadata = _metadataProvider.GetMetadataForType(typeof(string));
|
||||
var container = "Hello";
|
||||
var model = container.Length;
|
||||
|
||||
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
||||
attribute.Setup(a => a.IsValid(model)).Returns(false);
|
||||
var attribute = new MaxLengthAttribute(4);
|
||||
attribute.ErrorMessage = "{0} should have no more than {1} characters.";
|
||||
|
||||
attribute.Object.ErrorMessage = "Length";
|
||||
|
||||
var localizedString = new LocalizedString("Length", "Longueur est invalide");
|
||||
var localizedString = new LocalizedString(attribute.ErrorMessage, "Longueur est invalide : 4");
|
||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
||||
stringLocalizer.Setup(s => s["Length", It.IsAny<object[]>()]).Returns(localizedString);
|
||||
stringLocalizer.Setup(s => s[attribute.ErrorMessage, It.IsAny<object[]>()]).Returns(localizedString);
|
||||
|
||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer.Object);
|
||||
var validationContext = new ModelValidationContext()
|
||||
{
|
||||
Metadata = metadata,
|
||||
Container = container,
|
||||
Model = model,
|
||||
};
|
||||
var validator = new DataAnnotationsModelValidator(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
attribute,
|
||||
stringLocalizer.Object);
|
||||
var validationContext = new ModelValidationContext(
|
||||
actionContext: new ActionContext(),
|
||||
modelMetadata: metadata,
|
||||
metadataProvider: _metadataProvider,
|
||||
container: container,
|
||||
model: "abcde");
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(validationContext);
|
||||
|
|
@ -284,11 +312,108 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
// Assert
|
||||
var validationResult = result.Single();
|
||||
Assert.Equal("", validationResult.MemberName);
|
||||
Assert.Equal("Longueur est invalide", validationResult.Message);
|
||||
Assert.Equal("Longueur est invalide : 4", validationResult.Message);
|
||||
}
|
||||
|
||||
private class DerivedRequiredAttribute : RequiredAttribute
|
||||
private const string LocalizationKey = "LocalizeIt";
|
||||
|
||||
public static TheoryData Validate_AttributesIncludeValues
|
||||
{
|
||||
get
|
||||
{
|
||||
var pattern = "apattern";
|
||||
var length = 5;
|
||||
var regex = "^((?!" + pattern + ").)*$";
|
||||
|
||||
return new TheoryData<ValidationAttribute, string, object[]>
|
||||
{
|
||||
{
|
||||
new RegularExpressionAttribute(regex) { ErrorMessage = LocalizationKey },
|
||||
pattern,
|
||||
new object[] { nameof(SampleModel), regex }
|
||||
},
|
||||
{
|
||||
new MaxLengthAttribute(length) { ErrorMessage = LocalizationKey },
|
||||
pattern,
|
||||
new object[] { nameof(SampleModel), length }},
|
||||
{
|
||||
new MaxLengthAttribute(length) { ErrorMessage = LocalizationKey },
|
||||
pattern,
|
||||
new object[] { nameof(SampleModel), length }
|
||||
},
|
||||
{
|
||||
new CompareAttribute(pattern) { ErrorMessage = LocalizationKey },
|
||||
pattern,
|
||||
new object[] { nameof(SampleModel), pattern }},
|
||||
{
|
||||
new MinLengthAttribute(length) { ErrorMessage = LocalizationKey },
|
||||
"a",
|
||||
new object[] { nameof(SampleModel), length }
|
||||
},
|
||||
{
|
||||
new CreditCardAttribute() { ErrorMessage = LocalizationKey },
|
||||
pattern,
|
||||
new object[] { nameof(SampleModel), "CreditCard" }
|
||||
},
|
||||
{
|
||||
new StringLengthAttribute(length) { ErrorMessage = LocalizationKey, MinimumLength = 1},
|
||||
"",
|
||||
new object[] { nameof(SampleModel), 1, length }
|
||||
},
|
||||
{
|
||||
new RangeAttribute(0, length) { ErrorMessage = LocalizationKey },
|
||||
pattern,
|
||||
new object[] { nameof(SampleModel), 0, length}
|
||||
},
|
||||
{
|
||||
new EmailAddressAttribute() { ErrorMessage = LocalizationKey },
|
||||
pattern,
|
||||
new object[] { nameof(SampleModel), "EmailAddress" }
|
||||
},
|
||||
{
|
||||
new PhoneAttribute() { ErrorMessage = LocalizationKey },
|
||||
pattern,
|
||||
new object[] { nameof(SampleModel), "PhoneNumber" }
|
||||
},
|
||||
{
|
||||
new UrlAttribute() { ErrorMessage = LocalizationKey },
|
||||
pattern,
|
||||
new object[] { nameof(SampleModel), "Url" }
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Validate_AttributesIncludeValues))]
|
||||
public void Validate_IsValidFalse_StringLocalizerGetsArguments(
|
||||
ValidationAttribute attribute,
|
||||
string model,
|
||||
object[] values)
|
||||
{
|
||||
// Arrange
|
||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
||||
|
||||
var validator = new DataAnnotationsModelValidator(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
attribute,
|
||||
stringLocalizer.Object);
|
||||
|
||||
var metadata = _metadataProvider.GetMetadataForType(typeof(SampleModel));
|
||||
var validationContext = new ModelValidationContext(
|
||||
actionContext: new ActionContext(),
|
||||
modelMetadata: metadata,
|
||||
metadataProvider: _metadataProvider,
|
||||
container: null,
|
||||
model: model);
|
||||
|
||||
// Act
|
||||
validator.Validate(validationContext);
|
||||
|
||||
// Assert
|
||||
var json = Newtonsoft.Json.JsonConvert.SerializeObject(values) + " " + attribute.GetType().Name;
|
||||
|
||||
stringLocalizer.Verify(l => l[LocalizationKey, values], json);
|
||||
}
|
||||
|
||||
public abstract class TestableValidationAttribute : ValidationAttribute
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -12,6 +11,40 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
public class MaxLengthAttributeAdapterTest
|
||||
{
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithMaxLengthAttribute_Localize()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
||||
|
||||
var attribute = new MaxLengthAttribute(10);
|
||||
attribute.ErrorMessage = "Property must be max '{1}' characters long.";
|
||||
|
||||
var expectedProperties = new object[] { "Length", 10 };
|
||||
var expectedMessage = "Property must be max '10' characters long.";
|
||||
|
||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
||||
stringLocalizer.Setup(s => s[attribute.ErrorMessage, expectedProperties])
|
||||
.Returns(new LocalizedString(attribute.ErrorMessage, expectedMessage));
|
||||
|
||||
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("maxlength", rule.ValidationType);
|
||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
||||
Assert.Equal(10, rule.ValidationParameters["max"]);
|
||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithMaxLengthAttribute()
|
||||
|
|
@ -77,7 +110,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
attribute.ErrorMessage = errorKey;
|
||||
var localizedString = new LocalizedString(errorKey, "Longueur est invalide");
|
||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
||||
stringLocalizer.Setup(s => s[errorKey, It.IsAny<object[]>()]).Returns(localizedString);
|
||||
stringLocalizer.Setup(s => s[errorKey, metadata.GetDisplayName(), attribute.Length]).Returns(localizedString);
|
||||
|
||||
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer.Object);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,48 @@
|
|||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class MinLengthAttributeAdapterTest
|
||||
{
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithMinLengthAttribute_Localize()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
||||
|
||||
var attribute = new MinLengthAttribute(6);
|
||||
attribute.ErrorMessage = "Property must be at least '{1}' characters long.";
|
||||
|
||||
var expectedProperties = new object[] { "Length", 6 };
|
||||
var expectedMessage = "Property must be at least '6' characters long.";
|
||||
|
||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
||||
stringLocalizer.Setup(s => s[attribute.ErrorMessage, expectedProperties])
|
||||
.Returns(new LocalizedString(attribute.ErrorMessage, expectedMessage));
|
||||
|
||||
var adapter = new MinLengthAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("minlength", rule.ValidationType);
|
||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
||||
Assert.Equal(6, rule.ValidationParameters["min"]);
|
||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithMinLengthAttribute()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
|
|
@ -11,13 +13,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void GetClientValidationRules_ReturnsValidationParameters()
|
||||
public void GetClientValidationRules_ReturnsValidationParameters_WithoutLocalization()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
||||
|
||||
var attribute = new RangeAttribute(typeof(decimal), "0", "100");
|
||||
attribute.ErrorMessage = "The field Length must be between {1} and {2}.";
|
||||
|
||||
var expectedMessage = "The field Length must be between 0 and 100.";
|
||||
|
||||
var adapter = new RangeAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
|
|
@ -32,7 +38,42 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal(0m, rule.ValidationParameters["min"]);
|
||||
Assert.Equal(100m, rule.ValidationParameters["max"]);
|
||||
Assert.Equal(@"The field Length must be between 0 and 100.", rule.ErrorMessage);
|
||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void GetClientValidationRules_ReturnsValidationParameters()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
||||
|
||||
var attribute = new RangeAttribute(typeof(decimal), "0", "100");
|
||||
attribute.ErrorMessage = "The field Length must be between {1} and {2}.";
|
||||
|
||||
var expectedProperties = new object[] { "Length", 0m, 100m };
|
||||
var expectedMessage = "The field Length must be between 0 and 100.";
|
||||
|
||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
||||
stringLocalizer.Setup(s => s[attribute.ErrorMessage, expectedProperties])
|
||||
.Returns(new LocalizedString(attribute.ErrorMessage, expectedMessage));
|
||||
|
||||
var adapter = new RangeAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("range", rule.ValidationType);
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal(0m, rule.ValidationParameters["min"]);
|
||||
Assert.Equal(100m, rule.ValidationParameters["max"]);
|
||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,48 @@
|
|||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class RequiredAttributeAdapterTest
|
||||
{
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void GetClientValidationRules_ReturnsValidationParameters_Localize()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
||||
|
||||
var attribute = new RequiredAttribute();
|
||||
|
||||
var expectedProperties = new object[] { "Length" };
|
||||
var message = "This paramter is required.";
|
||||
var expectedMessage = "FR This parameter is required.";
|
||||
attribute.ErrorMessage = message;
|
||||
|
||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
||||
stringLocalizer.Setup(s => s[attribute.ErrorMessage, expectedProperties])
|
||||
.Returns(new LocalizedString(attribute.ErrorMessage, expectedMessage));
|
||||
|
||||
var adapter = new RequiredAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("required", rule.ValidationType);
|
||||
Assert.Empty(rule.ValidationParameters);
|
||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void GetClientValidationRules_ReturnsValidationParameters()
|
||||
|
|
|
|||
|
|
@ -3,12 +3,49 @@
|
|||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class StringLengthAttributeAdapterTest
|
||||
{
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void GetClientValidationRules_WithMaxLength_ReturnsValidationParameters_Localize()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
||||
|
||||
var attribute = new StringLengthAttribute(8);
|
||||
attribute.ErrorMessage = "Property must not be longer than '{1}' characters.";
|
||||
|
||||
var expectedMessage = "Property must not be longer than '8' characters.";
|
||||
|
||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
||||
var expectedProperties = new object[] { "Length", 0, 8 };
|
||||
|
||||
stringLocalizer.Setup(s => s[attribute.ErrorMessage, expectedProperties])
|
||||
.Returns(new LocalizedString(attribute.ErrorMessage, expectedMessage));
|
||||
|
||||
var adapter = new StringLengthAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("length", rule.ValidationType);
|
||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
||||
Assert.Equal(8, rule.ValidationParameters["max"]);
|
||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void GetClientValidationRules_WithMaxLength_ReturnsValidationParameters()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.DataAnnotations.Test
|
||||
{
|
||||
public class ValidationAttributeAdapterOfTAttributeTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetErrorMessage_DontLocalizeWhenErrorMessageResourceTypeGiven()
|
||||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
||||
var modelMetadata = metadataProvider.GetMetadataForProperty(typeof(string), "Length");
|
||||
|
||||
var stringLocalizer = new Mock<IStringLocalizer>(MockBehavior.Loose);
|
||||
|
||||
var attribute = new TestValidationAttribute();
|
||||
var adapter = new TestValidationAttributeAdapter(attribute, stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var validationContext = new ModelValidationContext(
|
||||
actionContext,
|
||||
modelMetadata,
|
||||
metadataProvider,
|
||||
container: null,
|
||||
model: null);
|
||||
|
||||
// Act
|
||||
adapter.GetErrorMessage(validationContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(attribute.Formated);
|
||||
}
|
||||
|
||||
public class TestValidationAttribute : ValidationAttribute
|
||||
{
|
||||
public bool Formated = false;
|
||||
|
||||
public override string FormatErrorMessage(string name)
|
||||
{
|
||||
Formated = true;
|
||||
return base.FormatErrorMessage(name);
|
||||
}
|
||||
}
|
||||
|
||||
public class TestValidationAttributeAdapter : ValidationAttributeAdapter<TestValidationAttribute>
|
||||
{
|
||||
public TestValidationAttributeAdapter(TestValidationAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
{ }
|
||||
|
||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string GetErrorMessage(ModelValidationContextBase validationContext)
|
||||
{
|
||||
var displayName = validationContext.ModelMetadata.GetDisplayName();
|
||||
return GetErrorMessage(validationContext.ModelMetadata, displayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// 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.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class ValidationAttributeAdapterProviderTest
|
||||
{
|
||||
private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider =
|
||||
new ValidationAttributeAdapterProvider();
|
||||
|
||||
public static TheoryData<ValidationAttribute, Type> DataAnnotationAdapters
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<ValidationAttribute, Type>
|
||||
{
|
||||
{
|
||||
new RegularExpressionAttribute("abc"),
|
||||
typeof(RegularExpressionAttributeAdapter)
|
||||
},
|
||||
{
|
||||
new MaxLengthAttribute(),
|
||||
typeof(MaxLengthAttributeAdapter)
|
||||
},
|
||||
{
|
||||
new MinLengthAttribute(1),
|
||||
typeof(MinLengthAttributeAdapter)
|
||||
},
|
||||
{
|
||||
new RangeAttribute(1, 100),
|
||||
typeof(RangeAttributeAdapter)
|
||||
},
|
||||
{
|
||||
new StringLengthAttribute(6),
|
||||
typeof(StringLengthAttributeAdapter)
|
||||
},
|
||||
{
|
||||
new RequiredAttribute(),
|
||||
typeof(RequiredAttributeAdapter)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(DataAnnotationAdapters))]
|
||||
public void AdapterFactory_RegistersAdapters_ForDataAnnotationAttributes(
|
||||
ValidationAttribute attribute,
|
||||
Type expectedAdapterType)
|
||||
{
|
||||
// Arrange and Act
|
||||
var adapter = _validationAttributeAdapterProvider.GetAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
// Assert
|
||||
Assert.IsType(expectedAdapterType, adapter);
|
||||
}
|
||||
|
||||
public static TheoryData<ValidationAttribute, string> DataTypeAdapters
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<ValidationAttribute, string> {
|
||||
{ new UrlAttribute(), "url" },
|
||||
{ new CreditCardAttribute(), "creditcard" },
|
||||
{ new EmailAddressAttribute(), "email" },
|
||||
{ new PhoneAttribute(), "phone" }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(DataTypeAdapters))]
|
||||
public void AdapterFactory_RegistersAdapters_ForDataTypeAttributes(
|
||||
ValidationAttribute attribute,
|
||||
string expectedRuleName)
|
||||
{
|
||||
// Arrange & Act
|
||||
var adapter = _validationAttributeAdapterProvider.GetAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
// Assert
|
||||
var dataTypeAdapter = Assert.IsType<DataTypeAttributeAdapter>(adapter);
|
||||
Assert.Equal(expectedRuleName, dataTypeAdapter.RuleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -84,10 +84,10 @@ mypartial
|
|||
"Learn More" + Environment.NewLine +
|
||||
"Hi John ! You are in 2015 year and today is Thursday";
|
||||
|
||||
yield return new[] {"en-GB", expected1 };
|
||||
yield return new[] { "en-GB", expected1 };
|
||||
|
||||
var expected2 =
|
||||
"Bonjour!" + Environment.NewLine +
|
||||
"Bonjour!" + Environment.NewLine +
|
||||
"apprendre Encore Plus" + Environment.NewLine +
|
||||
"Salut John ! Vous êtes en 2015 an aujourd'hui est Thursday";
|
||||
yield return new[] { "fr", expected2 };
|
||||
|
|
@ -118,10 +118,10 @@ mypartial
|
|||
{
|
||||
// Arrange
|
||||
var expected =
|
||||
@"<span class=""field-validation-error"" data-valmsg-for=""Name"" data-valmsg-replace=""true"">Nom non valide. Longueur minimale de nom est 4</span>
|
||||
@"<span class=""field-validation-error"" data-valmsg-for=""Name"" data-valmsg-replace=""true"">Nom non valide. Longueur minimale de nom est 6</span>
|
||||
<span class=""field-validation-error"" data-valmsg-for=""Product.ProductName"" data-valmsg-replace=""true"">Nom du produit est invalide</span>
|
||||
<div class=""editor-label""><label for=""Name"">Name</label></div>
|
||||
<div class=""editor-field""><input class=""input-validation-error text-box single-line"" data-val=""true"" data-val-minlength=""Nom non valide. Longueur minimale de nom est 4"" data-val-minlength-min=""4"" id=""Name"" name=""Name"" type=""text"" value=""A"" /> <span class=""field-validation-error"" data-valmsg-for=""Name"" data-valmsg-replace=""true"">Nom non valide. Longueur minimale de nom est 4</span></div>
|
||||
<div class=""editor-field""><input class=""input-validation-error text-box single-line"" data-val=""true"" data-val-minlength=""Nom non valide. Longueur minimale de nom est 6"" data-val-minlength-min=""6"" id=""Name"" name=""Name"" type=""text"" value=""A"" /> <span class=""field-validation-error"" data-valmsg-for=""Name"" data-valmsg-replace=""true"">Nom non valide. Longueur minimale de nom est 6</span></div>
|
||||
|
||||
<div class=""editor-label""><label for=""Product_ProductName"">ProductName</label></div>
|
||||
<div class=""editor-field""><input class=""input-validation-error text-box single-line"" data-val=""true"" data-val-required=""Nom du produit est invalide"" id=""Product_ProductName"" name=""Product.ProductName"" type=""text"" value="""" /> <span class=""field-validation-error"" data-valmsg-for=""Product.ProductName"" data-valmsg-replace=""true"">Nom du produit est invalide</span></div>";
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
// 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.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations.Internal;
|
||||
using Microsoft.AspNet.Mvc.Formatters.Json.Internal;
|
||||
using Microsoft.AspNet.Mvc.Internal;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.OptionsModel;
|
||||
|
|
@ -17,6 +20,9 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
Value = new MvcOptions();
|
||||
MvcCoreMvcOptionsSetup.ConfigureMvc(Value, new TestHttpRequestStreamReaderFactory());
|
||||
var collection = new ServiceCollection().AddOptions();
|
||||
collection.AddSingleton<ICompositeMetadataDetailsProvider, DefaultCompositeMetadataDetailsProvider>();
|
||||
collection.AddSingleton<IModelMetadataProvider, DefaultModelMetadataProvider>();
|
||||
collection.AddSingleton<IValidationAttributeAdapterProvider, ValidationAttributeAdapterProvider>();
|
||||
MvcDataAnnotationsMvcOptionsSetup.ConfigureMvc(
|
||||
Value,
|
||||
collection.BuildServiceProvider());
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
|
|
@ -14,6 +15,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
new DefaultClientModelValidatorProvider(),
|
||||
new DataAnnotationsClientModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||
{
|
||||
|
|
@ -14,8 +15,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
{
|
||||
new DefaultModelValidatorProvider(),
|
||||
new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null),
|
||||
stringLocalizerFactory: null)
|
||||
};
|
||||
|
||||
return new TestModelValidatorProvider(providers);
|
||||
|
|
|
|||
|
|
@ -1863,9 +1863,12 @@ namespace Microsoft.AspNet.Mvc.Test
|
|||
HttpContext = httpContext,
|
||||
ModelBinders = new[] { binder, },
|
||||
ValueProviders = new[] { valueProvider, },
|
||||
ValidatorProviders = new []
|
||||
ValidatorProviders = new[]
|
||||
{
|
||||
new DataAnnotationsModelValidatorProvider(options: null, stringLocalizerFactory: null),
|
||||
new DataAnnotationsModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||
stringLocalizerFactory: null),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNet.Antiforgery;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Mvc.Abstractions;
|
||||
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNet.Mvc.Routing;
|
||||
|
|
@ -226,6 +227,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
localizationOptionsAccesor.SetupGet(o => o.Value).Returns(new MvcDataAnnotationsLocalizationOptions());
|
||||
|
||||
options.ClientModelValidatorProviders.Add(new DataAnnotationsClientModelValidatorProvider(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
localizationOptionsAccesor.Object,
|
||||
stringLocalizerFactory: null));
|
||||
var optionsAccessor = new Mock<IOptions<MvcViewOptions>>();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace LocalizationWebSite.Models
|
|||
{
|
||||
public class User
|
||||
{
|
||||
[MinLength(4, ErrorMessage = "NameError")]
|
||||
[MinLength(6, ErrorMessage = "NameError")]
|
||||
public string Name { get; set; }
|
||||
|
||||
public Product Product { get; set; }
|
||||
|
|
|
|||
|
|
@ -118,6 +118,6 @@
|
|||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="NameError" xml:space="preserve">
|
||||
<value>Nom non valide. Longueur minimale de nom est 4</value>
|
||||
<value>Nom non valide. Longueur minimale de nom est {1}</value>
|
||||
</data>
|
||||
</root>
|
||||
Loading…
Reference in New Issue