diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs index 0ba54c5448..5b253b53fe 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs @@ -33,6 +33,11 @@ public class AbpValidationAttributeAdapterProvider : IValidationAttributeAdapter return new DynamicRangeAttributeAdapter((DynamicRangeAttribute)attribute, stringLocalizer); } + if (type == typeof(EnumDataTypeAttribute)) + { + return new EnumDataTypeAttributeAdapter((EnumDataTypeAttribute)attribute, stringLocalizer); + } + return _defaultAdapter.GetAttributeAdapter(attribute, stringLocalizer); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/EnumDataTypeAttributeAdapter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/EnumDataTypeAttributeAdapter.cs new file mode 100644 index 0000000000..29ad39097d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/EnumDataTypeAttributeAdapter.cs @@ -0,0 +1,34 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.Extensions.Localization; + +namespace Volo.Abp.AspNetCore.Mvc.DataAnnotations; + +public class EnumDataTypeAttributeAdapter : AttributeAdapterBase +{ + public EnumDataTypeAttributeAdapter( + EnumDataTypeAttribute attribute, + IStringLocalizer? stringLocalizer) + : base(attribute, stringLocalizer) + { + } + + public override void AddValidation(ClientModelValidationContext context) + { + Check.NotNull(context, nameof(context)); + + //There is no built-in client side validation rule for enum values. + //This adapter is used to localize the error message on the server side. + } + + public override string GetErrorMessage(ModelValidationContextBase validationContext) + { + Check.NotNull(validationContext, nameof(validationContext)); + + return GetErrorMessage( + validationContext.ModelMetadata, + validationContext.ModelMetadata.GetDisplayName() + ); + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider_Tests.cs new file mode 100644 index 0000000000..d5bb122939 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider_Tests.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.Extensions.Localization; +using Shouldly; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.DataAnnotations; + +public class AbpValidationAttributeAdapterProvider_Tests +{ + private readonly AbpValidationAttributeAdapterProvider _provider = new(new ValidationAttributeAdapterProvider()); + + [Fact] + public void Should_Return_An_Adapter_For_The_EnumDataTypeAttribute() + { + //ASP.NET Core does not provide an adapter for the EnumDataTypeAttribute. + new ValidationAttributeAdapterProvider() + .GetAttributeAdapter(new EnumDataTypeAttribute(typeof(MyEnum)), null) + .ShouldBeNull(); + + _provider.GetAttributeAdapter(new EnumDataTypeAttribute(typeof(MyEnum)), null) + .ShouldBeOfType(); + } + + [Fact] + public void Should_Localize_The_Error_Message_Of_The_EnumDataTypeAttribute() + { + var attribute = new EnumDataTypeAttribute(typeof(MyEnum)) { ErrorMessage = "MyEnumIsInvalid" }; + + var adapter = _provider.GetAttributeAdapter(attribute, new TestStringLocalizer())!; + + adapter.GetErrorMessage(CreateValidationContext()).ShouldBe("Localized:MyEnumIsInvalid"); + } + + private static ModelValidationContextBase CreateValidationContext() + { + var metadataProvider = new EmptyModelMetadataProvider(); + + return new ClientModelValidationContext( + new ActionContext(), + metadataProvider.GetMetadataForProperty(typeof(MyModel), nameof(MyModel.Value)), + metadataProvider, + new Dictionary() + ); + } + + public enum MyEnum + { + Value1 = 1 + } + + public class MyModel + { + public MyEnum Value { get; set; } + } + + private class TestStringLocalizer : IStringLocalizer + { + public LocalizedString this[string name] => new(name, "Localized:" + name); + + public LocalizedString this[string name, params object[] arguments] => new(name, "Localized:" + name); + + public IEnumerable GetAllStrings(bool includeParentCultures) + { + return new List(); + } + } +}