Browse Source

Introduce DynamicRangeAttribute

pull/5261/head
liangshiwei 6 years ago
parent
commit
453771ba31
  1. 9
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs
  2. 46
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicRangeAttributeAdapter.cs
  3. 64
      framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicRangeAttribute.cs
  4. 36
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs
  5. 19
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs

9
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/AbpValidationAttributeAdapterProvider.cs

@ -27,8 +27,13 @@ namespace Volo.Abp.AspNetCore.Mvc.DataAnnotations
{
return new DynamicMaxLengthAttributeAdapter((DynamicMaxLengthAttribute) attribute, stringLocalizer);
}
if (type == typeof(DynamicRangeAttribute))
{
return new DynamicRangeAttributeAdapter((DynamicRangeAttribute) attribute, stringLocalizer);
}
return _defaultAdapter.GetAttributeAdapter(attribute, stringLocalizer);
}
}
}
}

46
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DataAnnotations/DynamicRangeAttributeAdapter.cs

@ -0,0 +1,46 @@
using System;
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 DynamicRangeAttributeAdapter : AttributeAdapterBase<DynamicRangeAttribute>
{
private readonly string _max;
private readonly string _min;
public DynamicRangeAttributeAdapter(
DynamicRangeAttribute attribute,
IStringLocalizer stringLocalizer)
: base(attribute, stringLocalizer)
{
_min = Attribute.Minimum.ToString()?.ToString(CultureInfo.InvariantCulture);
_max = Attribute.Maximum.ToString()?.ToString(CultureInfo.InvariantCulture);
}
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));
MergeAttribute(context.Attributes, "data-val-length-min", _min);
MergeAttribute(context.Attributes, "data-val-length-max", _max);
}
public override string GetErrorMessage(ModelValidationContextBase validationContext)
{
Check.NotNull(validationContext, nameof(validationContext));
return GetErrorMessage(
validationContext.ModelMetadata,
validationContext.ModelMetadata.GetDisplayName(),
Attribute.Minimum,
Attribute.Maximum
);
}
}
}

64
framework/src/Volo.Abp.Core/Volo/Abp/Validation/DynamicRangeAttribute.cs

@ -0,0 +1,64 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Reflection;
using JetBrains.Annotations;
namespace Volo.Abp.Validation
{
public class DynamicRangeAttribute : RangeAttribute
{
private static readonly FieldInfo MaximumField;
private static readonly FieldInfo MinimumField;
static DynamicRangeAttribute()
{
MaximumField = typeof(RangeAttribute).GetField(
"<Maximum>k__BackingField",
BindingFlags.Instance | BindingFlags.NonPublic
);
Debug.Assert(MaximumField != null, nameof(MaximumField) + " != null");
MinimumField = typeof(RangeAttribute).GetField(
"<Minimum>k__BackingField",
BindingFlags.Instance | BindingFlags.NonPublic
);
Debug.Assert(MinimumField != null, nameof(MinimumField) + " != null");
}
/// <param name="sourceType">A type to get the values of the properties</param>
/// <param name="minimumPropertyName">The name of the public static property for the <see cref="RangeAttribute.Minimum"/></param>
/// <param name="maximumPropertyName">The name of the public static property for the <see cref="RangeAttribute.Maximum"/></param>
/// <param name="operandType">The type of the range parameters. Must implement IComparable. <see cref="RangeAttribute.OperandType"/></param>
public DynamicRangeAttribute(
[NotNull] Type sourceType,
[NotNull] Type operandType,
[CanBeNull] string minimumPropertyName,
[CanBeNull] string maximumPropertyName
)
: base(operandType, string.Empty, string.Empty)
{
Check.NotNull(sourceType, nameof(sourceType));
if (minimumPropertyName != null)
{
var minimumProperty = sourceType.GetProperty(
minimumPropertyName,
BindingFlags.Static | BindingFlags.Public
);
Debug.Assert(minimumProperty != null, nameof(minimumProperty) + " != null");
MinimumField.SetValue(this, minimumProperty.GetValue(null));
}
if (maximumPropertyName != null)
{
var maximumProperty = sourceType.GetProperty(
maximumPropertyName,
BindingFlags.Static | BindingFlags.Public
);
Debug.Assert(maximumProperty != null, nameof(maximumProperty) + " != null");
MaximumField.SetValue(this, maximumProperty.GetValue(null));
}
}
}
}

36
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
@ -32,7 +33,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation
{
return Content("ModelState.IsValid: " + ModelState.IsValid.ToString().ToLowerInvariant());
}
[HttpGet]
[Route("object-result-action-dynamic-length")]
public Task<string> ObjectResultActionDynamicLength(ValidationDynamicTestModel model)
@ -47,7 +48,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation
[StringLength(5, MinimumLength = 2)]
public string Value1 { get; set; }
}
public class ValidationDynamicTestModel
{
[DynamicStringLength(typeof(Consts), nameof(Consts.MaxValue1Length), nameof(Consts.MinValue1Length))]
@ -55,18 +56,41 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation
[DynamicMaxLength(typeof(Consts), nameof(Consts.MaxValue2Length))]
public string Value2 { get; set; }
[DynamicMaxLength(typeof(Consts), nameof(Consts.MaxValue3Length))]
public int[] Value3 { get; set; }
[DynamicRange(typeof(Consts), typeof(int), nameof(Consts.MinValue1), nameof(Consts.MaxValue1))]
public int Value4 { get; set; }
[DynamicRange(typeof(Consts), typeof(double), nameof(Consts.MinValue2), nameof(Consts.MaxValue2))]
public double Value5 { get; set; }
[DynamicRange(typeof(Consts), typeof(DateTime), nameof(Consts.MinValue3), nameof(Consts.MaxValue3))]
public DateTime Value6 { get; set; }
public static class Consts
{
public static int MinValue1Length { get; set; } = 2;
public static int MaxValue1Length { get; set; } = 7;
public static int MaxValue2Length { get; set; } = 4;
public static int MaxValue3Length { get; set; } = 2;
public static int MinValue1 { get; set; } = 1;
public static int MaxValue1 { get; set; } = 5;
public static double MinValue2 { get; set; } = 1.2;
public static double MaxValue2 { get; set; } = 7.2;
public static string MinValue3 { get; set; } = "1/2/2004";
public static string MaxValue3 { get; set; } = "3/4/2004";
}
}
@ -83,4 +107,4 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation
}
}
}
}
}

19
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs

@ -61,9 +61,9 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation
[Fact]
public async Task Should_Validate_Dynamic_Length_Object_Result_Success()
{
var result = await GetResponseAsStringAsync("/api/validation-test/object-result-action-dynamic-length?value1=hello&value3[0]=53&value3[1]=54");
var result = await GetResponseAsStringAsync("/api/validation-test/object-result-action-dynamic-length?value1=hello&value3[0]=53&value3[1]=54&value4=4&value5=1.5&value6=2004-02-04");
result.ShouldBe("hello");
}
[Fact]
@ -71,15 +71,24 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation
{
var result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/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<RemoteServiceErrorResponse>("/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<RemoteServiceErrorResponse>("/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);
result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/api/validation-test/object-result-action-dynamic-length?value1=123458&value3[0]=53&value3[1]=54&value3[2]=55&value3[3]=56", HttpStatusCode.BadRequest); //value3 has max length of 2.
result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0);
result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/api/validation-test/object-result-action-dynamic-length?value1=123458&value3[0]=53&value3[1]=54&value[4]=10", HttpStatusCode.BadRequest); //value4 has max num of 5.
result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0);
result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/api/validation-test/object-result-action-dynamic-length?value1=123458&value3[0]=53&value3[1]=54&value4=2&value5=1.1", HttpStatusCode.BadRequest); //value4 has min num of 1.2.
result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0);
result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/api/validation-test/object-result-action-dynamic-length?value1=123458&value3[0]=53&value3[1]=54&value4=2&value5=1.2&value6=2004-05-04", HttpStatusCode.BadRequest); //value4 has max date of 3/4/2004.
result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0);
}
}
}

Loading…
Cancel
Save