From e0c37f8b0cafc6a3b14bb5a259942eb638892de3 Mon Sep 17 00:00:00 2001
From: cKey <35512826+colinin@users.noreply.github.com>
Date: Mon, 5 Sep 2022 15:27:48 +0800
Subject: [PATCH 1/2] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E5=AE=A2=E6=88=B7?=
=?UTF-8?q?=E7=AB=AF=E4=BB=A3=E7=90=86=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
1:增加
**LINGYUN.Abp.Http.Client.Wrapper**模块,依赖此模块自动识别远程客户端代理时是否使用包装器;
2:**LINGYUN.Abp.Dapr.Client**增加接口方法**AddStaticDaprClientProxies**,可以通过***.generate-proxy.json**配置文件调用远程接口;
---
aspnet-core/LINGYUN.MicroService.All.sln | 14 ++
.../FodyWeavers.xml | 0
.../FodyWeavers.xsd | 0
.../LINGYUN.Abp.Http.Client.Wrapper.csproj} | 8 +-
.../Wrapper/AbpHttpClientWrapperModule.cs | 33 ++++
.../Wrapper/AbpHttpClientWrapperModule.cs | 13 --
...amicHttpProxyInterceptorWrapClientProxy.cs | 153 ------------------
.../Wrapper/AbpDaprClientWrapperModule.cs | 17 +-
.../AbpDaprClientProxyOptions.cs | 5 +-
.../ClientProxying/DaprClientProxyBase.cs | 124 ++++++++++++++
.../DynamicDaprClientProxyInterceptor.cs | 3 +-
.../DynamicDaprProxyInterceptorClientProxy.cs | 122 +-------------
...iceCollectionDaprClientProxyExtensions.cs} | 35 +++-
.../dapr/LINGYUN.Abp.Dapr.Client/README.md | 2 +-
.../Dapr/ServiceInvocation/TestAppService.cs | 7 +-
.../TestClientProxy.Generated.cs | 45 ++++++
.../ClientProxies/TestClientProxy.cs | 7 +
.../ClientProxies/app-generate-proxy.json | 114 +++++++++++++
.../LINGYUN.Abp.Dapr.Client.Tests.csproj | 10 +-
.../Client/Tests/AbpDaptClientTestModule.cs | 11 +-
.../Tests/TestAppServiceStaticProxyTests.cs | 53 ++++++
.../Dapr/Client/Tests/TestAppServiceTests.cs | 10 +-
.../Dapr/ServiceInvocation/ITestAppService.cs | 2 +-
23 files changed, 476 insertions(+), 312 deletions(-)
rename aspnet-core/modules/common/{LINGYUN.Abp.HttpClient.Wrapper => LINGYUN.Abp.Http.Client.Wrapper}/FodyWeavers.xml (100%)
rename aspnet-core/modules/common/{LINGYUN.Abp.HttpClient.Wrapper => LINGYUN.Abp.Http.Client.Wrapper}/FodyWeavers.xsd (100%)
rename aspnet-core/modules/common/{LINGYUN.Abp.HttpClient.Wrapper/LINGYUN.Abp.HttpClient.Wrapper.csproj => LINGYUN.Abp.Http.Client.Wrapper/LINGYUN.Abp.Http.Client.Wrapper.csproj} (63%)
create mode 100644 aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/LINGYUN/Abp/Http/Client/Wrapper/AbpHttpClientWrapperModule.cs
delete mode 100644 aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/AbpHttpClientWrapperModule.cs
delete mode 100644 aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/DynamicHttpProxyInterceptorWrapClientProxy.cs
rename aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/{DynamicProxying => ClientProxying}/AbpDaprClientProxyOptions.cs (90%)
create mode 100644 aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/ClientProxying/DaprClientProxyBase.cs
rename aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/Microsoft/Extensions/DependencyInjection/{ServiceCollectionDynamicDaprClientProxyExtensions.cs => ServiceCollectionDaprClientProxyExtensions.cs} (81%)
create mode 100644 aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/TestClientProxy.Generated.cs
create mode 100644 aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/TestClientProxy.cs
create mode 100644 aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/app-generate-proxy.json
create mode 100644 aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/TestAppServiceStaticProxyTests.cs
diff --git a/aspnet-core/LINGYUN.MicroService.All.sln b/aspnet-core/LINGYUN.MicroService.All.sln
index 64f8b664f..d7c8a7fa4 100644
--- a/aspnet-core/LINGYUN.MicroService.All.sln
+++ b/aspnet-core/LINGYUN.MicroService.All.sln
@@ -468,6 +468,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.Jobs", "modules\common\LINGYUN.Abp.Notifications.Jobs\LINGYUN.Abp.Notifications.Jobs.csproj", "{3E9D07D8-963C-4A61-B720-BD47593BE752}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Http.Client.Wrapper", "modules\common\LINGYUN.Abp.Http.Client.Wrapper\LINGYUN.Abp.Http.Client.Wrapper.csproj", "{942816E3-B270-40DC-9532-C1077FF59A32}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Dapr.Actors.AspNetCore.Wrapper", "modules\dapr\LINGYUN.Abp.Dapr.Actors.AspNetCore.Wrapper\LINGYUN.Abp.Dapr.Actors.AspNetCore.Wrapper.csproj", "{FBB50072-33BE-4B4A-8908-E98BC0C80B92}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1214,6 +1218,14 @@ Global
{3E9D07D8-963C-4A61-B720-BD47593BE752}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E9D07D8-963C-4A61-B720-BD47593BE752}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E9D07D8-963C-4A61-B720-BD47593BE752}.Release|Any CPU.Build.0 = Release|Any CPU
+ {942816E3-B270-40DC-9532-C1077FF59A32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {942816E3-B270-40DC-9532-C1077FF59A32}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {942816E3-B270-40DC-9532-C1077FF59A32}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {942816E3-B270-40DC-9532-C1077FF59A32}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FBB50072-33BE-4B4A-8908-E98BC0C80B92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FBB50072-33BE-4B4A-8908-E98BC0C80B92}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FBB50072-33BE-4B4A-8908-E98BC0C80B92}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FBB50072-33BE-4B4A-8908-E98BC0C80B92}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1443,6 +1455,8 @@ Global
{ECC8B9A9-9E92-4493-984D-2E350A49189D} = {C5CAD011-DF84-4914-939C-0C029DCEF26F}
{62971DAE-CE71-4E9C-B6F5-514C8E2B915C} = {ECC8B9A9-9E92-4493-984D-2E350A49189D}
{3E9D07D8-963C-4A61-B720-BD47593BE752} = {ECC8B9A9-9E92-4493-984D-2E350A49189D}
+ {942816E3-B270-40DC-9532-C1077FF59A32} = {8AC72641-30D3-4ACF-89FA-808FADC55C2E}
+ {FBB50072-33BE-4B4A-8908-E98BC0C80B92} = {DC33925B-264D-421B-96CC-46F853CBCC70}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/FodyWeavers.xml b/aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/FodyWeavers.xml
similarity index 100%
rename from aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/FodyWeavers.xml
rename to aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/FodyWeavers.xml
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/FodyWeavers.xsd b/aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/FodyWeavers.xsd
similarity index 100%
rename from aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/FodyWeavers.xsd
rename to aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/FodyWeavers.xsd
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN.Abp.HttpClient.Wrapper.csproj b/aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/LINGYUN.Abp.Http.Client.Wrapper.csproj
similarity index 63%
rename from aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN.Abp.HttpClient.Wrapper.csproj
rename to aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/LINGYUN.Abp.Http.Client.Wrapper.csproj
index 40f24499c..8ac196841 100644
--- a/aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN.Abp.HttpClient.Wrapper.csproj
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/LINGYUN.Abp.Http.Client.Wrapper.csproj
@@ -4,7 +4,7 @@
- net6.0
+ netstandard2.0
@@ -12,8 +12,8 @@
-
-
-
+
+
+
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/LINGYUN/Abp/Http/Client/Wrapper/AbpHttpClientWrapperModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/LINGYUN/Abp/Http/Client/Wrapper/AbpHttpClientWrapperModule.cs
new file mode 100644
index 000000000..723d010bc
--- /dev/null
+++ b/aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/LINGYUN/Abp/Http/Client/Wrapper/AbpHttpClientWrapperModule.cs
@@ -0,0 +1,33 @@
+using LINGYUN.Abp.Wrapper;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Volo.Abp.Http.Client;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Http.Client.Wrapper;
+
+[DependsOn(
+ typeof(AbpHttpClientModule),
+ typeof(AbpWrapperModule))]
+public class AbpHttpClientWrapperModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ PreConfigure(options =>
+ {
+ options.ProxyClientBuildActions.Add(
+ (_, builder) =>
+ {
+ builder.ConfigureHttpClient((provider, client) =>
+ {
+ var wrapperOptions = provider.GetRequiredService>();
+ var wrapperHeader = wrapperOptions.Value.IsEnabled
+ ? AbpHttpWrapConsts.AbpWrapResult
+ : AbpHttpWrapConsts.AbpDontWrapResult;
+
+ client.DefaultRequestHeaders.TryAddWithoutValidation(wrapperHeader, "true");
+ });
+ });
+ });
+ }
+}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/AbpHttpClientWrapperModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/AbpHttpClientWrapperModule.cs
deleted file mode 100644
index 9f7aa9db9..000000000
--- a/aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/AbpHttpClientWrapperModule.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using LINGYUN.Abp.Wrapper;
-using Volo.Abp.Http.Client;
-using Volo.Abp.Modularity;
-
-namespace LINGYUN.Abp.HttpClient.Wrapper
-{
- [DependsOn(
- typeof(AbpHttpClientModule),
- typeof(AbpWrapperModule))]
- public class AbpHttpClientWrapperModule : AbpModule
- {
- }
-}
diff --git a/aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/DynamicHttpProxyInterceptorWrapClientProxy.cs b/aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/DynamicHttpProxyInterceptorWrapClientProxy.cs
deleted file mode 100644
index b568c0da6..000000000
--- a/aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/DynamicHttpProxyInterceptorWrapClientProxy.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-using LINGYUN.Abp.Wrapper;
-using Microsoft.Extensions.Options;
-using System;
-using System.Collections.Generic;
-using System.Net.Http;
-using System.Threading.Tasks;
-using Volo.Abp;
-using Volo.Abp.Content;
-using Volo.Abp.DependencyInjection;
-using Volo.Abp.Http;
-using Volo.Abp.Http.Client;
-using Volo.Abp.Http.Client.Authentication;
-using Volo.Abp.Http.Client.ClientProxying;
-using Volo.Abp.Http.Client.DynamicProxying;
-
-namespace LINGYUN.Abp.HttpClient.Wrapper
-{
- [Dependency(ReplaceServices = true)]
- [ExposeServices(typeof(DynamicHttpProxyInterceptorClientProxy<>))]
- public class DynamicHttpProxyInterceptorWrapClientProxy
- : DynamicHttpProxyInterceptorClientProxy, ITransientDependency
- {
- protected IOptions WrapperOptions => LazyServiceProvider.LazyGetRequiredService>();
-
- protected override async Task RequestAsync(ClientProxyRequestContext requestContext)
- {
- var response = await RequestAndGetResponseAsync(requestContext);
-
- var responseContent = response.Content;
-
- if (typeof(T) == typeof(IRemoteStreamContent) ||
- typeof(T) == typeof(RemoteStreamContent))
- {
- /* returning a class that holds a reference to response
- * content just to be sure that GC does not dispose of
- * it before we finish doing our work with the stream */
- return (T)(object)new RemoteStreamContent(
- await responseContent.ReadAsStreamAsync(),
- responseContent.Headers?.ContentDisposition?.FileNameStar ??
- RemoveQuotes(responseContent.Headers?.ContentDisposition?.FileName).ToString(),
- responseContent.Headers?.ContentType?.ToString(),
- responseContent.Headers?.ContentLength);
- }
-
- var stringContent = await responseContent.ReadAsStringAsync();
-
- if (stringContent.IsNullOrWhiteSpace())
- {
- return default;
- }
-
- // 对于包装后的结果需要处理
- if (response.Headers.Contains(AbpHttpWrapConsts.AbpWrapResult))
- {
- var wrapResult = JsonSerializer.Deserialize>(stringContent);
-
- ThrowExceptionForResponse(wrapResult);
-
- if (typeof(T) == typeof(string))
- {
- return (T)(object)wrapResult.Result;
- }
-
- return wrapResult.Result;
- }
-
- if (typeof(T) == typeof(string))
- {
- return (T)(object)stringContent;
- }
-
- if (stringContent.IsNullOrWhiteSpace())
- {
- return default;
- }
-
- return JsonSerializer.Deserialize(stringContent);
- }
-
- public override async Task CallRequestAsync(ClientProxyRequestContext requestContext)
- {
- var response = await RequestAndGetResponseAsync(requestContext);
- // 对于包装后的结果需要处理
- if (response.Headers.Contains(AbpHttpWrapConsts.AbpWrapResult))
- {
- var stringContent = await response.Content.ReadAsStringAsync();
- var wrapResult = JsonSerializer.Deserialize(stringContent);
-
- ThrowExceptionForResponse(wrapResult);
- }
-
- return response.Content;
- }
-
- protected virtual void ThrowExceptionForResponse(WrapResult wrapResult)
- {
- if (!string.Equals(wrapResult.Code, WrapperOptions.Value.CodeWithSuccess))
- {
- var errorInfo = new RemoteServiceErrorInfo(
- wrapResult.Message,
- wrapResult.Details,
- wrapResult.Code);
- throw new AbpRemoteCallException(errorInfo)
- {
- HttpStatusCode = (int)WrapperOptions.Value.HttpStatusCode
- };
- }
- }
-
- protected virtual async Task RequestAndGetResponseAsync(ClientProxyRequestContext requestContext)
- {
- var clientConfig = ClientOptions.Value.HttpClientProxies.GetOrDefault(requestContext.ServiceType) ?? throw new AbpException($"Could not get HttpClientProxyConfig for {requestContext.ServiceType.FullName}.");
- var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(clientConfig.RemoteServiceName);
-
- var client = HttpClientFactory.Create(clientConfig.RemoteServiceName);
-
- var apiVersion = await GetApiVersionInfoAsync(requestContext);
- var url = remoteServiceConfig.BaseUrl.EnsureEndsWith('/') + await GetUrlWithParametersAsync(requestContext, apiVersion);
-
- var requestMessage = new HttpRequestMessage(requestContext.Action.GetHttpMethod(), url)
- {
- Content = await ClientProxyRequestPayloadBuilder.BuildContentAsync(requestContext.Action, requestContext.Arguments, JsonSerializer, apiVersion)
- };
-
- AddHeaders(requestContext.Arguments, requestContext.Action, requestMessage, apiVersion);
-
- if (requestContext.Action.AllowAnonymous != true)
- {
- await ClientAuthenticator.Authenticate(
- new RemoteServiceHttpClientAuthenticateContext(
- client,
- requestMessage,
- remoteServiceConfig,
- clientConfig.RemoteServiceName
- )
- );
- }
-
- var response = await client.SendAsync(
- requestMessage,
- HttpCompletionOption.ResponseHeadersRead /*this will buffer only the headers, the content will be used as a stream*/,
- GetCancellationToken(requestContext.Arguments)
- );
-
- if (!response.IsSuccessStatusCode)
- {
- await ThrowExceptionForResponseAsync(response);
- }
-
- return response;
- }
- }
-}
diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client.Wrapper/LINGYUN/Abp/Dapr/Client/Wrapper/AbpDaprClientWrapperModule.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client.Wrapper/LINGYUN/Abp/Dapr/Client/Wrapper/AbpDaprClientWrapperModule.cs
index 9b17166bf..2766623bb 100644
--- a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client.Wrapper/LINGYUN/Abp/Dapr/Client/Wrapper/AbpDaprClientWrapperModule.cs
+++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client.Wrapper/LINGYUN/Abp/Dapr/Client/Wrapper/AbpDaprClientWrapperModule.cs
@@ -1,10 +1,11 @@
-using LINGYUN.Abp.Dapr.Client.DynamicProxying;
-using LINGYUN.Abp.Wrapper;
+using LINGYUN.Abp.Wrapper;
using Microsoft.Extensions.Options;
using Volo.Abp.Http;
using Volo.Abp.Http.Client;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
+using Microsoft.Extensions.DependencyInjection;
+using LINGYUN.Abp.Dapr.Client.ClientProxying;
namespace LINGYUN.Abp.Dapr.Client.Wrapper
{
@@ -14,8 +15,20 @@ namespace LINGYUN.Abp.Dapr.Client.Wrapper
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
+ var wrapperOptions = context.Services.ExecutePreConfiguredActions();
+
Configure(options =>
{
+ options.ProxyRequestActions.Add(
+ (_, request) =>
+ {
+ var wrapperHeader = wrapperOptions.IsEnabled
+ ? AbpHttpWrapConsts.AbpWrapResult
+ : AbpHttpWrapConsts.AbpDontWrapResult;
+
+ request.Headers.TryAddWithoutValidation(wrapperHeader, "true");
+ });
+
options.OnResponse(async (response, serviceProvider) =>
{
var stringContent = await response.Content.ReadAsStringAsync();
diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/AbpDaprClientProxyOptions.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/ClientProxying/AbpDaprClientProxyOptions.cs
similarity index 90%
rename from aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/AbpDaprClientProxyOptions.cs
rename to aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/ClientProxying/AbpDaprClientProxyOptions.cs
index 9aa24063e..d359469c9 100644
--- a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/AbpDaprClientProxyOptions.cs
+++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/ClientProxying/AbpDaprClientProxyOptions.cs
@@ -1,10 +1,11 @@
-using System;
+using LINGYUN.Abp.Dapr.Client.DynamicProxying;
+using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
-namespace LINGYUN.Abp.Dapr.Client.DynamicProxying
+namespace LINGYUN.Abp.Dapr.Client.ClientProxying
{
public class AbpDaprClientProxyOptions
{
diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/ClientProxying/DaprClientProxyBase.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/ClientProxying/DaprClientProxyBase.cs
new file mode 100644
index 000000000..dca292e43
--- /dev/null
+++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/ClientProxying/DaprClientProxyBase.cs
@@ -0,0 +1,124 @@
+using Dapr.Client;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Content;
+using Volo.Abp.Http.Client.Authentication;
+using Volo.Abp.Http.Client.ClientProxying;
+
+namespace LINGYUN.Abp.Dapr.Client.ClientProxying
+{
+ public abstract class DaprClientProxyBase : ClientProxyBase
+ {
+ protected IOptions DaprClientProxyOptions => LazyServiceProvider.LazyGetRequiredService>();
+ protected IDaprClientFactory DaprClientFactory => LazyServiceProvider.LazyGetRequiredService();
+
+ protected async override Task RequestAsync(ClientProxyRequestContext requestContext)
+ {
+ var response = await MakeRequestAsync(requestContext);
+
+ var responseContent = response.Content;
+
+ if (typeof(T) == typeof(IRemoteStreamContent) ||
+ typeof(T) == typeof(RemoteStreamContent))
+ {
+ /* returning a class that holds a reference to response
+ * content just to be sure that GC does not dispose of
+ * it before we finish doing our work with the stream */
+ return (T)(object)new RemoteStreamContent(
+ await responseContent.ReadAsStreamAsync(),
+ responseContent.Headers?.ContentDisposition?.FileNameStar ??
+ RemoveQuotes(responseContent.Headers?.ContentDisposition?.FileName).ToString(),
+ responseContent.Headers?.ContentType?.ToString(),
+ responseContent.Headers?.ContentLength);
+ }
+
+ var stringContent = await DaprClientProxyOptions
+ .Value
+ .ProxyResponseContent(response, LazyServiceProvider);
+
+ if (stringContent.IsNullOrWhiteSpace())
+ {
+ return default;
+ }
+
+ if (typeof(T) == typeof(string))
+ {
+ return (T)(object)stringContent;
+ }
+
+ return JsonSerializer.Deserialize(stringContent);
+ }
+
+ protected async override Task GetConfiguredApiVersionAsync(ClientProxyRequestContext requestContext)
+ {
+ var clientConfig = DaprClientProxyOptions.Value.DaprClientProxies.GetOrDefault(requestContext.ServiceType)
+ ?? throw new AbpException($"Could not get DynamicDaprClientProxyConfig for {requestContext.ServiceType.FullName}.");
+ var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(clientConfig.RemoteServiceName);
+
+ return remoteServiceConfig?.Version;
+ }
+
+ private async Task MakeRequestAsync(ClientProxyRequestContext requestContext)
+ {
+ var clientConfig = DaprClientProxyOptions.Value.DaprClientProxies.GetOrDefault(requestContext.ServiceType) ?? throw new AbpException($"Could not get DaprClientProxyConfig for {requestContext.ServiceType.FullName}.");
+ var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(clientConfig.RemoteServiceName);
+
+ var appId = remoteServiceConfig.GetAppId();
+ var apiVersion = await GetApiVersionInfoAsync(requestContext);
+ var methodName = await GetUrlWithParametersAsync(requestContext, apiVersion);
+ // See: https://docs.dapr.io/reference/api/service_invocation_api/#examples
+ var daprClient = DaprClientFactory.CreateClient(clientConfig.RemoteServiceName);
+ var requestMessage = daprClient.CreateInvokeMethodRequest(
+ requestContext.Action.GetHttpMethod(),
+ appId,
+ methodName);
+ requestMessage.Content = await ClientProxyRequestPayloadBuilder.BuildContentAsync(
+ requestContext.Action,
+ requestContext.Arguments,
+ JsonSerializer,
+ apiVersion);
+
+ AddHeaders(requestContext.Arguments, requestContext.Action, requestMessage, apiVersion);
+
+ if (requestContext.Action.AllowAnonymous != true)
+ {
+ var httpClient = HttpClientFactory.Create(AbpDaprClientModule.DaprHttpClient);
+
+ await ClientAuthenticator.Authenticate(
+ new RemoteServiceHttpClientAuthenticateContext(
+ httpClient,
+ requestMessage,
+ remoteServiceConfig,
+ clientConfig.RemoteServiceName
+ )
+ );
+
+ // 其他库可能将授权标头写入到HttpClient中
+ if (requestMessage.Headers.Authorization == null &&
+ httpClient.DefaultRequestHeaders.Authorization != null)
+ {
+ requestMessage.Headers.Authorization = httpClient.DefaultRequestHeaders.Authorization;
+ }
+ }
+
+ // 增加一个可配置的请求消息
+ foreach (var clientRequestAction in DaprClientProxyOptions.Value.ProxyRequestActions)
+ {
+ clientRequestAction(appId, requestMessage);
+ }
+
+ var response = await daprClient.InvokeMethodWithResponseAsync(requestMessage, GetCancellationToken(requestContext.Arguments));
+
+ if (!response.IsSuccessStatusCode)
+ {
+ await ThrowExceptionForResponseAsync(response);
+ }
+
+ return response;
+ }
+ }
+}
diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/DynamicDaprClientProxyInterceptor.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/DynamicDaprClientProxyInterceptor.cs
index baea6e563..2344d9892 100644
--- a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/DynamicDaprClientProxyInterceptor.cs
+++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/DynamicDaprClientProxyInterceptor.cs
@@ -1,4 +1,5 @@
-using Microsoft.Extensions.Logging;
+using LINGYUN.Abp.Dapr.Client.ClientProxying;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using System;
diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/DynamicDaprProxyInterceptorClientProxy.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/DynamicDaprProxyInterceptorClientProxy.cs
index 80d7e550f..489dec591 100644
--- a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/DynamicDaprProxyInterceptorClientProxy.cs
+++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/DynamicDaprProxyInterceptorClientProxy.cs
@@ -1,134 +1,20 @@
-using Dapr.Client;
-using Microsoft.Extensions.Options;
-using System;
-using System.Collections.Generic;
+using LINGYUN.Abp.Dapr.Client.ClientProxying;
using System.Net.Http;
using System.Threading.Tasks;
-using Volo.Abp;
-using Volo.Abp.Content;
-using Volo.Abp.Http.Client.Authentication;
using Volo.Abp.Http.Client.ClientProxying;
namespace LINGYUN.Abp.Dapr.Client.DynamicProxying
{
- public class DynamicDaprProxyInterceptorClientProxy : ClientProxyBase
+ public class DynamicDaprProxyInterceptorClientProxy : DaprClientProxyBase
{
- protected IOptions DaprClientProxyOptions => LazyServiceProvider.LazyGetRequiredService>();
- protected IDaprClientFactory DaprClientFactory => LazyServiceProvider.LazyGetRequiredService();
-
- public virtual async Task CallRequestAsync(ClientProxyRequestContext requestContext)
+ public async virtual Task CallRequestAsync(ClientProxyRequestContext requestContext)
{
return await RequestAsync(requestContext);
}
- public virtual async Task CallRequestAsync(ClientProxyRequestContext requestContext)
+ public async virtual Task CallRequestAsync(ClientProxyRequestContext requestContext)
{
return await RequestAsync(requestContext);
}
-
- protected override async Task RequestAsync(ClientProxyRequestContext requestContext)
- {
- var response = await MakeRequestAsync(requestContext);
-
- var responseContent = response.Content;
-
- if (typeof(T) == typeof(IRemoteStreamContent) ||
- typeof(T) == typeof(RemoteStreamContent))
- {
- /* returning a class that holds a reference to response
- * content just to be sure that GC does not dispose of
- * it before we finish doing our work with the stream */
- return (T)(object)new RemoteStreamContent(
- await responseContent.ReadAsStreamAsync(),
- responseContent.Headers?.ContentDisposition?.FileNameStar ??
- RemoveQuotes(responseContent.Headers?.ContentDisposition?.FileName).ToString(),
- responseContent.Headers?.ContentType?.ToString(),
- responseContent.Headers?.ContentLength);
- }
-
- var stringContent = await DaprClientProxyOptions
- .Value
- .ProxyResponseContent(response, LazyServiceProvider);
-
- if (stringContent.IsNullOrWhiteSpace())
- {
- return default;
- }
-
- if (typeof(T) == typeof(string))
- {
- return (T)(object)stringContent;
- }
-
- return JsonSerializer.Deserialize(stringContent);
- }
-
- protected override async Task GetConfiguredApiVersionAsync(ClientProxyRequestContext requestContext)
- {
- var clientConfig = DaprClientProxyOptions.Value.DaprClientProxies.GetOrDefault(requestContext.ServiceType)
- ?? throw new AbpException($"Could not get DynamicDaprClientProxyConfig for {requestContext.ServiceType.FullName}.");
- var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(clientConfig.RemoteServiceName);
-
- return remoteServiceConfig?.Version;
- }
-
- private async Task MakeRequestAsync(ClientProxyRequestContext requestContext)
- {
- var clientConfig = DaprClientProxyOptions.Value.DaprClientProxies.GetOrDefault(requestContext.ServiceType) ?? throw new AbpException($"Could not get DaprClientProxyConfig for {requestContext.ServiceType.FullName}.");
- var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(clientConfig.RemoteServiceName);
-
- var appId = remoteServiceConfig.GetAppId();
- var apiVersion = await GetApiVersionInfoAsync(requestContext);
- var methodName = await GetUrlWithParametersAsync(requestContext, apiVersion);
- // See: https://docs.dapr.io/reference/api/service_invocation_api/#examples
- var daprClient = DaprClientFactory.CreateClient(clientConfig.RemoteServiceName);
- var requestMessage = daprClient.CreateInvokeMethodRequest(
- requestContext.Action.GetHttpMethod(),
- appId,
- methodName);
- requestMessage.Content = await ClientProxyRequestPayloadBuilder.BuildContentAsync(
- requestContext.Action,
- requestContext.Arguments,
- JsonSerializer,
- apiVersion);
-
- AddHeaders(requestContext.Arguments, requestContext.Action, requestMessage, apiVersion);
-
- if (requestContext.Action.AllowAnonymous != true)
- {
- var httpClient = HttpClientFactory.Create(AbpDaprClientModule.DaprHttpClient);
-
- await ClientAuthenticator.Authenticate(
- new RemoteServiceHttpClientAuthenticateContext(
- httpClient,
- requestMessage,
- remoteServiceConfig,
- clientConfig.RemoteServiceName
- )
- );
-
- // 其他库可能将授权标头写入到HttpClient中
- if (requestMessage.Headers.Authorization == null &&
- httpClient.DefaultRequestHeaders.Authorization != null)
- {
- requestMessage.Headers.Authorization = httpClient.DefaultRequestHeaders.Authorization;
- }
- }
-
- // 增加一个可配置的请求消息
- foreach (var clientRequestAction in DaprClientProxyOptions.Value.ProxyRequestActions)
- {
- clientRequestAction(appId, requestMessage);
- }
-
- var response = await daprClient.InvokeMethodWithResponseAsync(requestMessage, GetCancellationToken(requestContext.Arguments));
-
- if (!response.IsSuccessStatusCode)
- {
- await ThrowExceptionForResponseAsync(response);
- }
-
- return response;
- }
}
}
diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicDaprClientProxyExtensions.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDaprClientProxyExtensions.cs
similarity index 81%
rename from aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicDaprClientProxyExtensions.cs
rename to aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDaprClientProxyExtensions.cs
index c5d34a9c4..27e81105a 100644
--- a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicDaprClientProxyExtensions.cs
+++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDaprClientProxyExtensions.cs
@@ -2,6 +2,7 @@
using Dapr.Client;
using JetBrains.Annotations;
using LINGYUN.Abp.Dapr.Client;
+using LINGYUN.Abp.Dapr.Client.ClientProxying;
using LINGYUN.Abp.Dapr.Client.DynamicProxying;
using Microsoft.Extensions.Options;
using System;
@@ -14,11 +15,37 @@ using Volo.Abp.Validation;
namespace Microsoft.Extensions.DependencyInjection
{
- public static class ServiceCollectionDynamicDaprClientProxyExtensions
+ public static class ServiceCollectionDaprClientProxyExtensions
{
private static readonly ProxyGenerator ProxyGeneratorInstance = new ProxyGenerator();
- #region Add DaprClient Proxies
+ #region Add Static DaprClient Proxies
+
+ public static IServiceCollection AddStaticDaprClientProxies(
+ [NotNull] this IServiceCollection services,
+ [NotNull] Assembly assembly,
+ [NotNull] string remoteServiceConfigurationName = RemoteServiceConfigurationDictionary.DefaultName)
+ {
+ Check.NotNull(services, nameof(assembly));
+
+ var serviceTypes = assembly.GetTypes().Where(IsSuitableForClientProxying).ToArray();
+
+ foreach (var serviceType in serviceTypes)
+ {
+ AddDaprClientFactory(services, remoteServiceConfigurationName);
+
+ services.Configure(options =>
+ {
+ options.DaprClientProxies[serviceType] = new DynamicDaprClientProxyConfig(serviceType, remoteServiceConfigurationName);
+ });
+ }
+
+ return services;
+ }
+
+ #endregion
+
+ #region Add Dynamic DaprClient Proxies
public static IServiceCollection AddDaprClientProxies(
[NotNull] this IServiceCollection services,
@@ -28,7 +55,7 @@ namespace Microsoft.Extensions.DependencyInjection
{
Check.NotNull(services, nameof(assembly));
- var serviceTypes = assembly.GetTypes().Where(IsSuitableForDynamicActorProxying).ToArray();
+ var serviceTypes = assembly.GetTypes().Where(IsSuitableForClientProxying).ToArray();
foreach (var serviceType in serviceTypes)
{
@@ -153,7 +180,7 @@ namespace Microsoft.Extensions.DependencyInjection
return services;
}
- private static bool IsSuitableForDynamicActorProxying(Type type)
+ private static bool IsSuitableForClientProxying(Type type)
{
//TODO: Add option to change type filter
diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/README.md b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/README.md
index 5b59dca79..23b9f7489 100644
--- a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/README.md
+++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/README.md
@@ -152,7 +152,7 @@ public class InvokeClass
## 配置项说明
-* AbpDaprRemoteServiceOptions.RemoteServices 配置Dapr.AppId
+* AbpRemoteServiceOptions.RemoteServices 配置Dapr.AppId
```json
diff --git a/aspnet-core/tests/LINGYUN.Abp.Dapr.AspNetCore.TestHost/LINGYUN/Abp/Dapr/ServiceInvocation/TestAppService.cs b/aspnet-core/tests/LINGYUN.Abp.Dapr.AspNetCore.TestHost/LINGYUN/Abp/Dapr/ServiceInvocation/TestAppService.cs
index 01373762e..16bcec150 100644
--- a/aspnet-core/tests/LINGYUN.Abp.Dapr.AspNetCore.TestHost/LINGYUN/Abp/Dapr/ServiceInvocation/TestAppService.cs
+++ b/aspnet-core/tests/LINGYUN.Abp.Dapr.AspNetCore.TestHost/LINGYUN/Abp/Dapr/ServiceInvocation/TestAppService.cs
@@ -12,7 +12,6 @@ namespace LINGYUN.Abp.Dapr.ServiceInvocation
[Route("api/dapr/test")]
public class TestAppService : AbpController, ITestAppService
{
- private static int _inctement;
private readonly List _cache = new List
{
new NameValue("name1", "value1"),
@@ -29,11 +28,9 @@ namespace LINGYUN.Abp.Dapr.ServiceInvocation
}
[HttpPut]
- public Task UpdateAsync()
+ public Task UpdateAsync(int inctement)
{
- Interlocked.Increment(ref _inctement);
-
- _cache[0].Value = $"value:updated:{_inctement}";
+ _cache[0].Value = $"value:updated:{inctement}";
return Task.FromResult(_cache[0]);
}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/TestClientProxy.Generated.cs b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/TestClientProxy.Generated.cs
new file mode 100644
index 000000000..1e9d70f80
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/TestClientProxy.Generated.cs
@@ -0,0 +1,45 @@
+// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Http.Client;
+using Volo.Abp.Http.Modeling;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Http.Client.ClientProxying;
+using LINGYUN.Abp.Dapr.ServiceInvocation;
+using LINGYUN.Abp.Dapr;
+using System.Linq;
+using Volo.Abp;
+using System.Collections.Generic;
+using Volo.Abp.Reflection;
+using System.Net.Http;
+using LINGYUN.Abp.Dapr.Client.ClientProxying;
+
+// ReSharper disable once CheckNamespace
+namespace LINGYUN.Abp.Dapr.ServiceInvocation.ClientProxies;
+
+[Dependency(ReplaceServices = true)]
+[ExposeServices(typeof(ITestAppService), typeof(TestClientProxy))]
+public partial class TestClientProxy : DaprClientProxyBase, ITestAppService
+{
+ public virtual async Task> GetAsync()
+ {
+ return await RequestAsync>(nameof(GetAsync));
+ }
+
+ public virtual async Task UpdateAsync(int inctement)
+ {
+ return await RequestAsync(nameof(UpdateAsync), new ClientProxyRequestTypeValue
+ {
+ { typeof(int), inctement }
+ });
+ }
+
+ public virtual async Task GetWrapedAsync(string name)
+ {
+ return await RequestAsync(nameof(GetWrapedAsync), new ClientProxyRequestTypeValue
+ {
+ { typeof(string), name }
+ });
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/TestClientProxy.cs b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/TestClientProxy.cs
new file mode 100644
index 000000000..8727dae3b
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/TestClientProxy.cs
@@ -0,0 +1,7 @@
+// This file is part of TestClientProxy, you can customize it here
+// ReSharper disable once CheckNamespace
+namespace LINGYUN.Abp.Dapr.ServiceInvocation.ClientProxies;
+
+public partial class TestClientProxy
+{
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/app-generate-proxy.json b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/app-generate-proxy.json
new file mode 100644
index 000000000..227944676
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/app-generate-proxy.json
@@ -0,0 +1,114 @@
+{
+ "modules": {
+ "app": {
+ "rootPath": "app",
+ "remoteServiceName": "TestDapr",
+ "controllers": {
+ "LINGYUN.Abp.Dapr.ServiceInvocation.TestAppService": {
+ "controllerName": "Test",
+ "controllerGroupName": "Test",
+ "isRemoteService": true,
+ "apiVersion": null,
+ "type": "LINGYUN.Abp.Dapr.ServiceInvocation.TestAppService",
+ "interfaces": [
+ {
+ "type": "LINGYUN.Abp.Dapr.ServiceInvocation.ITestAppService"
+ }
+ ],
+ "actions": {
+ "GetAsync": {
+ "uniqueName": "GetAsync",
+ "name": "GetAsync",
+ "httpMethod": "GET",
+ "url": "api/dapr/test",
+ "supportedVersions": [],
+ "parametersOnMethod": [],
+ "parameters": [],
+ "returnValue": {
+ "type": "Volo.Abp.Application.Dtos.ListResultDto",
+ "typeSimple": "Volo.Abp.Application.Dtos.ListResultDto"
+ },
+ "allowAnonymous": null,
+ "implementFrom": "LINGYUN.Abp.Dapr.ServiceInvocation.ITestAppService"
+ },
+ "UpdateAsyncByInctement": {
+ "uniqueName": "UpdateAsyncByInctement",
+ "name": "UpdateAsync",
+ "httpMethod": "PUT",
+ "url": "api/dapr/test",
+ "supportedVersions": [],
+ "parametersOnMethod": [
+ {
+ "name": "inctement",
+ "typeAsString": "System.Int32, System.Private.CoreLib",
+ "type": "System.Int32",
+ "typeSimple": "number",
+ "isOptional": false,
+ "defaultValue": null
+ }
+ ],
+ "parameters": [
+ {
+ "nameOnMethod": "inctement",
+ "name": "inctement",
+ "jsonName": null,
+ "type": "System.Int32",
+ "typeSimple": "number",
+ "isOptional": false,
+ "defaultValue": null,
+ "constraintTypes": null,
+ "bindingSourceId": "ModelBinding",
+ "descriptorName": ""
+ }
+ ],
+ "returnValue": {
+ "type": "LINGYUN.Abp.Dapr.NameValue",
+ "typeSimple": "LINGYUN.Abp.Dapr.NameValue"
+ },
+ "allowAnonymous": null,
+ "implementFrom": "LINGYUN.Abp.Dapr.ServiceInvocation.ITestAppService"
+ },
+ "GetWrapedAsyncByName": {
+ "uniqueName": "GetWrapedAsyncByName",
+ "name": "GetWrapedAsync",
+ "httpMethod": "GET",
+ "url": "api/dapr/test/{name}",
+ "supportedVersions": [],
+ "parametersOnMethod": [
+ {
+ "name": "name",
+ "typeAsString": "System.String, System.Private.CoreLib",
+ "type": "System.String",
+ "typeSimple": "string",
+ "isOptional": false,
+ "defaultValue": null
+ }
+ ],
+ "parameters": [
+ {
+ "nameOnMethod": "name",
+ "name": "name",
+ "jsonName": null,
+ "type": "System.String",
+ "typeSimple": "string",
+ "isOptional": false,
+ "defaultValue": null,
+ "constraintTypes": [],
+ "bindingSourceId": "Path",
+ "descriptorName": ""
+ }
+ ],
+ "returnValue": {
+ "type": "LINGYUN.Abp.Dapr.TestNeedWrapObject",
+ "typeSimple": "LINGYUN.Abp.Dapr.TestNeedWrapObject"
+ },
+ "allowAnonymous": null,
+ "implementFrom": "LINGYUN.Abp.Dapr.ServiceInvocation.ITestAppService"
+ }
+ }
+ }
+ }
+ }
+ },
+ "types": {}
+}
\ No newline at end of file
diff --git a/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN.Abp.Dapr.Client.Tests.csproj b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN.Abp.Dapr.Client.Tests.csproj
index 012ccc756..f137662a1 100644
--- a/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN.Abp.Dapr.Client.Tests.csproj
+++ b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN.Abp.Dapr.Client.Tests.csproj
@@ -1,4 +1,4 @@
-
+
net6.0
@@ -6,6 +6,14 @@
false
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/AbpDaptClientTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/AbpDaptClientTestModule.cs
index c14f7599d..ac39f5d84 100644
--- a/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/AbpDaptClientTestModule.cs
+++ b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/AbpDaptClientTestModule.cs
@@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using System.IO;
using LINGYUN.Abp.Dapr.Client.Wrapper;
+using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.Dapr.Client.Tests
{
@@ -23,13 +24,11 @@ namespace LINGYUN.Abp.Dapr.Client.Tests
EnvironmentName = "Testing",
BasePath = Directory.GetCurrentDirectory()
}));
- }
- public override void ConfigureServices(ServiceConfigurationContext context)
- {
- context.Services.AddDaprClientProxies(
- typeof(AbpDaprTestModule).Assembly,
- "TestDapr");
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
}
}
}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/TestAppServiceStaticProxyTests.cs b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/TestAppServiceStaticProxyTests.cs
new file mode 100644
index 000000000..cfe68d82f
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/TestAppServiceStaticProxyTests.cs
@@ -0,0 +1,53 @@
+using LINGYUN.Abp.Dapr.ServiceInvocation;
+using Microsoft.Extensions.DependencyInjection;
+using Shouldly;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace LINGYUN.Abp.Dapr.Client.Tests
+{
+ public class TestAppServiceStaticProxyTests : AbpDaptClientTestBase
+ {
+ private readonly ITestAppService _service;
+
+ public TestAppServiceStaticProxyTests()
+ {
+ _service = GetRequiredService();
+ }
+
+ protected override void BeforeAddApplication(IServiceCollection services)
+ {
+ services.AddStaticDaprClientProxies(
+ typeof(AbpDaprTestModule).Assembly,
+ "TestDapr");
+ }
+
+ [Fact]
+ public async Task Get_Result_Items_Count_Should_5()
+ {
+ var result = await _service.GetAsync();
+
+ result.Items.Count.ShouldBe(5);
+ }
+
+ [Fact]
+ public async Task Should_Get_Wraped_Object()
+ {
+ var result = await _service.GetWrapedAsync("Test");
+
+ result.Name.ShouldBe("Test");
+ }
+
+ [Fact]
+ public async Task Update_Result_Value_Should_Value_Updated_1()
+ {
+ var result = await _service.UpdateAsync(1);
+
+ result.Value.ShouldBe("value:updated:1");
+ }
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/TestAppServiceTests.cs b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/TestAppServiceTests.cs
index 40329a2f0..4e3980cbf 100644
--- a/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/TestAppServiceTests.cs
+++ b/aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/TestAppServiceTests.cs
@@ -1,4 +1,5 @@
using LINGYUN.Abp.Dapr.ServiceInvocation;
+using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using System.Threading.Tasks;
using Xunit;
@@ -14,6 +15,13 @@ namespace LINGYUN.Abp.Dapr.Client.Tests
_service = GetRequiredService();
}
+ protected override void BeforeAddApplication(IServiceCollection services)
+ {
+ services.AddDaprClientProxies(
+ typeof(AbpDaprTestModule).Assembly,
+ "TestDapr");
+ }
+
[Fact]
public async Task Get_Result_Items_Count_Should_5()
{
@@ -33,7 +41,7 @@ namespace LINGYUN.Abp.Dapr.Client.Tests
[Fact]
public async Task Update_Result_Value_Should_Value_Updated_1()
{
- var result = await _service.UpdateAsync();
+ var result = await _service.UpdateAsync(1);
result.Value.ShouldBe("value:updated:1");
}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Dapr.Tests/LINGYUN/Abp/Dapr/ServiceInvocation/ITestAppService.cs b/aspnet-core/tests/LINGYUN.Abp.Dapr.Tests/LINGYUN/Abp/Dapr/ServiceInvocation/ITestAppService.cs
index c543aa5b3..4c453c2e1 100644
--- a/aspnet-core/tests/LINGYUN.Abp.Dapr.Tests/LINGYUN/Abp/Dapr/ServiceInvocation/ITestAppService.cs
+++ b/aspnet-core/tests/LINGYUN.Abp.Dapr.Tests/LINGYUN/Abp/Dapr/ServiceInvocation/ITestAppService.cs
@@ -9,7 +9,7 @@ namespace LINGYUN.Abp.Dapr.ServiceInvocation
{
Task> GetAsync();
- Task UpdateAsync();
+ Task UpdateAsync(int inctement);
Task GetWrapedAsync(string name);
}
From 6a81c0922f0156c28f57be73c2fa91856c573db3 Mon Sep 17 00:00:00 2001
From: cKey <35512826+colinin@users.noreply.github.com>
Date: Mon, 5 Sep 2022 16:18:14 +0800
Subject: [PATCH 2/2] =?UTF-8?q?=E5=A2=9E=E5=BC=BAcli=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
1:labp
增加命令行**generate-proxy**,仅限于CSharp客户端代理,增加对于dapr客户端代理的支持,详情见**labp help generate-proxy**
---
.../LINGYUN/Abp/Cli/AbpCliModule.cs | 5 +-
.../LINGYUN/Abp/Cli/Commands/CreateCommand.cs | 4 +
.../Abp/Cli/Commands/GenerateProxyCommand.cs | 146 ++++++
.../LINGYUN/Abp/Cli/Commands/HelpCommand.cs | 4 +
.../CSharp/CSharpServiceProxyGenerator.cs | 422 ++++++++++++++++++
.../Cli/ServiceProxying/GenerateProxyArgs.cs | 26 ++
6 files changed, 605 insertions(+), 2 deletions(-)
create mode 100644 aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateProxyCommand.cs
create mode 100644 aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs
create mode 100644 aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/GenerateProxyArgs.cs
diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs
index 4439a5c66..bb286cacd 100644
--- a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs
+++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs
@@ -16,8 +16,9 @@ namespace LINGYUN.Abp.Cli
Configure(options =>
{
options.Commands.Clear();
- options.Commands["help"] = typeof(HelpCommand);
- options.Commands["create"] = typeof(CreateCommand);
+ options.Commands[HelpCommand.Name] = typeof(HelpCommand);
+ options.Commands[CreateCommand.Name] = typeof(CreateCommand);
+ options.Commands[GenerateProxyCommand.Name] = typeof(GenerateProxyCommand);
});
}
}
diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/CreateCommand.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/CreateCommand.cs
index fff109ddf..e948aa07e 100644
--- a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/CreateCommand.cs
+++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/CreateCommand.cs
@@ -19,6 +19,10 @@ namespace LINGYUN.Abp.Cli.Commands
{
public class CreateCommand : IConsoleCommand, ITransientDependency
{
+ public const string Name = "create";
+
+ protected string CommandName => Name;
+
public class FindFile
{
public string Path { get; }
diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateProxyCommand.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateProxyCommand.cs
new file mode 100644
index 000000000..c1f490609
--- /dev/null
+++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateProxyCommand.cs
@@ -0,0 +1,146 @@
+using LINGYUN.Abp.Cli.ServiceProxying;
+using LINGYUN.Abp.Cli.ServiceProxying.CSharp;
+using Microsoft.Extensions.DependencyInjection;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using Volo.Abp.Cli.Args;
+using Volo.Abp.Cli.Commands;
+using Volo.Abp.DependencyInjection;
+
+using VoloGenerateProxyArgs = Volo.Abp.Cli.ServiceProxying.GenerateProxyArgs;
+
+namespace LINGYUN.Abp.Cli.Commands;
+
+public class GenerateProxyCommand : IConsoleCommand, ITransientDependency
+{
+ public const string Name = "generate-proxy";
+
+ protected string CommandName => Name;
+
+ protected IServiceScopeFactory ServiceScopeFactory { get; }
+
+ public GenerateProxyCommand(
+ IServiceScopeFactory serviceScopeFactory)
+ {
+ ServiceScopeFactory = serviceScopeFactory;
+ }
+
+ public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
+ {
+ using (var scope = ServiceScopeFactory.CreateScope())
+ {
+ var serviceProxyGenerator = scope.ServiceProvider.GetRequiredService();
+
+ await serviceProxyGenerator.GenerateProxyAsync(BuildArgs(commandLineArgs));
+ }
+ }
+
+ private VoloGenerateProxyArgs BuildArgs(CommandLineArgs commandLineArgs)
+ {
+ var provider = commandLineArgs.Options.GetOrNull(Options.Provider.Short, Options.Provider.Long);
+ var url = commandLineArgs.Options.GetOrNull(Options.Url.Short, Options.Url.Long);
+ var target = commandLineArgs.Options.GetOrNull(Options.Target.Long);
+ var module = commandLineArgs.Options.GetOrNull(Options.Module.Short, Options.Module.Long) ?? "app";
+ var output = commandLineArgs.Options.GetOrNull(Options.Output.Short, Options.Output.Long);
+ var apiName = commandLineArgs.Options.GetOrNull(Options.ApiName.Short, Options.ApiName.Long);
+ var source = commandLineArgs.Options.GetOrNull(Options.Source.Short, Options.Source.Long);
+ var workDirectory = commandLineArgs.Options.GetOrNull(Options.WorkDirectory.Short, Options.WorkDirectory.Long) ?? Directory.GetCurrentDirectory();
+ var folder = commandLineArgs.Options.GetOrNull(Options.Folder.Long);
+
+ return new GenerateProxyArgs(CommandName, workDirectory, module, url, output, target, apiName, source, folder, provider, commandLineArgs.Options);
+ }
+
+ public string GetUsageInfo()
+ {
+ var sb = new StringBuilder();
+
+ sb.AppendLine("");
+ sb.AppendLine("Usage:");
+ sb.AppendLine("");
+ sb.AppendLine($" labp {CommandName}");
+ sb.AppendLine("");
+ sb.AppendLine("Options:");
+ sb.AppendLine("");
+ sb.AppendLine("-m|--module (default: 'app') The name of the backend module you wish to generate proxies for.");
+ sb.AppendLine("-wd|--working-directory Execution directory.");
+ sb.AppendLine("-u|--url API definition URL from.");
+ sb.AppendLine("-p|--provider The client proxy provider(http, dapr).");
+ sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI");
+
+ sb.AppendLine("");
+ sb.AppendLine("Examples:");
+ sb.AppendLine("");
+ sb.AppendLine(" labp generate-proxy");
+ sb.AppendLine(" labp generate-proxy -p dapr");
+ sb.AppendLine(" labp generate-proxy -m identity -o Pages/Identity/client-proxies.js -url https://localhost:44302/");
+ sb.AppendLine(" labp generate-proxy --folder MyProxies/InnerFolder -url https://localhost:44302/");
+
+ return sb.ToString();
+ }
+
+ public string GetShortDescription()
+ {
+ return "Generates client service proxies and DTOs to consume HTTP APIs.";
+ }
+
+ public static class Options
+ {
+ public static class Provider
+ {
+ public const string Short = "p";
+ public const string Long = "provider";
+ }
+
+ public static class Module
+ {
+ public const string Short = "m";
+ public const string Long = "module";
+ }
+
+ public static class ApiName
+ {
+ public const string Short = "a";
+ public const string Long = "api-name";
+ }
+
+ public static class Source
+ {
+ public const string Short = "s";
+ public const string Long = "source";
+ }
+ public static class Output
+ {
+ public const string Short = "o";
+ public const string Long = "output";
+ }
+
+ public static class Target
+ {
+ public const string Long = "target";
+ }
+
+ public static class Prompt
+ {
+ public const string Short = "p";
+ public const string Long = "prompt";
+ }
+
+ public static class Folder
+ {
+ public const string Long = "folder";
+ }
+
+ public static class Url
+ {
+ public const string Short = "u";
+ public const string Long = "url";
+ }
+
+ public static class WorkDirectory
+ {
+ public const string Short = "wd";
+ public const string Long = "working-directory";
+ }
+ }
+}
diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/HelpCommand.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/HelpCommand.cs
index 4bf6038c5..e9f956b05 100644
--- a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/HelpCommand.cs
+++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/HelpCommand.cs
@@ -14,6 +14,10 @@ namespace LINGYUN.Abp.Cli.Commands
{
public class HelpCommand : IConsoleCommand, ITransientDependency
{
+ public const string Name = "help";
+
+ protected string CommandName => Name;
+
public ILogger Logger { get; set; }
protected AbpCliOptions AbpCliOptions { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs
new file mode 100644
index 000000000..3c368c8eb
--- /dev/null
+++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs
@@ -0,0 +1,422 @@
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Volo.Abp.Cli;
+using Volo.Abp.Cli.Commands;
+using Volo.Abp.Cli.Http;
+using Volo.Abp.Cli.ServiceProxying;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Http.Modeling;
+using Volo.Abp.Json;
+
+using VoloGenerateProxyArgs = Volo.Abp.Cli.ServiceProxying.GenerateProxyArgs;
+
+namespace LINGYUN.Abp.Cli.ServiceProxying.CSharp;
+
+[Dependency(ReplaceServices = true)]
+[ExposeServices(typeof(IServiceProxyGenerator), typeof(CSharpServiceProxyGenerator))]
+public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase, ITransientDependency
+{
+ public const string Name = "CSHARP";
+
+ private const string UsingPlaceholder = "";
+ private const string MethodPlaceholder = "";
+ private const string ClassName = "";
+ private const string ServiceInterface = "";
+ private readonly static string[] ServicePostfixes = { "AppService", "ApplicationService" , "Service"};
+ private const string DefaultNamespace = "ClientProxies";
+ private const string Namespace = "";
+ private const string DefaultProvider = "ClientProxyBase";
+ private const string Provider = "";
+ private const string AppServicePrefix = "Volo.Abp.Application.Services";
+ private readonly string _clientProxyGeneratedTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" +
+ $"{Environment.NewLine}" +
+ $"{Environment.NewLine}" +
+ $"{Environment.NewLine}// ReSharper disable once CheckNamespace" +
+ $"{Environment.NewLine}namespace ;" +
+ $"{Environment.NewLine}" +
+ $"{Environment.NewLine}[Dependency(ReplaceServices = true)]" +
+ $"{Environment.NewLine}[ExposeServices(typeof(), typeof())]" +
+ $"{Environment.NewLine}public partial class : <>, " +
+ $"{Environment.NewLine}{{" +
+ $"{Environment.NewLine} " +
+ $"{Environment.NewLine}}}" +
+ $"{Environment.NewLine}";
+ private readonly string _clientProxyTemplate = "// This file is part of , you can customize it here" +
+ $"{Environment.NewLine}// ReSharper disable once CheckNamespace" +
+ $"{Environment.NewLine}namespace ;" +
+ $"{Environment.NewLine}" +
+ $"{Environment.NewLine}public partial class " +
+ $"{Environment.NewLine}{{" +
+ $"{Environment.NewLine}}}" +
+ $"{Environment.NewLine}";
+ private readonly List _usingNamespaceList = new()
+ {
+ "using System;",
+ "using System.Threading.Tasks;",
+ "using Volo.Abp.Application.Dtos;",
+ "using Volo.Abp.Http.Client;",
+ "using Volo.Abp.Http.Modeling;",
+ "using Volo.Abp.DependencyInjection;",
+ "using Volo.Abp.Http.Client.ClientProxying;",
+ "using LINGYUN.Abp.Dapr;",
+ "using LINGYUN.Abp.Dapr.Client;",
+ "using LINGYUN.Abp.Dapr.Client.ClientProxying;"
+ };
+
+ public CSharpServiceProxyGenerator(
+ CliHttpClientFactory cliHttpClientFactory,
+ IJsonSerializer jsonSerializer) :
+ base(cliHttpClientFactory, jsonSerializer)
+ {
+ }
+
+ public async override Task GenerateProxyAsync(VoloGenerateProxyArgs args)
+ {
+ CheckWorkDirectory(args.WorkDirectory);
+ CheckFolder(args.Folder);
+
+ if (args.CommandName == RemoveProxyCommand.Name)
+ {
+ RemoveClientProxyFile(args);
+ return;
+ }
+
+ var applicationApiDescriptionModel = await GetApplicationApiDescriptionModelAsync(args);
+
+ foreach (var controller in applicationApiDescriptionModel.Modules.Values.SelectMany(x => x.Controllers))
+ {
+ if (ShouldGenerateProxy(controller.Value))
+ {
+ await GenerateClientProxyFileAsync(args, controller.Value);
+ }
+ }
+
+ await CreateGenerateProxyJsonFile(args, applicationApiDescriptionModel);
+ }
+
+ private async Task CreateGenerateProxyJsonFile(VoloGenerateProxyArgs args, ApplicationApiDescriptionModel applicationApiDescriptionModel)
+ {
+ var folder = args.Folder.IsNullOrWhiteSpace() ? DefaultNamespace : args.Folder;
+ var filePath = Path.Combine(args.WorkDirectory, folder, $"{args.Module}-generate-proxy.json");
+
+ using (var writer = new StreamWriter(filePath))
+ {
+ await writer.WriteAsync(JsonSerializer.Serialize(applicationApiDescriptionModel, indented: true));
+ }
+ }
+
+ private void RemoveClientProxyFile(VoloGenerateProxyArgs args)
+ {
+ var folder = args.Folder.IsNullOrWhiteSpace() ? DefaultNamespace : args.Folder;
+ var folderPath = Path.Combine(args.WorkDirectory, folder);
+
+ if (Directory.Exists(folderPath))
+ {
+ Directory.Delete(folderPath, true);
+ }
+
+ Logger.LogInformation($"Delete {GetLoggerOutputPath(folderPath, args.WorkDirectory)}");
+ }
+
+ private async Task GenerateClientProxyFileAsync(
+ VoloGenerateProxyArgs args,
+ ControllerApiDescriptionModel controllerApiDescription)
+ {
+ var folder = args.Folder.IsNullOrWhiteSpace() ? DefaultNamespace : args.Folder;
+
+ var appServiceTypeFullName = controllerApiDescription.Interfaces.Last().Type;
+ var appServiceTypeName = appServiceTypeFullName.Split('.').Last();
+ var clientProxyName = $"{controllerApiDescription.ControllerName}ClientProxy";
+ var clientProvider = args.As().Provider;
+ var rootNamespace = $"{GetTypeNamespace(controllerApiDescription.Type)}.{folder.Replace('/', '.')}";
+ var clientProxyBuilder = new StringBuilder(_clientProxyTemplate);
+ clientProxyBuilder.Replace(ClassName, clientProxyName);
+ clientProxyBuilder.Replace(Namespace, rootNamespace);
+ clientProxyBuilder.Replace(Provider, clientProvider);
+
+ var filePath = Path.Combine(args.WorkDirectory, folder, $"{clientProxyName}.cs");
+ Directory.CreateDirectory(Path.GetDirectoryName(filePath));
+
+ if (!File.Exists(filePath))
+ {
+ using (var writer = new StreamWriter(filePath))
+ {
+ await writer.WriteAsync(clientProxyBuilder.ToString());
+ }
+
+ Logger.LogInformation($"Create {GetLoggerOutputPath(filePath, args.WorkDirectory)}");
+ }
+
+ await GenerateClientProxyGeneratedFileAsync(
+ args,
+ controllerApiDescription,
+ clientProxyName,
+ appServiceTypeName,
+ appServiceTypeFullName,
+ rootNamespace,
+ folder);
+ }
+
+ private async Task GenerateClientProxyGeneratedFileAsync(
+ VoloGenerateProxyArgs args,
+ ControllerApiDescriptionModel controllerApiDescription,
+ string clientProxyName,
+ string appServiceTypeName,
+ string appServiceTypeFullName,
+ string rootNamespace,
+ string folder)
+ {
+ var clientProxyBuilder = new StringBuilder(_clientProxyGeneratedTemplate);
+
+ var usingNamespaceList = new List(_usingNamespaceList)
+ {
+ $"using {GetTypeNamespace(appServiceTypeFullName)};"
+ };
+
+ clientProxyBuilder.Replace(ClassName, clientProxyName);
+ clientProxyBuilder.Replace(Namespace, rootNamespace);
+ clientProxyBuilder.Replace(ServiceInterface, appServiceTypeName);
+
+ foreach (var action in controllerApiDescription.Actions.Values)
+ {
+ if (!ShouldGenerateMethod(appServiceTypeFullName, action))
+ {
+ continue;
+ }
+
+ GenerateMethod(action, clientProxyBuilder, usingNamespaceList);
+ }
+
+ foreach (var usingNamespace in usingNamespaceList)
+ {
+ clientProxyBuilder.Replace($"{UsingPlaceholder}", $"{usingNamespace}{Environment.NewLine}{UsingPlaceholder}");
+ }
+
+ clientProxyBuilder.Replace($"{Environment.NewLine}{UsingPlaceholder}", string.Empty);
+ clientProxyBuilder.Replace($"{Environment.NewLine}{Environment.NewLine} {MethodPlaceholder}", string.Empty);
+
+ var filePath = Path.Combine(args.WorkDirectory, folder, $"{clientProxyName}.Generated.cs");
+
+ using (var writer = new StreamWriter(filePath))
+ {
+ await writer.WriteAsync(clientProxyBuilder.ToString());
+ Logger.LogInformation($"Create {GetLoggerOutputPath(filePath, args.WorkDirectory)}");
+ }
+ }
+
+ private void GenerateMethod(
+ ActionApiDescriptionModel action,
+ StringBuilder clientProxyBuilder,
+ List usingNamespaceList)
+ {
+ var methodBuilder = new StringBuilder();
+
+ var returnTypeName = GetRealTypeName(action.ReturnValue.Type, usingNamespaceList);
+
+ if (!action.Name.EndsWith("Async"))
+ {
+ GenerateSynchronizationMethod(action, returnTypeName, methodBuilder, usingNamespaceList);
+ clientProxyBuilder.Replace(MethodPlaceholder, $"{methodBuilder}{Environment.NewLine} {MethodPlaceholder}");
+ return;
+ }
+
+ GenerateAsynchronousMethod(action, returnTypeName, methodBuilder, usingNamespaceList);
+ clientProxyBuilder.Replace(MethodPlaceholder, $"{methodBuilder}{Environment.NewLine} {MethodPlaceholder}");
+ }
+
+ private void GenerateSynchronizationMethod(ActionApiDescriptionModel action, string returnTypeName, StringBuilder methodBuilder, List usingNamespaceList)
+ {
+ methodBuilder.AppendLine($"public virtual {returnTypeName} {action.Name}()");
+
+ foreach (var parameter in action.Parameters.GroupBy(x => x.Name).Select(x => x.First()))
+ {
+ methodBuilder.Replace("", $"{GetRealTypeName(parameter.Type, usingNamespaceList)} {parameter.Name}, ");
+ }
+
+ methodBuilder.Replace("", string.Empty);
+ methodBuilder.Replace(", )", ")");
+
+ methodBuilder.AppendLine(" {");
+ methodBuilder.AppendLine(" //Client Proxy does not support the synchronization method, you should always use asynchronous methods as a best practice");
+ methodBuilder.AppendLine(" throw new System.NotImplementedException(); ");
+ methodBuilder.AppendLine(" }");
+ }
+
+ private void GenerateAsynchronousMethod(
+ ActionApiDescriptionModel action,
+ string returnTypeName,
+ StringBuilder methodBuilder,
+ List usingNamespaceList)
+ {
+ var returnSign = returnTypeName == "void" ? "Task" : $"Task<{returnTypeName}>";
+
+ methodBuilder.AppendLine($"public virtual async {returnSign} {action.Name}()");
+
+ foreach (var parameter in action.ParametersOnMethod)
+ {
+ methodBuilder.Replace("", $"{GetRealTypeName(parameter.Type, usingNamespaceList)} {parameter.Name}, ");
+ }
+
+ methodBuilder.Replace("", string.Empty);
+ methodBuilder.Replace(", )", ")");
+
+ methodBuilder.AppendLine(" {");
+
+ var argsTemplate = "new ClientProxyRequestTypeValue" +
+ $"{Environment.NewLine} {{" +
+ $"{Environment.NewLine} }}";
+
+ var args = action.ParametersOnMethod.Any() ? argsTemplate : string.Empty;
+
+ if (returnTypeName == "void")
+ {
+ methodBuilder.AppendLine($" await RequestAsync(nameof({action.Name}), {args});");
+ }
+ else
+ {
+ methodBuilder.AppendLine($" return await RequestAsync<{returnTypeName}>(nameof({action.Name}), {args});");
+ }
+
+ foreach (var parameter in action.ParametersOnMethod)
+ {
+ methodBuilder.Replace("", $"{Environment.NewLine} {{ typeof({GetRealTypeName(parameter.Type)}), {parameter.Name} }},");
+ }
+
+ methodBuilder.Replace(",", string.Empty);
+ methodBuilder.Replace(", )", ")");
+ methodBuilder.AppendLine(" }");
+ }
+
+ private bool ShouldGenerateProxy(ControllerApiDescriptionModel controllerApiDescription)
+ {
+ if (!controllerApiDescription.Interfaces.Any())
+ {
+ return false;
+ }
+
+ var serviceInterface = controllerApiDescription.Interfaces.Last();
+ return ServicePostfixes.Any(x => serviceInterface.Type.EndsWith(x));
+ }
+
+ private bool ShouldGenerateMethod(string appServiceTypeName, ActionApiDescriptionModel action)
+ {
+ return action.ImplementFrom.StartsWith(AppServicePrefix) || action.ImplementFrom.StartsWith(appServiceTypeName);
+ }
+
+ private string GetTypeNamespace(string typeFullName)
+ {
+ return typeFullName.Substring(0, typeFullName.LastIndexOf('.'));
+ }
+
+ private string GetRealTypeName(string typeName, List usingNamespaceList = null)
+ {
+ var filter = new[] { "<", ",", ">" };
+ var stringBuilder = new StringBuilder();
+ var typeNames = typeName.Split('.');
+
+ if (typeNames.All(x => !filter.Any(x.Contains)))
+ {
+ if (usingNamespaceList != null)
+ {
+ AddUsingNamespace(usingNamespaceList, typeName);
+ }
+
+ return NormalizeTypeName(typeNames.Last());
+ }
+
+ var fullName = string.Empty;
+
+ foreach (var item in typeNames)
+ {
+ if (filter.Any(x => item.Contains(x)))
+ {
+ if (usingNamespaceList != null)
+ {
+ AddUsingNamespace(usingNamespaceList, $"{fullName}.{item}".TrimStart('.'));
+ }
+
+ fullName = string.Empty;
+
+ if (item.Contains('<') || item.Contains(','))
+ {
+ stringBuilder.Append(item.Substring(0, item.IndexOf(item.Contains('<') ? '<' : ',') + 1));
+ fullName = item.Substring(item.IndexOf(item.Contains('<') ? '<' : ',') + 1);
+ }
+ else
+ {
+ stringBuilder.Append(item);
+ }
+ }
+ else
+ {
+ fullName = $"{fullName}.{item}";
+ }
+ }
+
+ return stringBuilder.ToString();
+ }
+
+ private void AddUsingNamespace(List usingNamespaceList, string typeName)
+ {
+ var rootNamespace = $"using {GetTypeNamespace(typeName)};";
+ if (usingNamespaceList.Contains(rootNamespace))
+ {
+ return;
+ }
+
+ usingNamespaceList.Add(rootNamespace);
+ }
+
+ private string NormalizeTypeName(string typeName)
+ {
+ var nullable = string.Empty;
+ if (typeName.EndsWith("?"))
+ {
+ typeName = typeName.TrimEnd('?');
+ nullable = "?";
+ }
+
+ typeName = typeName switch
+ {
+ "Void" => "void",
+ "Boolean" => "bool",
+ "String" => "string",
+ "Int32" => "int",
+ "Int64" => "long",
+ "Double" => "double",
+ "Object" => "object",
+ "Byte" => "byte",
+ "Char" => "char",
+ _ => typeName
+ };
+
+ return $"{typeName}{nullable}";
+ }
+
+ private void CheckWorkDirectory(string directory)
+ {
+ if (!Directory.Exists(directory))
+ {
+ throw new CliUsageException("Specified directory does not exist.");
+ }
+
+ var projectFiles = Directory.GetFiles(directory, "*.csproj");
+ if (!projectFiles.Any())
+ {
+ throw new CliUsageException("No project file(csproj) found in the directory.");
+ }
+ }
+
+ private void CheckFolder(string folder)
+ {
+ if (!folder.IsNullOrWhiteSpace() && Path.HasExtension(folder))
+ {
+ throw new CliUsageException("Option folder should be a directory.");
+ }
+ }
+}
diff --git a/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/GenerateProxyArgs.cs b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/GenerateProxyArgs.cs
new file mode 100644
index 000000000..8be4f1660
--- /dev/null
+++ b/aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/GenerateProxyArgs.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+namespace LINGYUN.Abp.Cli.ServiceProxying;
+
+public class GenerateProxyArgs : Volo.Abp.Cli.ServiceProxying.GenerateProxyArgs
+{
+ public string Provider { get; }
+
+ public GenerateProxyArgs(
+ [NotNull] string commandName,
+ [NotNull] string workDirectory,
+ string module,
+ string url,
+ string output,
+ string target,
+ string apiName,
+ string source,
+ string folder,
+ string provider,
+ Dictionary extraProperties = null)
+ : base(commandName, workDirectory, module, url, output, target, apiName, source, folder, extraProperties)
+ {
+ Provider = provider;
+ }
+}