* Parameters into the messages
This commit is contained in:
parent
a208c95a4f
commit
3393ba43c2
|
|
@ -1,41 +1,25 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
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(
|
public ClientModelValidationContext(
|
||||||
ActionContext actionContext,
|
ActionContext actionContext,
|
||||||
ModelMetadata metadata,
|
ModelMetadata metadata,
|
||||||
IModelMetadataProvider metadataProvider)
|
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>
|
/// <summary>
|
||||||
/// A context object for <see cref="IModelValidator"/>.
|
/// A context object for <see cref="IModelValidator"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ModelValidationContext
|
public class ModelValidationContext : ModelValidationContextBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="Mvc.ActionContext"/>
|
/// Create a new instance of <see cref="ModelValidationContext"/>.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Gets or sets the model object.
|
/// Gets the model object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object Model { get; set; }
|
public object Model { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the model container object.
|
/// Gets the model container object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object Container { get; set; }
|
public object Container { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the <see cref="ModelMetadata"/> associated with <see cref="Model"/>.
|
|
||||||
/// </summary>
|
|
||||||
public ModelMetadata Metadata { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -48,6 +48,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
var visitor = new ValidationVisitor(
|
var visitor = new ValidationVisitor(
|
||||||
actionContext,
|
actionContext,
|
||||||
validatorProvider,
|
validatorProvider,
|
||||||
|
_modelMetadataProvider,
|
||||||
validationState);
|
validationState);
|
||||||
|
|
||||||
var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType());
|
var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType());
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
public class ValidationVisitor
|
public class ValidationVisitor
|
||||||
{
|
{
|
||||||
private readonly IModelValidatorProvider _validatorProvider;
|
private readonly IModelValidatorProvider _validatorProvider;
|
||||||
|
private readonly IModelMetadataProvider _metadataProvider;
|
||||||
private readonly ActionContext _actionContext;
|
private readonly ActionContext _actionContext;
|
||||||
private readonly ModelStateDictionary _modelState;
|
private readonly ModelStateDictionary _modelState;
|
||||||
private readonly ValidationStateDictionary _validationState;
|
private readonly ValidationStateDictionary _validationState;
|
||||||
|
|
@ -36,6 +37,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
public ValidationVisitor(
|
public ValidationVisitor(
|
||||||
ActionContext actionContext,
|
ActionContext actionContext,
|
||||||
IModelValidatorProvider validatorProvider,
|
IModelValidatorProvider validatorProvider,
|
||||||
|
IModelMetadataProvider metadataProvider,
|
||||||
ValidationStateDictionary validationState)
|
ValidationStateDictionary validationState)
|
||||||
{
|
{
|
||||||
if (actionContext == null)
|
if (actionContext == null)
|
||||||
|
|
@ -50,6 +52,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
|
||||||
_actionContext = actionContext;
|
_actionContext = actionContext;
|
||||||
_validatorProvider = validatorProvider;
|
_validatorProvider = validatorProvider;
|
||||||
|
_metadataProvider = metadataProvider;
|
||||||
_validationState = validationState;
|
_validationState = validationState;
|
||||||
|
|
||||||
_modelState = actionContext.ModelState;
|
_modelState = actionContext.ModelState;
|
||||||
|
|
@ -92,13 +95,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
var count = validators.Count;
|
var count = validators.Count;
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
var context = new ModelValidationContext()
|
var context = new ModelValidationContext(
|
||||||
{
|
_actionContext,
|
||||||
ActionContext = _actionContext,
|
_metadata,
|
||||||
Container = _container,
|
_metadataProvider,
|
||||||
Model = _model,
|
_container,
|
||||||
Metadata = _metadata,
|
_model);
|
||||||
};
|
|
||||||
|
|
||||||
var results = new List<ModelValidationResult>();
|
var results = new List<ModelValidationResult>();
|
||||||
for (var i = 0; i < count; i++)
|
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
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
public class CompareAttributeAdapter : DataAnnotationsClientModelValidator<CompareAttribute>
|
public class CompareAttributeAdapter : AttributeAdapterBase<CompareAttribute>
|
||||||
{
|
{
|
||||||
public CompareAttributeAdapter(CompareAttribute attribute, IStringLocalizer stringLocalizer)
|
public CompareAttributeAdapter(CompareAttribute attribute, IStringLocalizer stringLocalizer)
|
||||||
: base(new CompareAttributeWrapper(attribute), stringLocalizer)
|
: base(new CompareAttributeWrapper(attribute), stringLocalizer)
|
||||||
|
|
@ -28,19 +28,35 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorMessage = ((CompareAttributeWrapper)Attribute).FormatErrorMessage(context);
|
var errorMessage = GetErrorMessage(context);
|
||||||
var clientRule = new ModelClientValidationEqualToRule(errorMessage,
|
var clientRule = new ModelClientValidationEqualToRule(errorMessage, "*." + Attribute.OtherProperty);
|
||||||
FormatPropertyForClientValidation(Attribute.OtherProperty));
|
|
||||||
return new[] { clientRule };
|
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
|
private sealed class CompareAttributeWrapper : CompareAttribute
|
||||||
{
|
{
|
||||||
|
public ModelValidationContextBase ValidationContext { get; set; }
|
||||||
|
|
||||||
public CompareAttributeWrapper(CompareAttribute attribute)
|
public CompareAttributeWrapper(CompareAttribute attribute)
|
||||||
: base(attribute.OtherProperty)
|
: 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,
|
return string.Format(CultureInfo.CurrentCulture,
|
||||||
ErrorMessageString,
|
ErrorMessageString,
|
||||||
displayName,
|
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
|
// 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.
|
// the error message for client validation, the display name is not populated and won't be used.
|
||||||
var metadata = context.ModelMetadata;
|
var otherPropertyDisplayName = attribute.OtherPropertyDisplayName;
|
||||||
var otherPropertyDisplayName = OtherPropertyDisplayName;
|
if (otherPropertyDisplayName == null && validationContext.ModelMetadata.ContainerType != null)
|
||||||
if (otherPropertyDisplayName == null && metadata.ContainerType != null)
|
|
||||||
{
|
{
|
||||||
var otherProperty = context.MetadataProvider.GetMetadataForProperty(
|
var otherProperty = validationContext.MetadataProvider.GetMetadataForProperty(
|
||||||
metadata.ContainerType,
|
validationContext.ModelMetadata.ContainerType,
|
||||||
OtherProperty);
|
attribute.OtherProperty);
|
||||||
if (otherProperty != null)
|
if (otherProperty != null)
|
||||||
{
|
{
|
||||||
return otherProperty.GetDisplayName();
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Microsoft.Extensions.OptionsModel;
|
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
|
/// for attributes which derive from <see cref="ValidationAttribute"/>. It also provides
|
||||||
/// a validator for types which implement <see cref="IClientModelValidator"/>.
|
/// a validator for types which implement <see cref="IClientModelValidator"/>.
|
||||||
/// The logic to support <see cref="IClientModelValidator"/>
|
/// The logic to support <see cref="IClientModelValidator"/>
|
||||||
/// is implemented in <see cref="DataAnnotationsClientModelValidator{}"/>.
|
/// is implemented in <see cref="ValidationAttributeAdapter{}"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DataAnnotationsClientModelValidatorProvider : IClientModelValidatorProvider
|
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 IOptions<MvcDataAnnotationsLocalizationOptions> _options;
|
||||||
private readonly IStringLocalizerFactory _stringLocalizerFactory;
|
private readonly IStringLocalizerFactory _stringLocalizerFactory;
|
||||||
|
private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new instance of <see cref="DataAnnotationsClientModelValidatorProvider"/>.
|
/// 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="options">The <see cref="IOptions{MvcDataAnnotationsLocalizationOptions}"/>.</param>
|
||||||
/// <param name="stringLocalizerFactory">The <see cref="IStringLocalizerFactory"/>.</param>
|
/// <param name="stringLocalizerFactory">The <see cref="IStringLocalizerFactory"/>.</param>
|
||||||
public DataAnnotationsClientModelValidatorProvider(
|
public DataAnnotationsClientModelValidatorProvider(
|
||||||
|
IValidationAttributeAdapterProvider validationAttributeAdapterProvider,
|
||||||
IOptions<MvcDataAnnotationsLocalizationOptions> options,
|
IOptions<MvcDataAnnotationsLocalizationOptions> options,
|
||||||
IStringLocalizerFactory stringLocalizerFactory)
|
IStringLocalizerFactory stringLocalizerFactory)
|
||||||
{
|
{
|
||||||
|
if (validationAttributeAdapterProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(validationAttributeAdapterProvider));
|
||||||
|
}
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
_validationAttributeAdapterProvider = validationAttributeAdapterProvider;
|
||||||
_options = options;
|
_options = options;
|
||||||
_stringLocalizerFactory = stringLocalizerFactory;
|
_stringLocalizerFactory = stringLocalizerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Dictionary<Type, DataAnnotationsClientModelValidationFactory> AttributeFactories
|
|
||||||
{
|
|
||||||
get { return _attributeFactories; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void GetValidators(ClientValidatorProviderContext context)
|
public void GetValidators(ClientValidatorProviderContext context)
|
||||||
{
|
{
|
||||||
|
|
@ -70,10 +70,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
hasRequiredAttribute |= attribute is RequiredAttribute;
|
hasRequiredAttribute |= attribute is RequiredAttribute;
|
||||||
|
|
||||||
DataAnnotationsClientModelValidationFactory factory;
|
var adapter = _validationAttributeAdapterProvider.GetAttributeAdapter(attribute, stringLocalizer);
|
||||||
if (_attributeFactories.TryGetValue(attribute.GetType(), out factory))
|
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));
|
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.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Validates based on the given <see cref="ValidationAttribute"/>.
|
||||||
|
/// </summary>
|
||||||
public class DataAnnotationsModelValidator : IModelValidator
|
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)
|
if (attribute == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(attribute));
|
throw new ArgumentNullException(nameof(attribute));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_validationAttributeAdapterProvider = validationAttributeAdapterProvider;
|
||||||
Attribute = attribute;
|
Attribute = attribute;
|
||||||
_stringLocalizer = stringLocalizer;
|
_stringLocalizer = stringLocalizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The attribute being validated against.
|
||||||
|
/// </summary>
|
||||||
public ValidationAttribute Attribute { get; }
|
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)
|
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 memberName = metadata.PropertyName ?? metadata.ModelType.Name;
|
||||||
var container = validationContext.Container;
|
var container = validationContext.Container;
|
||||||
|
|
||||||
|
|
@ -61,8 +111,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
string.IsNullOrEmpty(Attribute.ErrorMessageResourceName) &&
|
string.IsNullOrEmpty(Attribute.ErrorMessageResourceName) &&
|
||||||
Attribute.ErrorMessageResourceType == null)
|
Attribute.ErrorMessageResourceType == null)
|
||||||
{
|
{
|
||||||
var displayName = validationContext.Metadata.GetDisplayName();
|
errorMessage = GetErrorMessage(validationContext);
|
||||||
errorMessage = _stringLocalizer[Attribute.ErrorMessage, displayName];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var validationResult = new ModelValidationResult(errorMemberName, errorMessage ?? result.ErrorMessage);
|
var validationResult = new ModelValidationResult(errorMemberName, errorMessage ?? result.ErrorMessage);
|
||||||
|
|
@ -71,5 +120,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
|
||||||
return Enumerable.Empty<ModelValidationResult>();
|
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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
#if DOTNET5_4
|
#if DOTNET5_4
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -19,16 +21,30 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
private readonly IOptions<MvcDataAnnotationsLocalizationOptions> _options;
|
private readonly IOptions<MvcDataAnnotationsLocalizationOptions> _options;
|
||||||
private readonly IStringLocalizerFactory _stringLocalizerFactory;
|
private readonly IStringLocalizerFactory _stringLocalizerFactory;
|
||||||
|
private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new instance of <see cref="DataAnnotationsModelValidatorProvider"/>.
|
/// Create a new instance of <see cref="DataAnnotationsModelValidatorProvider"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="options">The <see cref="IOptions{MvcDataAnnotationsLocalizationOptions}"/>.</param>
|
/// <param name="options">The <see cref="IOptions{MvcDataAnnotationsLocalizationOptions}"/>.</param>
|
||||||
/// <param name="stringLocalizerFactory">The <see cref="IStringLocalizerFactory"/>.</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(
|
public DataAnnotationsModelValidatorProvider(
|
||||||
|
IValidationAttributeAdapterProvider validationAttributeAdapterProvider,
|
||||||
IOptions<MvcDataAnnotationsLocalizationOptions> options,
|
IOptions<MvcDataAnnotationsLocalizationOptions> options,
|
||||||
IStringLocalizerFactory stringLocalizerFactory)
|
IStringLocalizerFactory stringLocalizerFactory)
|
||||||
{
|
{
|
||||||
|
if (validationAttributeAdapterProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(validationAttributeAdapterProvider));
|
||||||
|
}
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
_validationAttributeAdapterProvider = validationAttributeAdapterProvider;
|
||||||
_options = options;
|
_options = options;
|
||||||
_stringLocalizerFactory = stringLocalizerFactory;
|
_stringLocalizerFactory = stringLocalizerFactory;
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +67,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
continue;
|
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
|
// 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.
|
// '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
|
/// A validation adapter that is used to map <see cref="DataTypeAttribute"/>'s to a single client side validation
|
||||||
/// rule.
|
/// rule.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DataTypeAttributeAdapter : DataAnnotationsClientModelValidator<DataTypeAttribute>
|
public class DataTypeAttributeAdapter : AttributeAdapterBase<DataTypeAttribute>
|
||||||
{
|
{
|
||||||
public DataTypeAttributeAdapter(DataTypeAttribute attribute, string ruleName, IStringLocalizer stringLocalizer)
|
public DataTypeAttributeAdapter(DataTypeAttribute attribute, string ruleName, IStringLocalizer stringLocalizer)
|
||||||
: base(attribute, stringLocalizer)
|
: base(attribute, stringLocalizer)
|
||||||
|
|
@ -36,8 +36,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorMessage = GetErrorMessage(context.ModelMetadata);
|
var errorMessage = GetErrorMessage(context);
|
||||||
return new[] { new ModelClientValidationRule(RuleName, errorMessage) };
|
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 System;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.AspNet.Mvc.DataAnnotations.Internal;
|
using Microsoft.AspNet.Mvc.DataAnnotations.Internal;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Microsoft.Extensions.OptionsModel;
|
using Microsoft.Extensions.OptionsModel;
|
||||||
|
|
@ -71,6 +73,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
{
|
{
|
||||||
services.TryAddEnumerable(
|
services.TryAddEnumerable(
|
||||||
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcDataAnnotationsMvcOptionsSetup>());
|
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcDataAnnotationsMvcOptionsSetup>());
|
||||||
|
services.TryAddSingleton<IValidationAttributeAdapterProvider, ValidationAttributeAdapterProvider>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing.
|
// 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.
|
// This service will be registered only if AddDataAnnotationsLocalization() is added to service collection.
|
||||||
var stringLocalizerFactory = serviceProvider.GetService<IStringLocalizerFactory>();
|
var stringLocalizerFactory = serviceProvider.GetService<IStringLocalizerFactory>();
|
||||||
|
var validationAttributeAdapterProvider = serviceProvider.GetRequiredService<IValidationAttributeAdapterProvider>();
|
||||||
|
|
||||||
options.ModelMetadataDetailsProviders.Add(new DataAnnotationsMetadataProvider());
|
options.ModelMetadataDetailsProviders.Add(new DataAnnotationsMetadataProvider());
|
||||||
|
|
||||||
options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider(
|
options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider(
|
||||||
|
validationAttributeAdapterProvider,
|
||||||
dataAnnotationLocalizationOptions,
|
dataAnnotationLocalizationOptions,
|
||||||
stringLocalizerFactory));
|
stringLocalizerFactory));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
public class MaxLengthAttributeAdapter : DataAnnotationsClientModelValidator<MaxLengthAttribute>
|
public class MaxLengthAttributeAdapter : AttributeAdapterBase<MaxLengthAttribute>
|
||||||
{
|
{
|
||||||
public MaxLengthAttributeAdapter(MaxLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
public MaxLengthAttributeAdapter(MaxLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
||||||
: base(attribute, stringLocalizer)
|
: base(attribute, stringLocalizer)
|
||||||
|
|
@ -23,8 +24,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var message = GetErrorMessage(context.ModelMetadata);
|
var message = GetErrorMessage(context);
|
||||||
return new[] { new ModelClientValidationMaxLengthRule(message, Attribute.Length) };
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
public class MinLengthAttributeAdapter : DataAnnotationsClientModelValidator<MinLengthAttribute>
|
public class MinLengthAttributeAdapter : AttributeAdapterBase<MinLengthAttribute>
|
||||||
{
|
{
|
||||||
public MinLengthAttributeAdapter(MinLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
public MinLengthAttributeAdapter(MinLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
||||||
: base(attribute, stringLocalizer)
|
: base(attribute, stringLocalizer)
|
||||||
|
|
@ -23,8 +24,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var message = GetErrorMessage(context.ModelMetadata);
|
var message = GetErrorMessage(context);
|
||||||
return new[] { new ModelClientValidationMinLengthRule(message, Attribute.Length) };
|
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);
|
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)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
public class RangeAttributeAdapter : DataAnnotationsClientModelValidator<RangeAttribute>
|
public class RangeAttributeAdapter : AttributeAdapterBase<RangeAttribute>
|
||||||
{
|
{
|
||||||
public RangeAttributeAdapter(RangeAttribute attribute, IStringLocalizer stringLocalizer)
|
public RangeAttributeAdapter(RangeAttribute attribute, IStringLocalizer stringLocalizer)
|
||||||
: base(attribute, stringLocalizer)
|
: base(attribute, stringLocalizer)
|
||||||
|
|
@ -23,8 +24,28 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
throw new ArgumentNullException(nameof(context));
|
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) };
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
public class RegularExpressionAttributeAdapter : DataAnnotationsClientModelValidator<RegularExpressionAttribute>
|
public class RegularExpressionAttributeAdapter : AttributeAdapterBase<RegularExpressionAttribute>
|
||||||
{
|
{
|
||||||
public RegularExpressionAttributeAdapter(RegularExpressionAttribute attribute, IStringLocalizer stringLocalizer)
|
public RegularExpressionAttributeAdapter(RegularExpressionAttribute attribute, IStringLocalizer stringLocalizer)
|
||||||
: base(attribute, stringLocalizer)
|
: base(attribute, stringLocalizer)
|
||||||
|
|
@ -23,8 +24,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorMessage = GetErrorMessage(context.ModelMetadata);
|
var errorMessage = GetErrorMessage(context);
|
||||||
return new[] { new ModelClientValidationRegexRule(errorMessage, Attribute.Pattern) };
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
public class RequiredAttributeAdapter : DataAnnotationsClientModelValidator<RequiredAttribute>
|
public class RequiredAttributeAdapter : AttributeAdapterBase<RequiredAttribute>
|
||||||
{
|
{
|
||||||
public RequiredAttributeAdapter(RequiredAttribute attribute, IStringLocalizer stringLocalizer)
|
public RequiredAttributeAdapter(RequiredAttribute attribute, IStringLocalizer stringLocalizer)
|
||||||
: base(attribute, stringLocalizer)
|
: base(attribute, stringLocalizer)
|
||||||
|
|
@ -23,8 +24,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorMessage = GetErrorMessage(context.ModelMetadata);
|
var errorMessage = GetErrorMessage(context);
|
||||||
return new[] { new ModelClientValidationRequiredRule(errorMessage) };
|
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">
|
<data name="NumericClientModelValidator_FieldMustBeNumber" xml:space="preserve">
|
||||||
<value>The field {0} must be a number.</value>
|
<value>The field {0} must be a number.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PropertyOfTypeCannotBeNull" xml:space="preserve">
|
||||||
|
<value>The '{0}' property of '{1}' must not be null.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -4,11 +4,12 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
public class StringLengthAttributeAdapter : DataAnnotationsClientModelValidator<StringLengthAttribute>
|
public class StringLengthAttributeAdapter : AttributeAdapterBase<StringLengthAttribute>
|
||||||
{
|
{
|
||||||
public StringLengthAttributeAdapter(StringLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
public StringLengthAttributeAdapter(StringLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
||||||
: base(attribute, stringLocalizer)
|
: base(attribute, stringLocalizer)
|
||||||
|
|
@ -23,11 +24,26 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorMessage = GetErrorMessage(context.ModelMetadata);
|
var errorMessage = GetErrorMessage(context);
|
||||||
var rule = new ModelClientValidationStringLengthRule(errorMessage,
|
var rule = new ModelClientValidationStringLengthRule(errorMessage,
|
||||||
Attribute.MinimumLength,
|
Attribute.MinimumLength,
|
||||||
Attribute.MaximumLength);
|
Attribute.MaximumLength);
|
||||||
return new[] { rule };
|
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.
|
/// An implementation of <see cref="IClientModelValidator"/> which understands data annotation attributes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TAttribute">The type of the attribute.</typeparam>
|
/// <typeparam name="TAttribute">The type of the attribute.</typeparam>
|
||||||
public abstract class DataAnnotationsClientModelValidator<TAttribute> : IClientModelValidator
|
public abstract class ValidationAttributeAdapter<TAttribute> : IClientModelValidator
|
||||||
where TAttribute : ValidationAttribute
|
where TAttribute : ValidationAttribute
|
||||||
{
|
{
|
||||||
private readonly IStringLocalizer _stringLocalizer;
|
private readonly IStringLocalizer _stringLocalizer;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new instance of <see cref="DataAnnotationsClientModelValidator{TAttribute}"/>.
|
/// Create a new instance of <see cref="ValidationAttributeAdapter{TAttribute}"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="attribute">The <typeparamref name="TAttribute"/> instance to validate.</param>
|
/// <param name="attribute">The <typeparamref name="TAttribute"/> instance to validate.</param>
|
||||||
/// <param name="stringLocalizer">The <see cref="IStringLocalizer"/>.</param>
|
/// <param name="stringLocalizer">The <see cref="IStringLocalizer"/>.</param>
|
||||||
public DataAnnotationsClientModelValidator(TAttribute attribute, IStringLocalizer stringLocalizer)
|
public ValidationAttributeAdapter(TAttribute attribute, IStringLocalizer stringLocalizer)
|
||||||
{
|
{
|
||||||
Attribute = attribute;
|
Attribute = attribute;
|
||||||
_stringLocalizer = stringLocalizer;
|
_stringLocalizer = stringLocalizer;
|
||||||
|
|
@ -44,25 +44,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="modelMetadata">The <see cref="ModelMetadata"/> associated with the model annotated with
|
/// <param name="modelMetadata">The <see cref="ModelMetadata"/> associated with the model annotated with
|
||||||
/// <see cref="Attribute"/>.</param>
|
/// <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>
|
/// <returns>Formatted error string.</returns>
|
||||||
protected virtual string GetErrorMessage(ModelMetadata modelMetadata)
|
protected virtual string GetErrorMessage(ModelMetadata modelMetadata, params object[] arguments)
|
||||||
{
|
{
|
||||||
if (modelMetadata == null)
|
if (modelMetadata == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(modelMetadata));
|
throw new ArgumentNullException(nameof(modelMetadata));
|
||||||
}
|
}
|
||||||
|
|
||||||
var displayName = modelMetadata.GetDisplayName();
|
|
||||||
|
|
||||||
if (_stringLocalizer != null &&
|
if (_stringLocalizer != null &&
|
||||||
!string.IsNullOrEmpty(Attribute.ErrorMessage) &&
|
!string.IsNullOrEmpty(Attribute.ErrorMessage) &&
|
||||||
string.IsNullOrEmpty(Attribute.ErrorMessageResourceName) &&
|
string.IsNullOrEmpty(Attribute.ErrorMessageResourceName) &&
|
||||||
Attribute.ErrorMessageResourceType == null)
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
@ -29,10 +30,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures.Internal
|
||||||
var dataAnnotationsLocalizationOptions =
|
var dataAnnotationsLocalizationOptions =
|
||||||
serviceProvider.GetRequiredService<IOptions<MvcDataAnnotationsLocalizationOptions>>();
|
serviceProvider.GetRequiredService<IOptions<MvcDataAnnotationsLocalizationOptions>>();
|
||||||
var stringLocalizerFactory = serviceProvider.GetService<IStringLocalizerFactory>();
|
var stringLocalizerFactory = serviceProvider.GetService<IStringLocalizerFactory>();
|
||||||
|
var validationAttributeAdapterProvider = serviceProvider.GetRequiredService<IValidationAttributeAdapterProvider>();
|
||||||
|
|
||||||
// Set up client validators
|
// Set up client validators
|
||||||
options.ClientModelValidatorProviders.Add(new DefaultClientModelValidatorProvider());
|
options.ClientModelValidatorProviders.Add(new DefaultClientModelValidatorProvider());
|
||||||
options.ClientModelValidatorProviders.Add(new DataAnnotationsClientModelValidatorProvider(
|
options.ClientModelValidatorProviders.Add(new DataAnnotationsClientModelValidatorProvider(
|
||||||
|
validationAttributeAdapterProvider,
|
||||||
dataAnnotationsLocalizationOptions,
|
dataAnnotationsLocalizationOptions,
|
||||||
stringLocalizerFactory));
|
stringLocalizerFactory));
|
||||||
options.ClientModelValidatorProviders.Add(new NumericClientModelValidatorProvider());
|
options.ClientModelValidatorProviders.Add(new NumericClientModelValidatorProvider());
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -230,6 +231,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
ModelBinder = innerBinder ?? CreateIntBinder(),
|
ModelBinder = innerBinder ?? CreateIntBinder(),
|
||||||
MetadataProvider = metataProvider,
|
MetadataProvider = metataProvider,
|
||||||
ValidatorProvider = new DataAnnotationsModelValidatorProvider(
|
ValidatorProvider = new DataAnnotationsModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null)
|
stringLocalizerFactory: null)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Globalization;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.AspNet.Mvc.Formatters;
|
using Microsoft.AspNet.Mvc.Formatters;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
|
|
@ -72,6 +73,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
};
|
};
|
||||||
|
|
||||||
var validator = new DataAnnotationsModelValidatorProvider(
|
var validator = new DataAnnotationsModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
var model = new MyModel();
|
var model = new MyModel();
|
||||||
|
|
@ -115,6 +117,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
};
|
};
|
||||||
|
|
||||||
var validator = new DataAnnotationsModelValidatorProvider(
|
var validator = new DataAnnotationsModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
var model = new MyModel { MyProperty = "Old-Value" };
|
var model = new MyModel { MyProperty = "Old-Value" };
|
||||||
|
|
@ -189,9 +192,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
};
|
};
|
||||||
|
|
||||||
var validator = new DataAnnotationsModelValidatorProvider(
|
var validator = new DataAnnotationsModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
var model = new MyModel {
|
var model = new MyModel
|
||||||
|
{
|
||||||
MyProperty = "Old-Value",
|
MyProperty = "Old-Value",
|
||||||
IncludedProperty = "Old-IncludedPropertyValue",
|
IncludedProperty = "Old-IncludedPropertyValue",
|
||||||
ExcludedProperty = "Old-ExcludedPropertyValue"
|
ExcludedProperty = "Old-ExcludedPropertyValue"
|
||||||
|
|
@ -256,7 +261,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
new List<IInputFormatter>(),
|
new List<IInputFormatter>(),
|
||||||
new Mock<IObjectModelValidator>(MockBehavior.Strict).Object,
|
new Mock<IObjectModelValidator>(MockBehavior.Strict).Object,
|
||||||
Mock.Of<IModelValidatorProvider>(),
|
Mock.Of<IModelValidatorProvider>(),
|
||||||
m => m.IncludedProperty );
|
m => m.IncludedProperty);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result);
|
Assert.False(result);
|
||||||
|
|
@ -276,6 +281,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
};
|
};
|
||||||
|
|
||||||
var validator = new DataAnnotationsModelValidatorProvider(
|
var validator = new DataAnnotationsModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
var model = new MyModel
|
var model = new MyModel
|
||||||
|
|
@ -328,6 +334,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
};
|
};
|
||||||
|
|
||||||
var validator = new DataAnnotationsModelValidatorProvider(
|
var validator = new DataAnnotationsModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
var model = new MyModel
|
var model = new MyModel
|
||||||
|
|
@ -533,6 +540,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
};
|
};
|
||||||
|
|
||||||
var validator = new DataAnnotationsModelValidatorProvider(
|
var validator = new DataAnnotationsModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
var model = new MyModel
|
var model = new MyModel
|
||||||
|
|
@ -621,6 +629,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
};
|
};
|
||||||
|
|
||||||
var validator = new DataAnnotationsModelValidatorProvider(
|
var validator = new DataAnnotationsModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
var model = new MyModel { MyProperty = "Old-Value" };
|
var model = new MyModel { MyProperty = "Old-Value" };
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
using Microsoft.AspNet.Testing.xunit;
|
using Microsoft.AspNet.Testing.xunit;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
|
@ -13,7 +14,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void ClientRulesWithCompareAttribute_ErrorMessageUsesDisplayName()
|
public void ClientRulesWithCompareAttribute_ErrorMessageUsesDisplayName_WithoutLocalizer()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
@ -37,6 +38,39 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
rule.ErrorMessage);
|
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]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void ClientRulesWithCompareAttribute_ErrorMessageUsesPropertyName()
|
public void ClientRulesWithCompareAttribute_ErrorMessageUsesPropertyName()
|
||||||
|
|
@ -67,7 +101,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var metadata = metadataProvider.GetMetadataForProperty( typeof(PropertyNameModel), "MyProperty");
|
var metadata = metadataProvider.GetMetadataForProperty(typeof(PropertyNameModel), "MyProperty");
|
||||||
|
|
||||||
var attribute = new CompareAttribute("OtherProperty")
|
var attribute = new CompareAttribute("OtherProperty")
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
|
@ -18,6 +19,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = new DataAnnotationsClientModelValidatorProvider(
|
var provider = new DataAnnotationsClientModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
|
|
||||||
|
|
@ -40,6 +42,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = new DataAnnotationsClientModelValidatorProvider(
|
var provider = new DataAnnotationsClientModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
|
|
||||||
|
|
@ -61,6 +64,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = new DataAnnotationsClientModelValidatorProvider(
|
var provider = new DataAnnotationsClientModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
|
|
||||||
|
|
@ -79,105 +83,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
Assert.Equal("Custom Required Message", adapter.Attribute.ErrorMessage);
|
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]
|
[Fact]
|
||||||
public void UnknownValidationAttribute_IsNotAddedAsValidator()
|
public void UnknownValidationAttribute_IsNotAddedAsValidator()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = new DataAnnotationsClientModelValidatorProvider(
|
var provider = new DataAnnotationsClientModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
var metadata = _metadataProvider.GetMetadataForType(typeof(DummyClassWithDummyValidationAttribute));
|
var metadata = _metadataProvider.GetMetadataForType(typeof(DummyClassWithDummyValidationAttribute));
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -17,6 +18,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = new DataAnnotationsModelValidatorProvider(
|
var provider = new DataAnnotationsModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
var mockValidatable = Mock.Of<IValidatableObject>();
|
var mockValidatable = Mock.Of<IValidatableObject>();
|
||||||
|
|
@ -36,6 +38,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
public void GetValidators_InsertsRequiredValidatorsFirst()
|
public void GetValidators_InsertsRequiredValidatorsFirst()
|
||||||
{
|
{
|
||||||
var provider = new DataAnnotationsModelValidatorProvider(
|
var provider = new DataAnnotationsModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
var metadata = _metadataProvider.GetMetadataForProperty(
|
var metadata = _metadataProvider.GetMetadataForProperty(
|
||||||
|
|
@ -58,6 +61,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = new DataAnnotationsModelValidatorProvider(
|
var provider = new DataAnnotationsModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
var metadata = _metadataProvider.GetMetadataForType(typeof(DummyClassWithDummyValidationAttribute));
|
var metadata = _metadataProvider.GetMetadataForType(typeof(DummyClassWithDummyValidationAttribute));
|
||||||
|
|
@ -88,6 +92,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = new DataAnnotationsModelValidatorProvider(
|
var provider = new DataAnnotationsModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null);
|
stringLocalizerFactory: null);
|
||||||
var mockValidatable = new Mock<IValidatableObject>();
|
var mockValidatable = new Mock<IValidatableObject>();
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -21,7 +23,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
var attribute = new RequiredAttribute();
|
var attribute = new RequiredAttribute();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var validator = new DataAnnotationsModelValidator(attribute, stringLocalizer: null);
|
var validator = new DataAnnotationsModelValidator(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
|
attribute,
|
||||||
|
stringLocalizer: null);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Same(attribute, validator.Attribute);
|
Assert.Same(attribute, validator.Attribute);
|
||||||
|
|
@ -67,13 +72,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
})
|
})
|
||||||
.Returns(ValidationResult.Success)
|
.Returns(ValidationResult.Success)
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
|
var validator = new DataAnnotationsModelValidator(
|
||||||
var validationContext = new ModelValidationContext()
|
new ValidationAttributeAdapterProvider(),
|
||||||
{
|
attribute.Object,
|
||||||
Metadata = metadata,
|
stringLocalizer: null);
|
||||||
Container = container,
|
var validationContext = new ModelValidationContext(
|
||||||
Model = model,
|
actionContext: new ActionContext(),
|
||||||
};
|
modelMetadata: metadata,
|
||||||
|
metadataProvider: _metadataProvider,
|
||||||
|
container: container,
|
||||||
|
model: model);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var results = validator.Validate(validationContext);
|
var results = validator.Validate(validationContext);
|
||||||
|
|
@ -94,13 +102,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
||||||
attribute.Setup(a => a.IsValid(model)).Returns(true);
|
attribute.Setup(a => a.IsValid(model)).Returns(true);
|
||||||
|
|
||||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
|
var validator = new DataAnnotationsModelValidator(
|
||||||
var validationContext = new ModelValidationContext()
|
new ValidationAttributeAdapterProvider(),
|
||||||
{
|
attribute.Object,
|
||||||
Metadata = metadata,
|
stringLocalizer: null);
|
||||||
Container = container,
|
var validationContext = new ModelValidationContext(
|
||||||
Model = model,
|
actionContext: new ActionContext(),
|
||||||
};
|
modelMetadata: metadata,
|
||||||
|
metadataProvider: _metadataProvider,
|
||||||
|
container: container,
|
||||||
|
model: model);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = validator.Validate(validationContext);
|
var result = validator.Validate(validationContext);
|
||||||
|
|
@ -120,13 +131,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
||||||
attribute.Setup(a => a.IsValid(model)).Returns(false);
|
attribute.Setup(a => a.IsValid(model)).Returns(false);
|
||||||
|
|
||||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
|
var validator = new DataAnnotationsModelValidator(
|
||||||
var validationContext = new ModelValidationContext()
|
new ValidationAttributeAdapterProvider(),
|
||||||
{
|
attribute.Object,
|
||||||
Metadata = metadata,
|
stringLocalizer: null);
|
||||||
Container = container,
|
var validationContext = new ModelValidationContext(
|
||||||
Model = model,
|
actionContext: new ActionContext(),
|
||||||
};
|
modelMetadata: metadata,
|
||||||
|
metadataProvider: _metadataProvider,
|
||||||
|
container: container,
|
||||||
|
model: model);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = validator.Validate(validationContext);
|
var result = validator.Validate(validationContext);
|
||||||
|
|
@ -149,13 +163,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
attribute
|
attribute
|
||||||
.Setup(p => p.IsValidPublic(It.IsAny<object>(), It.IsAny<ValidationContext>()))
|
.Setup(p => p.IsValidPublic(It.IsAny<object>(), It.IsAny<ValidationContext>()))
|
||||||
.Returns(ValidationResult.Success);
|
.Returns(ValidationResult.Success);
|
||||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
|
var validator = new DataAnnotationsModelValidator(
|
||||||
var validationContext = new ModelValidationContext()
|
new ValidationAttributeAdapterProvider(),
|
||||||
{
|
attribute.Object,
|
||||||
Metadata = metadata,
|
stringLocalizer: null);
|
||||||
Container = container,
|
var validationContext = new ModelValidationContext(
|
||||||
Model = model,
|
actionContext: new ActionContext(),
|
||||||
};
|
modelMetadata: metadata,
|
||||||
|
metadataProvider: _metadataProvider,
|
||||||
|
container: container,
|
||||||
|
model: model);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = validator.Validate(validationContext);
|
var result = validator.Validate(validationContext);
|
||||||
|
|
@ -178,14 +195,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
attribute
|
attribute
|
||||||
.Setup(p => p.IsValidPublic(It.IsAny<object>(), It.IsAny<ValidationContext>()))
|
.Setup(p => p.IsValidPublic(It.IsAny<object>(), It.IsAny<ValidationContext>()))
|
||||||
.Returns(new ValidationResult(errorMessage, memberNames: null));
|
.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()
|
var validationContext = new ModelValidationContext(
|
||||||
{
|
actionContext: new ActionContext(),
|
||||||
Metadata = metadata,
|
modelMetadata: metadata,
|
||||||
Container = container,
|
metadataProvider: _metadataProvider,
|
||||||
Model = model,
|
container: container,
|
||||||
};
|
model: model);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var results = validator.Validate(validationContext);
|
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>()))
|
.Setup(p => p.IsValidPublic(It.IsAny<object>(), It.IsAny<ValidationContext>()))
|
||||||
.Returns(new ValidationResult(errorMessage, new[] { "FirstName" }));
|
.Returns(new ValidationResult(errorMessage, new[] { "FirstName" }));
|
||||||
|
|
||||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
|
var validator = new DataAnnotationsModelValidator(
|
||||||
var validationContext = new ModelValidationContext()
|
new ValidationAttributeAdapterProvider(),
|
||||||
{
|
attribute.Object,
|
||||||
Metadata = metadata,
|
stringLocalizer: null);
|
||||||
Model = model,
|
var validationContext = new ModelValidationContext(
|
||||||
};
|
actionContext: new ActionContext(),
|
||||||
|
modelMetadata: metadata,
|
||||||
|
metadataProvider: _metadataProvider,
|
||||||
|
container: null,
|
||||||
|
model: model);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var results = validator.Validate(validationContext);
|
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>()))
|
.Setup(p => p.IsValidPublic(It.IsAny<object>(), It.IsAny<ValidationContext>()))
|
||||||
.Returns(new ValidationResult("Name error", new[] { "Name" }));
|
.Returns(new ValidationResult("Name error", new[] { "Name" }));
|
||||||
|
|
||||||
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
|
var validator = new DataAnnotationsModelValidator(
|
||||||
var validationContext = new ModelValidationContext()
|
new ValidationAttributeAdapterProvider(),
|
||||||
{
|
attribute.Object,
|
||||||
Metadata = metadata,
|
stringLocalizer: null);
|
||||||
Model = model,
|
var validationContext = new ModelValidationContext(
|
||||||
};
|
actionContext: new ActionContext(),
|
||||||
|
modelMetadata: metadata,
|
||||||
|
metadataProvider: _metadataProvider,
|
||||||
|
container: null,
|
||||||
|
model: model);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var results = validator.Validate(validationContext);
|
var results = validator.Validate(validationContext);
|
||||||
|
|
@ -259,24 +287,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
// Arrange
|
// Arrange
|
||||||
var metadata = _metadataProvider.GetMetadataForType(typeof(string));
|
var metadata = _metadataProvider.GetMetadataForType(typeof(string));
|
||||||
var container = "Hello";
|
var container = "Hello";
|
||||||
var model = container.Length;
|
|
||||||
|
|
||||||
var attribute = new Mock<ValidationAttribute> { CallBase = true };
|
var attribute = new MaxLengthAttribute(4);
|
||||||
attribute.Setup(a => a.IsValid(model)).Returns(false);
|
attribute.ErrorMessage = "{0} should have no more than {1} characters.";
|
||||||
|
|
||||||
attribute.Object.ErrorMessage = "Length";
|
var localizedString = new LocalizedString(attribute.ErrorMessage, "Longueur est invalide : 4");
|
||||||
|
|
||||||
var localizedString = new LocalizedString("Length", "Longueur est invalide");
|
|
||||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
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 validator = new DataAnnotationsModelValidator(
|
||||||
var validationContext = new ModelValidationContext()
|
new ValidationAttributeAdapterProvider(),
|
||||||
{
|
attribute,
|
||||||
Metadata = metadata,
|
stringLocalizer.Object);
|
||||||
Container = container,
|
var validationContext = new ModelValidationContext(
|
||||||
Model = model,
|
actionContext: new ActionContext(),
|
||||||
};
|
modelMetadata: metadata,
|
||||||
|
metadataProvider: _metadataProvider,
|
||||||
|
container: container,
|
||||||
|
model: "abcde");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = validator.Validate(validationContext);
|
var result = validator.Validate(validationContext);
|
||||||
|
|
@ -284,11 +312,108 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
// Assert
|
// Assert
|
||||||
var validationResult = result.Single();
|
var validationResult = result.Single();
|
||||||
Assert.Equal("", validationResult.MemberName);
|
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
|
public abstract class TestableValidationAttribute : ValidationAttribute
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -12,6 +11,40 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
public class MaxLengthAttributeAdapterTest
|
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]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void ClientRulesWithMaxLengthAttribute()
|
public void ClientRulesWithMaxLengthAttribute()
|
||||||
|
|
@ -77,7 +110,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
attribute.ErrorMessage = errorKey;
|
attribute.ErrorMessage = errorKey;
|
||||||
var localizedString = new LocalizedString(errorKey, "Longueur est invalide");
|
var localizedString = new LocalizedString(errorKey, "Longueur est invalide");
|
||||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
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);
|
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer.Object);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,48 @@
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
public class MinLengthAttributeAdapterTest
|
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]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void ClientRulesWithMinLengthAttribute()
|
public void ClientRulesWithMinLengthAttribute()
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
|
@ -11,13 +13,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void GetClientValidationRules_ReturnsValidationParameters()
|
public void GetClientValidationRules_ReturnsValidationParameters_WithoutLocalization()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
||||||
|
|
||||||
var attribute = new RangeAttribute(typeof(decimal), "0", "100");
|
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 adapter = new RangeAttributeAdapter(attribute, stringLocalizer: null);
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
|
|
@ -32,7 +38,42 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||||
Assert.Equal(0m, rule.ValidationParameters["min"]);
|
Assert.Equal(0m, rule.ValidationParameters["min"]);
|
||||||
Assert.Equal(100m, rule.ValidationParameters["max"]);
|
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 System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
public class RequiredAttributeAdapterTest
|
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]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void GetClientValidationRules_ReturnsValidationParameters()
|
public void GetClientValidationRules_ReturnsValidationParameters()
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,49 @@
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
public class StringLengthAttributeAdapterTest
|
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]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void GetClientValidationRules_WithMaxLength_ReturnsValidationParameters()
|
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,7 +84,7 @@ mypartial
|
||||||
"Learn More" + Environment.NewLine +
|
"Learn More" + Environment.NewLine +
|
||||||
"Hi John ! You are in 2015 year and today is Thursday";
|
"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 =
|
var expected2 =
|
||||||
"Bonjour!" + Environment.NewLine +
|
"Bonjour!" + Environment.NewLine +
|
||||||
|
|
@ -118,10 +118,10 @@ mypartial
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expected =
|
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>
|
<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-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-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>";
|
<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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.AspNet.Mvc.DataAnnotations.Internal;
|
using Microsoft.AspNet.Mvc.DataAnnotations.Internal;
|
||||||
using Microsoft.AspNet.Mvc.Formatters.Json.Internal;
|
using Microsoft.AspNet.Mvc.Formatters.Json.Internal;
|
||||||
using Microsoft.AspNet.Mvc.Internal;
|
using Microsoft.AspNet.Mvc.Internal;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.OptionsModel;
|
using Microsoft.Extensions.OptionsModel;
|
||||||
|
|
@ -17,6 +20,9 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
Value = new MvcOptions();
|
Value = new MvcOptions();
|
||||||
MvcCoreMvcOptionsSetup.ConfigureMvc(Value, new TestHttpRequestStreamReaderFactory());
|
MvcCoreMvcOptionsSetup.ConfigureMvc(Value, new TestHttpRequestStreamReaderFactory());
|
||||||
var collection = new ServiceCollection().AddOptions();
|
var collection = new ServiceCollection().AddOptions();
|
||||||
|
collection.AddSingleton<ICompositeMetadataDetailsProvider, DefaultCompositeMetadataDetailsProvider>();
|
||||||
|
collection.AddSingleton<IModelMetadataProvider, DefaultModelMetadataProvider>();
|
||||||
|
collection.AddSingleton<IValidationAttributeAdapterProvider, ValidationAttributeAdapterProvider>();
|
||||||
MvcDataAnnotationsMvcOptionsSetup.ConfigureMvc(
|
MvcDataAnnotationsMvcOptionsSetup.ConfigureMvc(
|
||||||
Value,
|
Value,
|
||||||
collection.BuildServiceProvider());
|
collection.BuildServiceProvider());
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
|
|
@ -14,6 +15,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
new DefaultClientModelValidatorProvider(),
|
new DefaultClientModelValidatorProvider(),
|
||||||
new DataAnnotationsClientModelValidatorProvider(
|
new DataAnnotationsClientModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null),
|
stringLocalizerFactory: null),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
|
|
@ -14,8 +15,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
new DefaultModelValidatorProvider(),
|
new DefaultModelValidatorProvider(),
|
||||||
new DataAnnotationsModelValidatorProvider(
|
new DataAnnotationsModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
|
||||||
stringLocalizerFactory: null),
|
stringLocalizerFactory: null)
|
||||||
};
|
};
|
||||||
|
|
||||||
return new TestModelValidatorProvider(providers);
|
return new TestModelValidatorProvider(providers);
|
||||||
|
|
|
||||||
|
|
@ -1863,9 +1863,12 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
HttpContext = httpContext,
|
HttpContext = httpContext,
|
||||||
ModelBinders = new[] { binder, },
|
ModelBinders = new[] { binder, },
|
||||||
ValueProviders = new[] { valueProvider, },
|
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.Antiforgery;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
using Microsoft.AspNet.Mvc.Abstractions;
|
using Microsoft.AspNet.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNet.Mvc.DataAnnotations;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.AspNet.Mvc.Routing;
|
using Microsoft.AspNet.Mvc.Routing;
|
||||||
|
|
@ -226,6 +227,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
localizationOptionsAccesor.SetupGet(o => o.Value).Returns(new MvcDataAnnotationsLocalizationOptions());
|
localizationOptionsAccesor.SetupGet(o => o.Value).Returns(new MvcDataAnnotationsLocalizationOptions());
|
||||||
|
|
||||||
options.ClientModelValidatorProviders.Add(new DataAnnotationsClientModelValidatorProvider(
|
options.ClientModelValidatorProviders.Add(new DataAnnotationsClientModelValidatorProvider(
|
||||||
|
new ValidationAttributeAdapterProvider(),
|
||||||
localizationOptionsAccesor.Object,
|
localizationOptionsAccesor.Object,
|
||||||
stringLocalizerFactory: null));
|
stringLocalizerFactory: null));
|
||||||
var optionsAccessor = new Mock<IOptions<MvcViewOptions>>();
|
var optionsAccessor = new Mock<IOptions<MvcViewOptions>>();
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ namespace LocalizationWebSite.Models
|
||||||
{
|
{
|
||||||
public class User
|
public class User
|
||||||
{
|
{
|
||||||
[MinLength(4, ErrorMessage = "NameError")]
|
[MinLength(6, ErrorMessage = "NameError")]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
public Product Product { 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>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="NameError" xml:space="preserve">
|
<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>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
Loading…
Reference in New Issue