From 6bd1ff7cbfcb629bbe875276f7bb9e4208a1a8ea Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 15 Apr 2019 20:06:24 +0800 Subject: [PATCH] Separate validators from the DTO, Extend current validation interceptor. --- .../Volo.Abp.FluentValidation.csproj | 1 - ...bpFluentValidationConventionalRegistrar.cs | 41 ++++++++++++ .../AbpFluentValidationCrossCuttingConcern.cs | 7 -- .../AbpFluentValidationModule.cs | 12 +++- ....cs => FluentMethodInvocationValidator.cs} | 15 ++++- .../FluentValidationInterceptor.cs | 53 --------------- .../FluentValidationInterceptorRegistrar.cs | 16 ----- .../Abp/FluentValidation/IFluentValidator.cs | 12 ---- .../Abp/Validation/AbpValidationModule.cs | 8 +++ ...nfiguration.cs => AbpValidationOptions.cs} | 4 ++ .../Validation/IMethodInvocationValidator.cs | 4 +- .../Abp/Validation/ValidationInterceptor.cs | 32 +++++---- ...plicationService_FluentValidation_Tests.cs | 65 +++++++++++++++---- 13 files changed, 151 insertions(+), 119 deletions(-) create mode 100644 framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationConventionalRegistrar.cs delete mode 100644 framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationCrossCuttingConcern.cs rename framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/{FluentValidator.cs => FluentMethodInvocationValidator.cs} (67%) delete mode 100644 framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptor.cs delete mode 100644 framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptorRegistrar.cs delete mode 100644 framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/IFluentValidator.cs rename framework/src/Volo.Abp.Validation/Volo/Abp/Validation/{IValidationConfiguration.cs => AbpValidationOptions.cs} (58%) diff --git a/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj b/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj index 30f5278ad3..12084177a9 100644 --- a/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj +++ b/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj @@ -18,7 +18,6 @@ - diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationConventionalRegistrar.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationConventionalRegistrar.cs new file mode 100644 index 0000000000..e16450c213 --- /dev/null +++ b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationConventionalRegistrar.cs @@ -0,0 +1,41 @@ +using System; +using FluentValidation; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.FluentValidation +{ + public class AbpFluentValidationConventionalRegistrar : DefaultConventionalRegistrar + { + public override void AddType(IServiceCollection services, Type type) + { + if (typeof(IValidator).IsAssignableFrom(type)) + { + var dtoType = GetFirstGenericArgumentOrNull(type, 1); + if (dtoType != null) + { + var serverType = typeof(IValidator<>).MakeGenericType(dtoType); + var serviceDescriptor = ServiceDescriptor.Describe(serverType, type, ServiceLifetime.Transient); + + services.Add(serviceDescriptor); + } + } + } + + private static Type GetFirstGenericArgumentOrNull(Type type, int depth) + { + const int maxFindDepth = 8; + + if (depth >= maxFindDepth) + { + return null; + } + if (type.IsGenericType && type.GetGenericArguments().Length >= 1) + { + return type.GetGenericArguments()[0]; + } + + return GetFirstGenericArgumentOrNull(type.BaseType, depth + 1); + } + } +} diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationCrossCuttingConcern.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationCrossCuttingConcern.cs deleted file mode 100644 index 54a2e55a10..0000000000 --- a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationCrossCuttingConcern.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Volo.Abp.FluentValidation -{ - public static class AbpFluentValidationCrossCuttingConcern - { - public const string FluentValidation = "AbpFluentValidation"; - } -} diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationModule.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationModule.cs index 723b771200..e48404a36d 100644 --- a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationModule.cs +++ b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationModule.cs @@ -1,13 +1,23 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity; +using Volo.Abp.Validation; namespace Volo.Abp.FluentValidation { + [DependsOn(typeof(AbpValidationModule))] public class AbpFluentValidationModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services.OnRegistred(FluentValidationInterceptorRegistrar.RegisterIfNeeded); + context.Services.AddConventionalRegistrar(new AbpFluentValidationConventionalRegistrar()); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.ValidationContributor.Add(); + }); } } } diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidator.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentMethodInvocationValidator.cs similarity index 67% rename from framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidator.cs rename to framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentMethodInvocationValidator.cs index b4b0d3d27e..4749802cb1 100644 --- a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidator.cs +++ b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentMethodInvocationValidator.cs @@ -1,21 +1,30 @@ +using System; using System.ComponentModel.DataAnnotations; using System.Linq; using FluentValidation; -using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.Validation; namespace Volo.Abp.FluentValidation { - public class FluentValidator : IFluentValidator, ITransientDependency + public class FluentMethodInvocationValidator : IMethodInvocationValidator, ITransientDependency { + private readonly IServiceProvider _serviceProvider; + + public FluentMethodInvocationValidator(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + public void Validate(MethodInvocationValidationContext context) { var validationResult = new AbpValidationResult(); foreach (var parameterValue in context.ParameterValues) { - if (parameterValue is IValidator validator) + var serverType = typeof(IValidator<>).MakeGenericType(parameterValue.GetType()); + + if (_serviceProvider.GetService(serverType) is IValidator validator) { var result = validator.Validate(parameterValue); if (!result.IsValid) diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptor.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptor.cs deleted file mode 100644 index 017813c5a3..0000000000 --- a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptor.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Threading.Tasks; -using Volo.Abp.Aspects; -using Volo.Abp.DependencyInjection; -using Volo.Abp.DynamicProxy; -using Volo.Abp.Validation; - -namespace Volo.Abp.FluentValidation -{ - public class FluentValidationInterceptor : AbpInterceptor, ITransientDependency - { - private readonly IFluentValidator _fluentValidator; - - public FluentValidationInterceptor(IFluentValidator fluentValidator) - { - _fluentValidator = fluentValidator; - } - - public override void Intercept(IAbpMethodInvocation invocation) - { - if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpFluentValidationCrossCuttingConcern.FluentValidation)) - { - invocation.Proceed(); - return; - } - - Validate(invocation); - - invocation.Proceed(); - } - - public override async Task InterceptAsync(IAbpMethodInvocation invocation) - { - if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpFluentValidationCrossCuttingConcern.FluentValidation)) - { - await invocation.ProceedAsync(); - return; - } - - Validate(invocation); - - await invocation.ProceedAsync(); - } - - protected virtual void Validate(IAbpMethodInvocation invocation) - { - _fluentValidator.Validate(new MethodInvocationValidationContext( - invocation.TargetObject, - invocation.Method, - invocation.Arguments - )); - } - } -} diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptorRegistrar.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptorRegistrar.cs deleted file mode 100644 index 62fa6670ab..0000000000 --- a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptorRegistrar.cs +++ /dev/null @@ -1,16 +0,0 @@ -using FluentValidation; -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.FluentValidation -{ - public static class FluentValidationInterceptorRegistrar - { - public static void RegisterIfNeeded(IOnServiceRegistredContext context) - { - if (typeof(IValidator).IsAssignableFrom(context.ImplementationType)) - { - context.Interceptors.TryAdd(); - } - } - } -} diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/IFluentValidator.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/IFluentValidator.cs deleted file mode 100644 index 58110d22ca..0000000000 --- a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/IFluentValidator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Volo.Abp.Validation; - -namespace Volo.Abp.FluentValidation -{ - public interface IFluentValidator - { - void Validate(MethodInvocationValidationContext context); - } -} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs index 03efe9d66a..39041e044c 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs @@ -9,5 +9,13 @@ namespace Volo.Abp.Validation { context.Services.OnRegistred(ValidationInterceptorRegistrar.RegisterIfNeeded); } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.ValidationContributor.Add(); + }); + } } } diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IValidationConfiguration.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationOptions.cs similarity index 58% rename from framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IValidationConfiguration.cs rename to framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationOptions.cs index 7040be55bc..69737e5fb6 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IValidationConfiguration.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationOptions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Volo.Abp.Collections; namespace Volo.Abp.Validation { @@ -7,9 +8,12 @@ namespace Volo.Abp.Validation { public List IgnoredTypes { get; } + public ITypeList ValidationContributor { get; set; } + public AbpValidationOptions() { IgnoredTypes = new List(); + ValidationContributor = new TypeList(); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IMethodInvocationValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IMethodInvocationValidator.cs index 218ad9313d..fd413bc498 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IMethodInvocationValidator.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IMethodInvocationValidator.cs @@ -1,7 +1,7 @@ -namespace Volo.Abp.Validation +namespace Volo.Abp.Validation { public interface IMethodInvocationValidator { void Validate(MethodInvocationValidationContext context); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs index 86d58e89a3..f15554d1c7 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Volo.Abp.Aspects; using Volo.Abp.DependencyInjection; using Volo.Abp.DynamicProxy; @@ -7,11 +10,13 @@ namespace Volo.Abp.Validation { public class ValidationInterceptor : AbpInterceptor, ITransientDependency { - private readonly IMethodInvocationValidator _validator; + private readonly AbpValidationOptions _abpValidationOptions; + private readonly IServiceProvider _serviceProvider; - public ValidationInterceptor(IMethodInvocationValidator validator) + public ValidationInterceptor(IServiceProvider serviceProvider, IOptions abpValidationOptions) { - _validator = validator; + _serviceProvider = serviceProvider; + _abpValidationOptions = abpValidationOptions.Value; } public override void Intercept(IAbpMethodInvocation invocation) @@ -42,13 +47,18 @@ namespace Volo.Abp.Validation protected virtual void Validate(IAbpMethodInvocation invocation) { - _validator.Validate( - new MethodInvocationValidationContext( - invocation.TargetObject, - invocation.Method, - invocation.Arguments - ) - ); + foreach (var validationContributor in _abpValidationOptions.ValidationContributor) + { + var validator = (IMethodInvocationValidator) _serviceProvider.GetRequiredService(validationContributor); + + validator.Validate( + new MethodInvocationValidationContext( + invocation.TargetObject, + invocation.Method, + invocation.Arguments + ) + ); + } } } } diff --git a/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs b/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs index 60cb373cf3..467a45a999 100644 --- a/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs +++ b/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using FluentValidation; using Microsoft.Extensions.DependencyInjection; using Shouldly; @@ -91,6 +91,18 @@ namespace Volo.Abp.FluentValidation })); } + [Fact] + public void NotValidateMyMethod_Test() + { + var output = _myAppService.NotValidateMyMethod(new MyMethodInput4 + { + MyStringValue4 = "444" + }); + + output.ShouldBe("444"); + } + + [DependsOn(typeof(AbpAutofacModule))] [DependsOn(typeof(AbpFluentValidationModule))] public class TestModule : AbpModule @@ -101,7 +113,7 @@ namespace Volo.Abp.FluentValidation { if (typeof(IMyAppService).IsAssignableFrom(onServiceRegistredContext.ImplementationType)) { - onServiceRegistredContext.Interceptors.TryAdd(); + onServiceRegistredContext.Interceptors.TryAdd(); } }); } @@ -117,6 +129,8 @@ namespace Volo.Abp.FluentValidation string MyMethod(MyMethodInput input); Task MyMethodAsync(MyMethodInput input); + + string NotValidateMyMethod(MyMethodInput4 input); } public class MyAppService : IMyAppService, ITransientDependency @@ -131,17 +145,15 @@ namespace Volo.Abp.FluentValidation return Task.FromResult(input.MyStringValue + input.MyMethodInput2.MyStringValue2 + input.MyMethodInput3.MyStringValue3); } - } - public class MyMethodInput : AbstractValidator - { - public MyMethodInput() + public string NotValidateMyMethod(MyMethodInput4 input) { - RuleFor(x => x.MyStringValue).Equal("aaa"); - RuleFor(x => x.MyMethodInput2.MyStringValue2).Equal("bbb"); - RuleFor(customer => customer.MyMethodInput3).SetValidator(new MyMethodInput3()); + return input.MyStringValue4; } + } + public class MyMethodInput + { public string MyStringValue { get; set; } public MyMethodInput2 MyMethodInput2 { get; set; } @@ -154,14 +166,41 @@ namespace Volo.Abp.FluentValidation public string MyStringValue2 { get; set; } } - public class MyMethodInput3 : AbstractValidator + public class MyMethodInput3 { - public MyMethodInput3() + + public string MyStringValue3 { get; set; } + } + + public class MyMethodInput4 + { + public string MyStringValue4 { get; set; } + } + + public class MyMethodInputValidator : AbstractValidator + { + public MyMethodInputValidator() { - RuleFor(x => x.MyStringValue3).Equal("ccc"); + RuleFor(x => x.MyStringValue).Equal("aaa"); + RuleFor(x => x.MyMethodInput2.MyStringValue2).Equal("bbb"); + RuleFor(customer => customer.MyMethodInput3).SetValidator(new MyMethodInput3Validator()); } + } - public string MyStringValue3 { get; set; } + public class MethodInputBaseValidator : AbstractValidator + { + public MethodInputBaseValidator() + { + RuleFor(x => x.MyStringValue3).NotNull(); + } + } + + public class MyMethodInput3Validator : MethodInputBaseValidator + { + public MyMethodInput3Validator() + { + RuleFor(x => x.MyStringValue3).Equal("ccc"); + } } } } \ No newline at end of file