mirror of https://github.com/abpframework/abp.git
13 changed files with 808 additions and 1 deletions
@ -0,0 +1,78 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.Aspects |
||||
|
{ |
||||
|
internal static class AbpCrossCuttingConcerns |
||||
|
{ |
||||
|
public const string Auditing = "AbpAuditing"; |
||||
|
public const string Validation = "AbpValidation"; |
||||
|
public const string UnitOfWork = "AbpUnitOfWork"; |
||||
|
public const string Authorization = "AbpAuthorization"; |
||||
|
|
||||
|
public static void AddApplied(object obj, params string[] concerns) |
||||
|
{ |
||||
|
if (concerns.IsNullOrEmpty()) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(concerns), $"{nameof(concerns)} should be provided!"); |
||||
|
} |
||||
|
|
||||
|
(obj as IAvoidDuplicateCrossCuttingConcerns)?.AppliedCrossCuttingConcerns.AddRange(concerns); |
||||
|
} |
||||
|
|
||||
|
public static void RemoveApplied(object obj, params string[] concerns) |
||||
|
{ |
||||
|
if (concerns.IsNullOrEmpty()) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(concerns), $"{nameof(concerns)} should be provided!"); |
||||
|
} |
||||
|
|
||||
|
var crossCuttingEnabledObj = obj as IAvoidDuplicateCrossCuttingConcerns; |
||||
|
if (crossCuttingEnabledObj == null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
foreach (var concern in concerns) |
||||
|
{ |
||||
|
crossCuttingEnabledObj.AppliedCrossCuttingConcerns.RemoveAll(c => c == concern); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static bool IsApplied([NotNull] object obj, [NotNull] string concern) |
||||
|
{ |
||||
|
if (obj == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(obj)); |
||||
|
} |
||||
|
|
||||
|
if (concern == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(concern)); |
||||
|
} |
||||
|
|
||||
|
return (obj as IAvoidDuplicateCrossCuttingConcerns)?.AppliedCrossCuttingConcerns.Contains(concern) ?? false; |
||||
|
} |
||||
|
|
||||
|
public static IDisposable Applying(object obj, params string[] concerns) |
||||
|
{ |
||||
|
AddApplied(obj, concerns); |
||||
|
return new DisposeAction(() => |
||||
|
{ |
||||
|
RemoveApplied(obj, concerns); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public static string[] GetApplieds(object obj) |
||||
|
{ |
||||
|
var crossCuttingEnabledObj = obj as IAvoidDuplicateCrossCuttingConcerns; |
||||
|
if (crossCuttingEnabledObj == null) |
||||
|
{ |
||||
|
return new string[0]; |
||||
|
} |
||||
|
|
||||
|
return crossCuttingEnabledObj.AppliedCrossCuttingConcerns.ToArray(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.Aspects |
||||
|
{ |
||||
|
public interface IAvoidDuplicateCrossCuttingConcerns |
||||
|
{ |
||||
|
List<string> AppliedCrossCuttingConcerns { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Can be added to a method to disable auto validation.
|
||||
|
/// </summary>
|
||||
|
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property)] |
||||
|
public class DisableValidationAttribute : Attribute |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Can be added to a method to enable auto validation if validation is disabled for it's class.
|
||||
|
/// </summary>
|
||||
|
[AttributeUsage(AttributeTargets.Method)] |
||||
|
public class EnableValidationAttribute : Attribute |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This interface is used to normalize inputs before method execution.
|
||||
|
/// </summary>
|
||||
|
public interface IShouldNormalize |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This method is called lastly before method execution (after validation if exists).
|
||||
|
/// </summary>
|
||||
|
void Normalize(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
public class AbpValidationOptions |
||||
|
{ |
||||
|
public List<Type> IgnoredTypes { get; } |
||||
|
|
||||
|
public AbpValidationOptions() |
||||
|
{ |
||||
|
IgnoredTypes = new List<Type>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,250 @@ |
|||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Reflection; |
||||
|
|
||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This class is used to validate a method call (invocation) for method arguments.
|
||||
|
/// </summary>
|
||||
|
public class MethodInvocationValidator : ITransientDependency |
||||
|
{ |
||||
|
private const int MaxRecursiveParameterValidationDepth = 8; |
||||
|
|
||||
|
protected MethodInfo Method { get; private set; } |
||||
|
protected object[] ParameterValues { get; private set; } |
||||
|
protected ParameterInfo[] Parameters { get; private set; } |
||||
|
protected List<ValidationResult> ValidationErrors { get; } |
||||
|
protected List<IShouldNormalize> ObjectsToBeNormalized { get; } |
||||
|
|
||||
|
private readonly AbpValidationOptions _options; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new <see cref="MethodInvocationValidator"/> instance.
|
||||
|
/// </summary>
|
||||
|
public MethodInvocationValidator(IOptions<AbpValidationOptions> options) |
||||
|
{ |
||||
|
_options = options.Value; |
||||
|
|
||||
|
ValidationErrors = new List<ValidationResult>(); |
||||
|
ObjectsToBeNormalized = new List<IShouldNormalize>(); |
||||
|
} |
||||
|
|
||||
|
/// <param name="method">Method to be validated</param>
|
||||
|
/// <param name="parameterValues">List of arguments those are used to call the <paramref name="method"/>.</param>
|
||||
|
public virtual void Initialize(MethodInfo method, object[] parameterValues) |
||||
|
{ |
||||
|
Check.NotNull(method, nameof(method)); |
||||
|
Check.NotNull(parameterValues, nameof(parameterValues)); |
||||
|
|
||||
|
Method = method; |
||||
|
ParameterValues = parameterValues; |
||||
|
Parameters = method.GetParameters(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Validates the method invocation.
|
||||
|
/// </summary>
|
||||
|
public void Validate() |
||||
|
{ |
||||
|
CheckInitialized(); |
||||
|
|
||||
|
if (Parameters.IsNullOrEmpty()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (!Method.IsPublic) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (IsValidationDisabled()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (Parameters.Length != ParameterValues.Length) |
||||
|
{ |
||||
|
throw new Exception("Method parameter count does not match with argument count!"); |
||||
|
} |
||||
|
|
||||
|
if (ValidationErrors.Any() && HasSingleNullArgument()) |
||||
|
{ |
||||
|
ThrowValidationError(); |
||||
|
} |
||||
|
|
||||
|
for (var i = 0; i < Parameters.Length; i++) |
||||
|
{ |
||||
|
ValidateMethodParameter(Parameters[i], ParameterValues[i]); |
||||
|
} |
||||
|
|
||||
|
if (ValidationErrors.Any()) |
||||
|
{ |
||||
|
ThrowValidationError(); |
||||
|
} |
||||
|
|
||||
|
foreach (var objectToBeNormalized in ObjectsToBeNormalized) |
||||
|
{ |
||||
|
objectToBeNormalized.Normalize(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual void CheckInitialized() |
||||
|
{ |
||||
|
if (Method == null) |
||||
|
{ |
||||
|
throw new AbpException("This object has not been initialized. Call Initialize method first."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual bool IsValidationDisabled() |
||||
|
{ |
||||
|
if (Method.IsDefined(typeof(EnableValidationAttribute), true)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableValidationAttribute>(Method) != null; |
||||
|
} |
||||
|
|
||||
|
protected virtual bool HasSingleNullArgument() |
||||
|
{ |
||||
|
return Parameters.Length == 1 && ParameterValues[0] == null; |
||||
|
} |
||||
|
|
||||
|
protected virtual void ThrowValidationError() |
||||
|
{ |
||||
|
throw new AbpValidationException( |
||||
|
"Method arguments are not valid! See ValidationErrors for details.", |
||||
|
ValidationErrors |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Validates given parameter for given value.
|
||||
|
/// </summary>
|
||||
|
/// <param name="parameterInfo">Parameter of the method to validate</param>
|
||||
|
/// <param name="parameterValue">Value to validate</param>
|
||||
|
protected virtual void ValidateMethodParameter(ParameterInfo parameterInfo, object parameterValue) |
||||
|
{ |
||||
|
if (parameterValue == null) |
||||
|
{ |
||||
|
if (!parameterInfo.IsOptional && |
||||
|
!parameterInfo.IsOut && |
||||
|
!TypeHelper.IsPrimitiveExtendedIncludingNullable(parameterInfo.ParameterType, includeEnums: true)) |
||||
|
{ |
||||
|
ValidationErrors.Add(new ValidationResult(parameterInfo.Name + " is null!", new[] { parameterInfo.Name })); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ValidateObjectRecursively(parameterValue, 1); |
||||
|
} |
||||
|
|
||||
|
protected virtual void ValidateObjectRecursively(object validatingObject, int currentDepth) |
||||
|
{ |
||||
|
if (currentDepth > MaxRecursiveParameterValidationDepth) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (validatingObject == null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
SetDataAnnotationAttributeErrors(validatingObject); |
||||
|
|
||||
|
//Validate items of enumerable
|
||||
|
if (validatingObject is IEnumerable && !(validatingObject is IQueryable)) |
||||
|
{ |
||||
|
foreach (var item in (validatingObject as IEnumerable)) |
||||
|
{ |
||||
|
ValidateObjectRecursively(item, currentDepth + 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//Add list to be normalized later
|
||||
|
if (validatingObject is IShouldNormalize) |
||||
|
{ |
||||
|
ObjectsToBeNormalized.Add(validatingObject as IShouldNormalize); |
||||
|
} |
||||
|
|
||||
|
//Do not recursively validate for enumerable objects
|
||||
|
if (validatingObject is IEnumerable) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var validatingObjectType = validatingObject.GetType(); |
||||
|
|
||||
|
//Do not recursively validate for primitive objects
|
||||
|
if (TypeHelper.IsPrimitiveExtendedIncludingNullable(validatingObjectType)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (_options.IgnoredTypes.Any(t => t.IsInstanceOfType(validatingObject))) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var properties = TypeDescriptor.GetProperties(validatingObject).Cast<PropertyDescriptor>(); |
||||
|
foreach (var property in properties) |
||||
|
{ |
||||
|
if (property.Attributes.OfType<DisableValidationAttribute>().Any()) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
ValidateObjectRecursively(property.GetValue(validatingObject), currentDepth + 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Checks all properties for DataAnnotations attributes.
|
||||
|
/// </summary>
|
||||
|
protected virtual void SetDataAnnotationAttributeErrors(object validatingObject) |
||||
|
{ |
||||
|
var properties = TypeDescriptor.GetProperties(validatingObject).Cast<PropertyDescriptor>(); |
||||
|
foreach (var property in properties) |
||||
|
{ |
||||
|
var validationAttributes = property.Attributes.OfType<ValidationAttribute>().ToArray(); |
||||
|
if (validationAttributes.IsNullOrEmpty()) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
var validationContext = new ValidationContext(validatingObject) |
||||
|
{ |
||||
|
DisplayName = property.DisplayName, |
||||
|
MemberName = property.Name |
||||
|
}; |
||||
|
|
||||
|
foreach (var attribute in validationAttributes) |
||||
|
{ |
||||
|
var result = attribute.GetValidationResult(property.GetValue(validatingObject), validationContext); |
||||
|
if (result != null) |
||||
|
{ |
||||
|
ValidationErrors.Add(result); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (validatingObject is IValidatableObject) |
||||
|
{ |
||||
|
var results = (validatingObject as IValidatableObject).Validate(new ValidationContext(validatingObject)); |
||||
|
ValidationErrors.AddRange(results); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
using System; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.Aspects; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.DynamicProxy; |
||||
|
|
||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
public class ValidationInterceptor : AbpInterceptor, ITransientDependency |
||||
|
{ |
||||
|
private readonly IServiceProvider _serviceProvider; |
||||
|
|
||||
|
public ValidationInterceptor(IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
_serviceProvider = serviceProvider; |
||||
|
} |
||||
|
|
||||
|
public override void Intercept(IAbpMethodInvocation invocation) |
||||
|
{ |
||||
|
if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Validation)) |
||||
|
{ |
||||
|
invocation.Proceed(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
using (var scope = _serviceProvider.CreateScope()) |
||||
|
{ |
||||
|
var validator = scope.ServiceProvider.GetRequiredService<MethodInvocationValidator>(); |
||||
|
validator.Initialize(invocation.Method, invocation.Arguments); |
||||
|
validator.Validate(); |
||||
|
} |
||||
|
|
||||
|
invocation.Proceed(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using Volo.Abp.Application.Services; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
public static class ValidationInterceptorRegistrar |
||||
|
{ |
||||
|
public static void RegisterIfNeeded(IOnServiceRegistredContext context) |
||||
|
{ |
||||
|
if (typeof(IApplicationService).IsAssignableFrom(context.ImplementationType)) |
||||
|
{ |
||||
|
//TODO: Notice that it may add the interceptor more than one for every exposed service type!?
|
||||
|
context.Interceptors.Add<ValidationInterceptor>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,352 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Shouldly; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
using Volo.Abp.Autofac; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.TestBase; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
public class ApplicationService_Validation_Tests : AbpIntegratedTest<ApplicationService_Validation_Tests.ValidationTestModule> |
||||
|
{ |
||||
|
private readonly IMyAppService _myAppService; |
||||
|
|
||||
|
public ApplicationService_Validation_Tests() |
||||
|
{ |
||||
|
_myAppService = ServiceProvider.GetRequiredService<IMyAppService>(); |
||||
|
} |
||||
|
|
||||
|
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
||||
|
{ |
||||
|
options.UseAutofac(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Work_Proper_With_Right_Inputs() |
||||
|
{ |
||||
|
var output = _myAppService.MyMethod(new MyMethodInput { MyStringValue = "test" }); |
||||
|
output.Result.ShouldBe(42); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Not_Work_With_Wrong_Inputs() |
||||
|
{ |
||||
|
Assert.Throws<AbpValidationException>(() => _myAppService.MyMethod(new MyMethodInput())); //MyStringValue is not supplied!
|
||||
|
Assert.Throws<AbpValidationException>(() => _myAppService.MyMethod(new MyMethodInput { MyStringValue = "a" })); //MyStringValue's min length should be 3!
|
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Work_With_Right_Nesned_Inputs() |
||||
|
{ |
||||
|
var output = _myAppService.MyMethod2(new MyMethod2Input |
||||
|
{ |
||||
|
MyStringValue2 = "test 1", |
||||
|
Input1 = new MyMethodInput { MyStringValue = "test 2" }, |
||||
|
DateTimeValue = DateTime.Now |
||||
|
}); |
||||
|
output.Result.ShouldBe(42); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Not_Work_With_Wrong_Nesned_Inputs_1() |
||||
|
{ |
||||
|
Assert.Throws<AbpValidationException>(() => |
||||
|
_myAppService.MyMethod2(new MyMethod2Input |
||||
|
{ |
||||
|
MyStringValue2 = "test 1", |
||||
|
Input1 = new MyMethodInput() //MyStringValue is not set
|
||||
|
})); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Not_Work_With_Wrong_Nesned_Inputs_2() |
||||
|
{ |
||||
|
Assert.Throws<AbpValidationException>(() => |
||||
|
_myAppService.MyMethod2(new MyMethod2Input //Input1 is not set
|
||||
|
{ |
||||
|
MyStringValue2 = "test 1" |
||||
|
})); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Not_Work_With_Wrong_List_Input_1() |
||||
|
{ |
||||
|
Assert.Throws<AbpValidationException>(() => |
||||
|
_myAppService.MyMethod3( |
||||
|
new MyMethod3Input |
||||
|
{ |
||||
|
MyStringValue2 = "test 1", |
||||
|
ListItems = new List<MyClassInList> |
||||
|
{ |
||||
|
new MyClassInList {ValueInList = null} |
||||
|
} |
||||
|
})); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Not_Work_With_Wrong_Array_Input_1() |
||||
|
{ |
||||
|
Assert.Throws<AbpValidationException>(() => |
||||
|
_myAppService.MyMethod3( |
||||
|
new MyMethod3Input |
||||
|
{ |
||||
|
MyStringValue2 = "test 1", |
||||
|
ArrayItems = new[] |
||||
|
{ |
||||
|
new MyClassInList {ValueInList = null} |
||||
|
} |
||||
|
})); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Not_Work_If_Array_Is_Null() |
||||
|
{ |
||||
|
Assert.Throws<AbpValidationException>(() => |
||||
|
_myAppService.MyMethod4(new MyMethod4Input()) //ArrayItems is null!
|
||||
|
); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Work_If_Array_Is_Null_But_DisabledValidation_For_Method() |
||||
|
{ |
||||
|
_myAppService.MyMethod4_2(new MyMethod4Input()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Work_If_Array_Is_Null_But_DisabledValidation_For_Property() |
||||
|
{ |
||||
|
_myAppService.MyMethod5(new MyMethod5Input()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Use_IValidatableObject() |
||||
|
{ |
||||
|
Assert.Throws<AbpValidationException>(() => |
||||
|
{ |
||||
|
_myAppService.MyMethod6(new MyMethod6Input |
||||
|
{ |
||||
|
MyStringValue = "test value" //MyIntValue has not set!
|
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Normalize_Nested_Dtos() |
||||
|
{ |
||||
|
var input = new MyMethod7Input |
||||
|
{ |
||||
|
Inner = new MyMethod7Input.MyMethod7InputInner |
||||
|
{ |
||||
|
Value = 10 |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
_myAppService.MyMethod7(input); |
||||
|
|
||||
|
input.Inner.Value.ShouldBe(12); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Stop_Recursive_Validation_In_A_Constant_Depth() |
||||
|
{ |
||||
|
_myAppService.MyMethod8(new MyClassWithRecursiveReference { Value = "42" }).Result.ShouldBe(42); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Allow_Null_For_Nullable_Enums() |
||||
|
{ |
||||
|
_myAppService.MyMethodWithNullableEnum(null); |
||||
|
} |
||||
|
|
||||
|
[DependsOn(typeof(AbpAutofacModule))] |
||||
|
public class ValidationTestModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(IServiceCollection services) |
||||
|
{ |
||||
|
services.AddType<MyAppService>(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public interface IMyAppService : IApplicationService |
||||
|
{ |
||||
|
MyMethodOutput MyMethod(MyMethodInput input); |
||||
|
MyMethodOutput MyMethod2(MyMethod2Input input); |
||||
|
MyMethodOutput MyMethod3(MyMethod3Input input); |
||||
|
MyMethodOutput MyMethod4(MyMethod4Input input); |
||||
|
MyMethodOutput MyMethod4_2(MyMethod4Input input); |
||||
|
MyMethodOutput MyMethod5(MyMethod5Input input); |
||||
|
MyMethodOutput MyMethod6(MyMethod6Input input); |
||||
|
MyMethodOutput MyMethod7(MyMethod7Input input); |
||||
|
MyMethodOutput MyMethod8(MyClassWithRecursiveReference input); |
||||
|
void MyMethodWithNullableEnum(MyEnum? value); |
||||
|
} |
||||
|
|
||||
|
public class MyAppService : ApplicationService, IMyAppService |
||||
|
{ |
||||
|
public MyMethodOutput MyMethod(MyMethodInput input) |
||||
|
{ |
||||
|
return new MyMethodOutput { Result = 42 }; |
||||
|
} |
||||
|
|
||||
|
public MyMethodOutput MyMethod2(MyMethod2Input input) |
||||
|
{ |
||||
|
return new MyMethodOutput { Result = 42 }; |
||||
|
} |
||||
|
|
||||
|
public MyMethodOutput MyMethod3(MyMethod3Input input) |
||||
|
{ |
||||
|
return new MyMethodOutput { Result = 42 }; |
||||
|
} |
||||
|
|
||||
|
public MyMethodOutput MyMethod4(MyMethod4Input input) |
||||
|
{ |
||||
|
return new MyMethodOutput { Result = 42 }; |
||||
|
} |
||||
|
|
||||
|
[DisableValidation] |
||||
|
public MyMethodOutput MyMethod4_2(MyMethod4Input input) |
||||
|
{ |
||||
|
return new MyMethodOutput { Result = 42 }; |
||||
|
} |
||||
|
|
||||
|
public MyMethodOutput MyMethod5(MyMethod5Input input) |
||||
|
{ |
||||
|
return new MyMethodOutput { Result = 42 }; |
||||
|
} |
||||
|
|
||||
|
public MyMethodOutput MyMethod6(MyMethod6Input input) |
||||
|
{ |
||||
|
return new MyMethodOutput { Result = 42 }; |
||||
|
} |
||||
|
|
||||
|
public MyMethodOutput MyMethod7(MyMethod7Input input) |
||||
|
{ |
||||
|
return new MyMethodOutput { Result = 42 }; |
||||
|
} |
||||
|
|
||||
|
public MyMethodOutput MyMethod8(MyClassWithRecursiveReference input) |
||||
|
{ |
||||
|
return new MyMethodOutput { Result = 42 }; |
||||
|
} |
||||
|
|
||||
|
public void MyMethodWithNullableEnum(MyEnum? value) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class MyMethodInput |
||||
|
{ |
||||
|
[Required] |
||||
|
[MinLength(3)] |
||||
|
public string MyStringValue { get; set; } |
||||
|
} |
||||
|
|
||||
|
public class MyMethod2Input |
||||
|
{ |
||||
|
[Required] |
||||
|
[MinLength(2)] |
||||
|
public string MyStringValue2 { get; set; } |
||||
|
|
||||
|
public DateTime DateTimeValue { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
public MyMethodInput Input1 { get; set; } |
||||
|
} |
||||
|
|
||||
|
public class MyMethod3Input |
||||
|
{ |
||||
|
[Required] |
||||
|
[MinLength(2)] |
||||
|
public string MyStringValue2 { get; set; } |
||||
|
|
||||
|
public List<MyClassInList> ListItems { get; set; } |
||||
|
|
||||
|
public MyClassInList[] ArrayItems { get; set; } |
||||
|
} |
||||
|
|
||||
|
public class MyMethod4Input |
||||
|
{ |
||||
|
[Required] |
||||
|
public MyClassInList[] ArrayItems { get; set; } |
||||
|
} |
||||
|
|
||||
|
public class MyMethod5Input |
||||
|
{ |
||||
|
[DisableValidation] |
||||
|
public MyClassInList[] ArrayItems { get; set; } |
||||
|
} |
||||
|
|
||||
|
public class MyMethod6Input : IValidatableObject |
||||
|
{ |
||||
|
[Required] |
||||
|
[MinLength(2)] |
||||
|
public string MyStringValue { get; set; } |
||||
|
|
||||
|
public int MyIntValue { get; set; } |
||||
|
|
||||
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) |
||||
|
{ |
||||
|
if (MyIntValue < 18) |
||||
|
{ |
||||
|
yield return new ValidationResult("MyIntValue must be greather than or equal to 18"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class MyMethod7Input : IShouldNormalize |
||||
|
{ |
||||
|
public MyMethod7InputInner Inner { get; set; } |
||||
|
|
||||
|
public void Normalize() |
||||
|
{ |
||||
|
Inner.Value++; |
||||
|
} |
||||
|
|
||||
|
public class MyMethod7InputInner : IShouldNormalize |
||||
|
{ |
||||
|
public int Value { get; set; } |
||||
|
|
||||
|
public void Normalize() |
||||
|
{ |
||||
|
Value++; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class MyClassInList |
||||
|
{ |
||||
|
[Required] |
||||
|
[MinLength(3)] |
||||
|
public string ValueInList { get; set; } |
||||
|
} |
||||
|
|
||||
|
public class MyMethodOutput |
||||
|
{ |
||||
|
public int Result { get; set; } |
||||
|
} |
||||
|
|
||||
|
public class MyClassWithRecursiveReference |
||||
|
{ |
||||
|
public MyClassWithRecursiveReference Reference { get; } |
||||
|
|
||||
|
[Required] |
||||
|
public string Value { get; set; } |
||||
|
|
||||
|
public MyClassWithRecursiveReference() |
||||
|
{ |
||||
|
Reference = this; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public enum MyEnum |
||||
|
{ |
||||
|
Value1, |
||||
|
Value2 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue