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 eaceddba70..c9b7cb21b3 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 @@ -22,6 +22,11 @@ namespace Volo.Abp.AspNetCore.Mvc.DataAnnotations { return new DynamicStringLengthAttributeAdapter((DynamicStringLengthAttribute) attribute, stringLocalizer); } + + if (type == typeof(DynamicMaxLengthAttribute)) + { + return new DynamicMaxLengthAttributeAdapter((DynamicMaxLengthAttribute) attribute, stringLocalizer); + } return _defaultAdapter.GetAttributeAdapter(attribute, stringLocalizer); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicMaxLengthAttributeAdapter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicMaxLengthAttributeAdapter.cs new file mode 100644 index 0000000000..ca3b3ce7ab --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicMaxLengthAttributeAdapter.cs @@ -0,0 +1,45 @@ +using System.Globalization; +using Microsoft.AspNetCore.Mvc.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.Extensions.Localization; +using Volo.Abp.Validation; + +namespace Volo.Abp.AspNetCore.Mvc.DataAnnotations +{ + public class DynamicMaxLengthAttributeAdapter : AttributeAdapterBase + { + private readonly string _max; + + public DynamicMaxLengthAttributeAdapter( + DynamicMaxLengthAttribute attribute, + IStringLocalizer stringLocalizer) + : base(attribute, stringLocalizer) + { + _max = Attribute.Length.ToString(CultureInfo.InvariantCulture); + } + + public override string GetErrorMessage(ModelValidationContextBase validationContext) + { + Check.NotNull(validationContext, nameof(validationContext)); + + return GetErrorMessage( + validationContext.ModelMetadata, + validationContext.ModelMetadata.GetDisplayName(), + Attribute.Length + ); + } + + public override void AddValidation(ClientModelValidationContext context) + { + Check.NotNull(context, nameof(context)); + + MergeAttribute(context.Attributes, "data-val", "true"); + MergeAttribute(context.Attributes, "data-val-length", GetErrorMessage(context)); + + if (Attribute.Length != int.MaxValue) + { + MergeAttribute(context.Attributes, "data-val-length-max", _max); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicMaxLengthAttribute.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicMaxLengthAttribute.cs new file mode 100644 index 0000000000..c76a54bdc2 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicMaxLengthAttribute.cs @@ -0,0 +1,39 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Reflection; +using JetBrains.Annotations; + +namespace Volo.Abp.Validation +{ + public class DynamicMaxLengthAttribute : MaxLengthAttribute + { + private static readonly FieldInfo MaximumLengthField; + + static DynamicMaxLengthAttribute() + { + MaximumLengthField = typeof(MaxLengthAttribute).GetField( + "k__BackingField", + BindingFlags.Instance | BindingFlags.NonPublic + ); + Debug.Assert(MaximumLengthField != null, nameof(MaximumLengthField) + " != null"); + } + + public DynamicMaxLengthAttribute( + [NotNull] Type sourceType, + [CanBeNull] string maximumLengthPropertyName) + { + Check.NotNull(sourceType, nameof(sourceType)); + + if (maximumLengthPropertyName != null) + { + var maximumLengthProperty = sourceType.GetProperty( + maximumLengthPropertyName, + BindingFlags.Static | BindingFlags.Public + ); + Debug.Assert(maximumLengthProperty != null, nameof(maximumLengthProperty) + " != null"); + MaximumLengthField.SetValue(this, (int) maximumLengthProperty.GetValue(null)); + } + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs index ae622acee2..df033c99b5 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs @@ -50,13 +50,18 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation public class ValidationDynamicTestModel { - [DynamicStringLength(typeof(Consts), nameof(Consts.MaxValue2Length), nameof(Consts.MinValue2Length))] + [DynamicStringLength(typeof(Consts), nameof(Consts.MaxValue1Length), nameof(Consts.MinValue1Length))] public string Value1 { get; set; } + [DynamicMaxLength(typeof(Consts), nameof(Consts.MaxValue2Length))] + public string Value2 { get; set; } + public static class Consts { - public static int MinValue2Length { get; set; } = 2; - public static int MaxValue2Length { get; set; } = 7; + public static int MinValue1Length { get; set; } = 2; + public static int MaxValue1Length { get; set; } = 7; + + public static int MaxValue2Length { get; set; } = 4; } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs index 767ff7aa3e..6a16e8e899 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs @@ -63,15 +63,20 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation { var result = await GetResponseAsStringAsync("/api/validation-test/object-result-action-dynamic-length?value1=hello"); result.ShouldBe("hello"); + } [Fact] public async Task Should_Validate_Dynamic_Length_Object_Result_Failing() { - var result = await GetResponseAsObjectAsync("/api/validation-test/object-result-action-dynamic-length?value1=a", HttpStatusCode.BadRequest); //value1 has min length of 2 chars. + var result = await GetResponseAsObjectAsync("/api/validation-test/object-result-action-dynamic-length?value1=a", HttpStatusCode.BadRequest); //value1 has min string length of 2 chars. + result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0); + + result = await GetResponseAsObjectAsync("/api/validation-test/object-result-action-dynamic-length?value1=12345678", HttpStatusCode.BadRequest); //value1 has max string length of 7 chars. result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0); - result = await GetResponseAsObjectAsync("/api/validation-test/object-result-action-dynamic-length?value1=12345678", HttpStatusCode.BadRequest); //value1 has max length of 7 chars. + + result = await GetResponseAsObjectAsync("/api/validation-test/object-result-action-dynamic-length?value1=123458&value2=12345", HttpStatusCode.BadRequest); //value2 has max length of 5 chars. result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0); } }