From 5fd5fe9f44452c09ceb597ce858da01d4a4702c5 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 15 Apr 2019 14:39:01 +0800 Subject: [PATCH 1/2] fix #953 Add support for FluentValidation. --- framework/Volo.Abp.sln | 20 ++- .../Volo.Abp.FluentValidation.csproj | 25 +++ .../AbpFluentValidationCrossCuttingConcern.cs | 7 + .../AbpFluentValidationModule.cs | 13 ++ .../FluentValidationInterceptor.cs | 53 ++++++ .../FluentValidationInterceptorRegistrar.cs | 16 ++ .../Abp/FluentValidation/FluentValidator.cs | 38 ++++ .../Abp/FluentValidation/IFluentValidator.cs | 12 ++ .../Volo.Abp.FluentValidation.Tests.csproj | 21 +++ ...plicationService_FluentValidation_Tests.cs | 167 ++++++++++++++++++ 10 files changed, 369 insertions(+), 3 deletions(-) create mode 100644 framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj create mode 100644 framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationCrossCuttingConcern.cs create mode 100644 framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationModule.cs create mode 100644 framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptor.cs create mode 100644 framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptorRegistrar.cs create mode 100644 framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidator.cs create mode 100644 framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/IFluentValidator.cs create mode 100644 framework/test/Volo.Abp.FluentValidation.Tests/Volo.Abp.FluentValidation.Tests.csproj create mode 100644 framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 9814829086..312a7761d9 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -1,7 +1,7 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2036 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28803.156 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}" EndProject @@ -219,6 +219,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp", "src\Volo.Abp\Vo EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Authentication.JwtBearer", "src\Volo.Abp.AspNetCore.Authentication.JwtBearer\Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj", "{46C6336C-A1D8-4858-98CE-6F4C698C5A77}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.FluentValidation", "src\Volo.Abp.FluentValidation\Volo.Abp.FluentValidation.csproj", "{43D5FE61-ECBF-4B16-AD95-0043E18EB93A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.FluentValidation.Tests", "test\Volo.Abp.FluentValidation.Tests\Volo.Abp.FluentValidation.Tests.csproj", "{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -649,6 +653,14 @@ Global {46C6336C-A1D8-4858-98CE-6F4C698C5A77}.Debug|Any CPU.Build.0 = Debug|Any CPU {46C6336C-A1D8-4858-98CE-6F4C698C5A77}.Release|Any CPU.ActiveCfg = Release|Any CPU {46C6336C-A1D8-4858-98CE-6F4C698C5A77}.Release|Any CPU.Build.0 = Release|Any CPU + {43D5FE61-ECBF-4B16-AD95-0043E18EB93A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {43D5FE61-ECBF-4B16-AD95-0043E18EB93A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {43D5FE61-ECBF-4B16-AD95-0043E18EB93A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {43D5FE61-ECBF-4B16-AD95-0043E18EB93A}.Release|Any CPU.Build.0 = Release|Any CPU + {E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -760,6 +772,8 @@ Global {575BEFA1-19C2-49B1-8D31-B5D4472328DE} = {447C8A77-E5F0-4538-8687-7383196D04EA} {6C161F55-54B6-42A5-B177-3B0ED50323C1} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {46C6336C-A1D8-4858-98CE-6F4C698C5A77} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {43D5FE61-ECBF-4B16-AD95-0043E18EB93A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {E9E1714F-7ED2-4BD1-BA4A-BA06E398288A} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj b/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj new file mode 100644 index 0000000000..30f5278ad3 --- /dev/null +++ b/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj @@ -0,0 +1,25 @@ + + + + + + netstandard2.0 + Volo.Abp.FluentValidation + Volo.Abp.FluentValidation + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationCrossCuttingConcern.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationCrossCuttingConcern.cs new file mode 100644 index 0000000000..54a2e55a10 --- /dev/null +++ b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationCrossCuttingConcern.cs @@ -0,0 +1,7 @@ +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 new file mode 100644 index 0000000000..723b771200 --- /dev/null +++ b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationModule.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace Volo.Abp.FluentValidation +{ + public class AbpFluentValidationModule : AbpModule + { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + context.Services.OnRegistred(FluentValidationInterceptorRegistrar.RegisterIfNeeded); + } + } +} diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptor.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptor.cs new file mode 100644 index 0000000000..017813c5a3 --- /dev/null +++ b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptor.cs @@ -0,0 +1,53 @@ +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 new file mode 100644 index 0000000000..62fa6670ab --- /dev/null +++ b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidationInterceptorRegistrar.cs @@ -0,0 +1,16 @@ +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/FluentValidator.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidator.cs new file mode 100644 index 0000000000..b4b0d3d27e --- /dev/null +++ b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentValidator.cs @@ -0,0 +1,38 @@ +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 void Validate(MethodInvocationValidationContext context) + { + var validationResult = new AbpValidationResult(); + + foreach (var parameterValue in context.ParameterValues) + { + if (parameterValue is IValidator validator) + { + var result = validator.Validate(parameterValue); + if (!result.IsValid) + { + validationResult.Errors.AddRange(result.Errors.Select(error => + new ValidationResult(error.ErrorMessage))); + } + } + } + + if (validationResult.Errors.Any()) + { + throw new AbpValidationException( + "Method arguments are not valid! See ValidationErrors for details.", + context.Errors + ); + } + } + } +} diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/IFluentValidator.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/IFluentValidator.cs new file mode 100644 index 0000000000..58110d22ca --- /dev/null +++ b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/IFluentValidator.cs @@ -0,0 +1,12 @@ +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/test/Volo.Abp.FluentValidation.Tests/Volo.Abp.FluentValidation.Tests.csproj b/framework/test/Volo.Abp.FluentValidation.Tests/Volo.Abp.FluentValidation.Tests.csproj new file mode 100644 index 0000000000..ab2f82eefa --- /dev/null +++ b/framework/test/Volo.Abp.FluentValidation.Tests/Volo.Abp.FluentValidation.Tests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp2.2 + Volo.Abp.FluentValidation.Tests + Volo.Abp.FluentValidation.Tests + true + false + false + false + + + + + + + + + + + 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 new file mode 100644 index 0000000000..60cb373cf3 --- /dev/null +++ b/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs @@ -0,0 +1,167 @@ +using System.Threading.Tasks; +using FluentValidation; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Autofac; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.Validation; +using Xunit; + +namespace Volo.Abp.FluentValidation +{ + public class ApplicationService_FluentValidation_Tests : AbpIntegratedTest + { + private readonly IMyAppService _myAppService; + + public ApplicationService_FluentValidation_Tests() + { + _myAppService = ServiceProvider.GetRequiredService(); + } + + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + + [Fact] + public async Task Should_Work_Proper_With_Right_Inputs() + { + // MyStringValue should be aaa, MyStringValue2 should be bbb. MyStringValue3 should be ccc + var output = _myAppService.MyMethod(new MyMethodInput + { + MyStringValue = "aaa", + MyMethodInput2 = new MyMethodInput2 + { + MyStringValue2 = "bbb" + }, + MyMethodInput3 = new MyMethodInput3 + { + MyStringValue3 = "ccc" + } + }); + output.ShouldBe("aaabbbccc"); + + var asyncOutput = await _myAppService.MyMethodAsync(new MyMethodInput + { + MyStringValue = "aaa", + MyMethodInput2 = new MyMethodInput2 + { + MyStringValue2 = "bbb" + }, + MyMethodInput3 = new MyMethodInput3 + { + MyStringValue3 = "ccc" + } + }); + + asyncOutput.ShouldBe("aaabbbccc"); + } + + [Fact] + public async Task Should_Not_Work_With_Wrong_Inputs() + { + // MyStringValue should be aaa, MyStringValue2 should be bbb. MyStringValue3 should be ccc + + Assert.Throws(() => _myAppService.MyMethod(new MyMethodInput + { + MyStringValue = "a", + MyMethodInput2 = new MyMethodInput2 + { + MyStringValue2 = "b" + }, + MyMethodInput3 = new MyMethodInput3 + { + MyStringValue3 = "c" + } + })); + + await Assert.ThrowsAsync(async () => await _myAppService.MyMethodAsync( + new MyMethodInput + { + MyStringValue = "a", + MyMethodInput2 = new MyMethodInput2 + { + MyStringValue2 = "b" + }, + MyMethodInput3 = new MyMethodInput3 + { + MyStringValue3 = "c" + } + })); + } + + [DependsOn(typeof(AbpAutofacModule))] + [DependsOn(typeof(AbpFluentValidationModule))] + public class TestModule : AbpModule + { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + context.Services.OnRegistred(onServiceRegistredContext => + { + if (typeof(IMyAppService).IsAssignableFrom(onServiceRegistredContext.ImplementationType)) + { + onServiceRegistredContext.Interceptors.TryAdd(); + } + }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddType(); + } + } + + public interface IMyAppService + { + string MyMethod(MyMethodInput input); + + Task MyMethodAsync(MyMethodInput input); + } + + public class MyAppService : IMyAppService, ITransientDependency + { + public string MyMethod(MyMethodInput input) + { + return input.MyStringValue + input.MyMethodInput2.MyStringValue2 + input.MyMethodInput3.MyStringValue3; + } + + public Task MyMethodAsync(MyMethodInput input) + { + return Task.FromResult(input.MyStringValue + input.MyMethodInput2.MyStringValue2 + + input.MyMethodInput3.MyStringValue3); + } + } + + public class MyMethodInput : AbstractValidator + { + public MyMethodInput() + { + RuleFor(x => x.MyStringValue).Equal("aaa"); + RuleFor(x => x.MyMethodInput2.MyStringValue2).Equal("bbb"); + RuleFor(customer => customer.MyMethodInput3).SetValidator(new MyMethodInput3()); + } + + public string MyStringValue { get; set; } + + public MyMethodInput2 MyMethodInput2 { get; set; } + + public MyMethodInput3 MyMethodInput3 { get; set; } + } + + public class MyMethodInput2 + { + public string MyStringValue2 { get; set; } + } + + public class MyMethodInput3 : AbstractValidator + { + public MyMethodInput3() + { + RuleFor(x => x.MyStringValue3).Equal("ccc"); + } + + public string MyStringValue3 { get; set; } + } + } +} \ No newline at end of file From 6bd1ff7cbfcb629bbe875276f7bb9e4208a1a8ea Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 15 Apr 2019 20:06:24 +0800 Subject: [PATCH 2/2] 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