diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs index 55998a512f..4aa183a43d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs @@ -57,7 +57,6 @@ namespace Volo.Abp.AspNetCore.Mvc options.ModelMetadataDetailsProviders.Add( new AbpDataAnnotationAutoLocalizationMetadataDetailsProvider(services) ); - options.ModelMetadataDetailsProviders.Add(new AbpExtraPropertyValidationMetadataProvider()); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpModelExpressionProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpModelExpressionProvider.cs deleted file mode 100644 index a89127c158..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpModelExpressionProvider.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Volo.Abp.AspNetCore.Mvc.ModelBinding.Metadata; -using Volo.Abp.Data; -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.AspNetCore.Mvc.ModelBinding -{ - [Dependency(ReplaceServices = true)] - public class AbpModelExpressionProvider : IModelExpressionProvider, ITransientDependency - { - private readonly ModelExpressionProvider _modelExpressionProvider; - - public AbpModelExpressionProvider(ModelExpressionProvider modelExpressionProvider) - { - _modelExpressionProvider = modelExpressionProvider; - } - - public ModelExpression CreateModelExpression( - ViewDataDictionary viewData, - Expression> expression) - { - var result = _modelExpressionProvider.CreateModelExpression( - viewData, - expression - ); - - AbpExtraPropertyValidationMetadataProvider.CurrentModeInfo.Value = null; - - if (result.ModelExplorer.Container == null) - { - return result; - } - - var extraPropertyName = ExtraPropertyBindingHelper.ExtractExtraPropertyName(result.Name); - if (extraPropertyName == null) - { - return result; - } - - var containerName = ExtraPropertyBindingHelper.ExtractContainerName(result.Name); - if (containerName == null) - { - return result; - } - - var containerModelExpolorer = result - .ModelExplorer - .Container - .Properties - .FirstOrDefault(p => p.Metadata.Name == containerName); - - if (containerModelExpolorer == null) - { - return result; - } - - if (!containerModelExpolorer.ModelType.IsAssignableTo()) - { - return result; - } - - AbpExtraPropertyValidationMetadataProvider.CurrentModeInfo.Value - = new AbpExtraPropertyValidationMetadataProvider.ExtraPropertyModelInfo( - containerModelExpolorer.ModelType, - extraPropertyName - ); - - return result; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/Metadata/AbpExtraPropertyValidationMetadataProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/Metadata/AbpExtraPropertyValidationMetadataProvider.cs deleted file mode 100644 index 2d47882d13..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/Metadata/AbpExtraPropertyValidationMetadataProvider.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Threading; -using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; -using Volo.Abp.ObjectExtending; - -namespace Volo.Abp.AspNetCore.Mvc.ModelBinding.Metadata -{ - public class AbpExtraPropertyValidationMetadataProvider : IValidationMetadataProvider - { - public static AsyncLocal CurrentModeInfo { get; } = new AsyncLocal(); - - public void CreateValidationMetadata(ValidationMetadataProviderContext context) - { - if (CurrentModeInfo.Value == null) - { - return; - } - - var currentModel = CurrentModeInfo.Value; - - CurrentModeInfo.Value = null; - - var propertyInfo = ObjectExtensionManager.Instance.GetPropertyOrNull( - currentModel.ExtensibleObjectType, - currentModel.PropertyName - ); - - if (propertyInfo == null) - { - return; - } - - foreach (var validationAttribute in propertyInfo.ValidationAttributes) - { - context.ValidationMetadata.ValidatorMetadata.Add(validationAttribute); - } - } - - public class ExtraPropertyModelInfo - { - public Type ExtensibleObjectType { get; } - public string PropertyName { get; } - - public ExtraPropertyModelInfo( - Type extensibleObjectType, - string propertyName) - { - ExtensibleObjectType = extensibleObjectType; - PropertyName = propertyName; - } - } - } -} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ViewFeatures/AbpValidationHtmlAttributeProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ViewFeatures/AbpValidationHtmlAttributeProvider.cs new file mode 100644 index 0000000000..50ac9ddf02 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ViewFeatures/AbpValidationHtmlAttributeProvider.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Reflection; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Mvc.ModelBinding; +using Volo.Abp.DependencyInjection; +using Volo.Abp.ObjectExtending; +using Volo.Abp.Validation.Localization; + +namespace Volo.Abp.AspNetCore.Mvc.ViewFeatures +{ + [Dependency(ReplaceServices = true)] + [ExposeServices(typeof(ValidationHtmlAttributeProvider))] + public class AbpValidationHtmlAttributeProvider + : DefaultValidationHtmlAttributeProvider, ISingletonDependency + { + private readonly IModelMetadataProvider _metadataProvider; + private readonly IStringLocalizerFactory _stringLocalizerFactory; + private readonly IStringLocalizer _validationStringLocalizer; + private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider; + + public AbpValidationHtmlAttributeProvider( + IOptions optionsAccessor, + IModelMetadataProvider metadataProvider, + ClientValidatorCache clientValidatorCache, + IValidationAttributeAdapterProvider validationAttributeAdapterProvider, + IStringLocalizerFactory stringLocalizerFactory, + IStringLocalizer validationStringLocalizer) + : base( + optionsAccessor, + metadataProvider, + clientValidatorCache) + { + _metadataProvider = metadataProvider; + _validationAttributeAdapterProvider = validationAttributeAdapterProvider; + _stringLocalizerFactory = stringLocalizerFactory; + _validationStringLocalizer = validationStringLocalizer; + } + + public override void AddValidationAttributes( + ViewContext viewContext, + ModelExplorer modelExplorer, + IDictionary attributes) + { + base.AddValidationAttributes(viewContext, modelExplorer, attributes); + + var nameAttribute = attributes.GetOrDefault("name"); + var propertyName = ExtraPropertyBindingHelper.ExtractExtraPropertyName(nameAttribute); + if (propertyName == null) + { + return; + } + + //NOTE: containerName can be null on controller actions..? + + var containerName = ExtraPropertyBindingHelper.ExtractContainerName(nameAttribute); + if (containerName == null) + { + return; + } + + var extensibleObjectType = modelExplorer.Container.ModelType + .GetProperty(containerName, BindingFlags.Instance | BindingFlags.Public) + .PropertyType; + + var propertyInfo = ObjectExtensionManager.Instance.GetPropertyOrNull(extensibleObjectType, propertyName); + + modelExplorer.Metadata.As().DisplayMetadata.DisplayName = () => propertyInfo.DisplayName.Localize(_stringLocalizerFactory); + + foreach (var validationAttribute in propertyInfo.ValidationAttributes) + { + var validationContext = new ClientModelValidationContext( + viewContext, + modelExplorer.Metadata, + _metadataProvider, + attributes); + + validationAttribute.ErrorMessage = ValidationAttributeErrorMessageStringProperty.GetValue(validationAttribute) as string; + + _validationAttributeAdapterProvider.GetAttributeAdapter(validationAttribute, _validationStringLocalizer) + .AddValidation(validationContext); + } + } + + private static readonly PropertyInfo ValidationAttributeErrorMessageStringProperty = typeof(ValidationAttribute) + .GetProperty("ErrorMessageString", BindingFlags.Instance | BindingFlags.NonPublic); + + } +}