Browse Source

增强客户端代理功能

1:增加
**LINGYUN.Abp.Http.Client.Wrapper**模块,依赖此模块自动识别远程客户端代理时是否使用包装器;

2:**LINGYUN.Abp.Dapr.Client**增加接口方法**AddStaticDaprClientProxies**,可以通过***.generate-proxy.json**配置文件调用远程接口;
pull/684/head
cKey 3 years ago
parent
commit
e0c37f8b0c
  1. 14
      aspnet-core/LINGYUN.MicroService.All.sln
  2. 0
      aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/FodyWeavers.xml
  3. 0
      aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/FodyWeavers.xsd
  4. 8
      aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/LINGYUN.Abp.Http.Client.Wrapper.csproj
  5. 33
      aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/LINGYUN/Abp/Http/Client/Wrapper/AbpHttpClientWrapperModule.cs
  6. 13
      aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/AbpHttpClientWrapperModule.cs
  7. 153
      aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/DynamicHttpProxyInterceptorWrapClientProxy.cs
  8. 17
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client.Wrapper/LINGYUN/Abp/Dapr/Client/Wrapper/AbpDaprClientWrapperModule.cs
  9. 5
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/ClientProxying/AbpDaprClientProxyOptions.cs
  10. 124
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/ClientProxying/DaprClientProxyBase.cs
  11. 3
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/DynamicDaprClientProxyInterceptor.cs
  12. 122
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/DynamicDaprProxyInterceptorClientProxy.cs
  13. 35
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDaprClientProxyExtensions.cs
  14. 2
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/README.md
  15. 7
      aspnet-core/tests/LINGYUN.Abp.Dapr.AspNetCore.TestHost/LINGYUN/Abp/Dapr/ServiceInvocation/TestAppService.cs
  16. 45
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/TestClientProxy.Generated.cs
  17. 7
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/TestClientProxy.cs
  18. 114
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/app-generate-proxy.json
  19. 10
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN.Abp.Dapr.Client.Tests.csproj
  20. 11
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/AbpDaptClientTestModule.cs
  21. 53
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/TestAppServiceStaticProxyTests.cs
  22. 10
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/TestAppServiceTests.cs
  23. 2
      aspnet-core/tests/LINGYUN.Abp.Dapr.Tests/LINGYUN/Abp/Dapr/ServiceInvocation/ITestAppService.cs

14
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}

0
aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/FodyWeavers.xml → aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/FodyWeavers.xml

0
aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/FodyWeavers.xsd → aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/FodyWeavers.xsd

8
aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN.Abp.HttpClient.Wrapper.csproj → aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/LINGYUN.Abp.Http.Client.Wrapper.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
@ -12,8 +12,8 @@
<PackageReference Include="Volo.Abp.Http.Client" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.Wrapper\LINGYUN.Abp.Wrapper.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.Wrapper\LINGYUN.Abp.Wrapper.csproj" />
</ItemGroup>
</Project>

33
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<AbpHttpClientBuilderOptions>(options =>
{
options.ProxyClientBuildActions.Add(
(_, builder) =>
{
builder.ConfigureHttpClient((provider, client) =>
{
var wrapperOptions = provider.GetRequiredService<IOptions<AbpWrapperOptions>>();
var wrapperHeader = wrapperOptions.Value.IsEnabled
? AbpHttpWrapConsts.AbpWrapResult
: AbpHttpWrapConsts.AbpDontWrapResult;
client.DefaultRequestHeaders.TryAddWithoutValidation(wrapperHeader, "true");
});
});
});
}
}

13
aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/AbpHttpClientWrapperModule.cs

@ -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
{
}
}

153
aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/DynamicHttpProxyInterceptorWrapClientProxy.cs

@ -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<TService>
: DynamicHttpProxyInterceptorClientProxy<TService>, ITransientDependency
{
protected IOptions<AbpWrapperOptions> WrapperOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpWrapperOptions>>();
protected override async Task<T> RequestAsync<T>(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<WrapResult<T>>(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<T>(stringContent);
}
public override async Task<HttpContent> CallRequestAsync(ClientProxyRequestContext requestContext)
{
var response = await RequestAndGetResponseAsync(requestContext);
// 对于包装后的结果需要处理
if (response.Headers.Contains(AbpHttpWrapConsts.AbpWrapResult))
{
var stringContent = await response.Content.ReadAsStringAsync();
var wrapResult = JsonSerializer.Deserialize<WrapResult>(stringContent);
ThrowExceptionForResponse(wrapResult);
}
return response.Content;
}
protected virtual void ThrowExceptionForResponse<T>(WrapResult<T> 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<HttpResponseMessage> 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;
}
}
}

17
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<AbpWrapperOptions>();
Configure<AbpDaprClientProxyOptions>(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();

5
aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/AbpDaprClientProxyOptions.cs → 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
{

124
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<TService> : ClientProxyBase<TService>
{
protected IOptions<AbpDaprClientProxyOptions> DaprClientProxyOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpDaprClientProxyOptions>>();
protected IDaprClientFactory DaprClientFactory => LazyServiceProvider.LazyGetRequiredService<IDaprClientFactory>();
protected async override Task<T> RequestAsync<T>(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<T>(stringContent);
}
protected async override Task<string> 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<HttpResponseMessage> 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;
}
}
}

3
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;

122
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<TService> : ClientProxyBase<TService>
public class DynamicDaprProxyInterceptorClientProxy<TService> : DaprClientProxyBase<TService>
{
protected IOptions<AbpDaprClientProxyOptions> DaprClientProxyOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpDaprClientProxyOptions>>();
protected IDaprClientFactory DaprClientFactory => LazyServiceProvider.LazyGetRequiredService<IDaprClientFactory>();
public virtual async Task<T> CallRequestAsync<T>(ClientProxyRequestContext requestContext)
public async virtual Task<T> CallRequestAsync<T>(ClientProxyRequestContext requestContext)
{
return await RequestAsync<T>(requestContext);
}
public virtual async Task<HttpContent> CallRequestAsync(ClientProxyRequestContext requestContext)
public async virtual Task<HttpContent> CallRequestAsync(ClientProxyRequestContext requestContext)
{
return await RequestAsync(requestContext);
}
protected override async Task<T> RequestAsync<T>(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<T>(stringContent);
}
protected override async Task<string> 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<HttpResponseMessage> 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;
}
}
}

35
aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicDaprClientProxyExtensions.cs → 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<AbpDaprClientProxyOptions>(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

2
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

7
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<NameValue> _cache = new List<NameValue>
{
new NameValue("name1", "value1"),
@ -29,11 +28,9 @@ namespace LINGYUN.Abp.Dapr.ServiceInvocation
}
[HttpPut]
public Task<NameValue> UpdateAsync()
public Task<NameValue> UpdateAsync(int inctement)
{
Interlocked.Increment(ref _inctement);
_cache[0].Value = $"value:updated:{_inctement}";
_cache[0].Value = $"value:updated:{inctement}";
return Task.FromResult(_cache[0]);
}

45
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>, ITestAppService
{
public virtual async Task<ListResultDto<NameValue>> GetAsync()
{
return await RequestAsync<ListResultDto<NameValue>>(nameof(GetAsync));
}
public virtual async Task<NameValue> UpdateAsync(int inctement)
{
return await RequestAsync<NameValue>(nameof(UpdateAsync), new ClientProxyRequestTypeValue
{
{ typeof(int), inctement }
});
}
public virtual async Task<TestNeedWrapObject> GetWrapedAsync(string name)
{
return await RequestAsync<TestNeedWrapObject>(nameof(GetWrapedAsync), new ClientProxyRequestTypeValue
{
{ typeof(string), name }
});
}
}

7
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
{
}

114
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<LINGYUN.Abp.Dapr.NameValue>",
"typeSimple": "Volo.Abp.Application.Dtos.ListResultDto<LINGYUN.Abp.Dapr.NameValue>"
},
"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": {}
}

10
aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN.Abp.Dapr.Client.Tests.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
@ -6,6 +6,14 @@
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<None Remove="ClientProxies\app-generate-proxy.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ClientProxies\app-generate-proxy.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="xunit" Version="2.4.1" />

11
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<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpDaptClientTestModule>();
});
}
}
}

53
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<ITestAppService>();
}
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");
}
}
}

10
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<ITestAppService>();
}
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");
}

2
aspnet-core/tests/LINGYUN.Abp.Dapr.Tests/LINGYUN/Abp/Dapr/ServiceInvocation/ITestAppService.cs

@ -9,7 +9,7 @@ namespace LINGYUN.Abp.Dapr.ServiceInvocation
{
Task<ListResultDto<NameValue>> GetAsync();
Task<NameValue> UpdateAsync();
Task<NameValue> UpdateAsync(int inctement);
Task<TestNeedWrapObject> GetWrapedAsync(string name);
}

Loading…
Cancel
Save