Browse Source

Merge pull request #684 from colinin/Enhance-Client-Proxy

Enhance client proxy
pull/712/head
yx lin 3 years ago
committed by GitHub
parent
commit
80e16589c9
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      aspnet-core/LINGYUN.MicroService.All.sln
  2. 5
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs
  3. 4
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/CreateCommand.cs
  4. 146
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateProxyCommand.cs
  5. 4
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/HelpCommand.cs
  6. 422
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs
  7. 26
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/GenerateProxyArgs.cs
  8. 0
      aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/FodyWeavers.xml
  9. 0
      aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/FodyWeavers.xsd
  10. 8
      aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/LINGYUN.Abp.Http.Client.Wrapper.csproj
  11. 33
      aspnet-core/modules/common/LINGYUN.Abp.Http.Client.Wrapper/LINGYUN/Abp/Http/Client/Wrapper/AbpHttpClientWrapperModule.cs
  12. 13
      aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/AbpHttpClientWrapperModule.cs
  13. 153
      aspnet-core/modules/common/LINGYUN.Abp.HttpClient.Wrapper/LINGYUN/Abp/HttpClient/Wrapper/DynamicHttpProxyInterceptorWrapClientProxy.cs
  14. 17
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client.Wrapper/LINGYUN/Abp/Dapr/Client/Wrapper/AbpDaprClientWrapperModule.cs
  15. 5
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/ClientProxying/AbpDaprClientProxyOptions.cs
  16. 124
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/ClientProxying/DaprClientProxyBase.cs
  17. 3
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/DynamicDaprClientProxyInterceptor.cs
  18. 122
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/LINGYUN/Abp/Dapr/Client/DynamicProxying/DynamicDaprProxyInterceptorClientProxy.cs
  19. 35
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDaprClientProxyExtensions.cs
  20. 2
      aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Client/README.md
  21. 7
      aspnet-core/tests/LINGYUN.Abp.Dapr.AspNetCore.TestHost/LINGYUN/Abp/Dapr/ServiceInvocation/TestAppService.cs
  22. 45
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/TestClientProxy.Generated.cs
  23. 7
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/TestClientProxy.cs
  24. 114
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/ClientProxies/app-generate-proxy.json
  25. 10
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN.Abp.Dapr.Client.Tests.csproj
  26. 11
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/AbpDaptClientTestModule.cs
  27. 53
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/TestAppServiceStaticProxyTests.cs
  28. 10
      aspnet-core/tests/LINGYUN.Abp.Dapr.Client.Tests/LINGYUN/Abp/Dapr/Client/Tests/TestAppServiceTests.cs
  29. 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}

5
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs

@ -16,8 +16,9 @@ namespace LINGYUN.Abp.Cli
Configure<AbpCliOptions>(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);
});
}
}

4
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; }

146
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<CSharpServiceProxyGenerator>();
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 <module-name> (default: 'app') The name of the backend module you wish to generate proxies for.");
sb.AppendLine("-wd|--working-directory <directory-path> Execution directory.");
sb.AppendLine("-u|--url <url> API definition URL from.");
sb.AppendLine("-p|--provider <client-proxy-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";
}
}
}

4
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<HelpCommand> Logger { get; set; }
protected AbpCliOptions AbpCliOptions { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }

422
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<CSharpServiceProxyGenerator>, ITransientDependency
{
public const string Name = "CSHARP";
private const string UsingPlaceholder = "<using placeholder>";
private const string MethodPlaceholder = "<method placeholder>";
private const string ClassName = "<className>";
private const string ServiceInterface = "<serviceInterface>";
private readonly static string[] ServicePostfixes = { "AppService", "ApplicationService" , "Service"};
private const string DefaultNamespace = "ClientProxies";
private const string Namespace = "<namespace>";
private const string DefaultProvider = "ClientProxyBase";
private const string Provider = "<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}<using placeholder>" +
$"{Environment.NewLine}" +
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" +
$"{Environment.NewLine}namespace <namespace>;" +
$"{Environment.NewLine}" +
$"{Environment.NewLine}[Dependency(ReplaceServices = true)]" +
$"{Environment.NewLine}[ExposeServices(typeof(<serviceInterface>), typeof(<className>))]" +
$"{Environment.NewLine}public partial class <className> : <provider><<serviceInterface>>, <serviceInterface>" +
$"{Environment.NewLine}{{" +
$"{Environment.NewLine} <method placeholder>" +
$"{Environment.NewLine}}}" +
$"{Environment.NewLine}";
private readonly string _clientProxyTemplate = "// This file is part of <className>, you can customize it here" +
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" +
$"{Environment.NewLine}namespace <namespace>;" +
$"{Environment.NewLine}" +
$"{Environment.NewLine}public partial class <className>" +
$"{Environment.NewLine}{{" +
$"{Environment.NewLine}}}" +
$"{Environment.NewLine}";
private readonly List<string> _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<GenerateProxyArgs>().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<string>(_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<string> 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<string> usingNamespaceList)
{
methodBuilder.AppendLine($"public virtual {returnTypeName} {action.Name}(<args>)");
foreach (var parameter in action.Parameters.GroupBy(x => x.Name).Select(x => x.First()))
{
methodBuilder.Replace("<args>", $"{GetRealTypeName(parameter.Type, usingNamespaceList)} {parameter.Name}, <args>");
}
methodBuilder.Replace("<args>", 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<string> usingNamespaceList)
{
var returnSign = returnTypeName == "void" ? "Task" : $"Task<{returnTypeName}>";
methodBuilder.AppendLine($"public virtual async {returnSign} {action.Name}(<args>)");
foreach (var parameter in action.ParametersOnMethod)
{
methodBuilder.Replace("<args>", $"{GetRealTypeName(parameter.Type, usingNamespaceList)} {parameter.Name}, <args>");
}
methodBuilder.Replace("<args>", string.Empty);
methodBuilder.Replace(", )", ")");
methodBuilder.AppendLine(" {");
var argsTemplate = "new ClientProxyRequestTypeValue" +
$"{Environment.NewLine} {{<args>" +
$"{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("<args>", $"{Environment.NewLine} {{ typeof({GetRealTypeName(parameter.Type)}), {parameter.Name} }},<args>");
}
methodBuilder.Replace(",<args>", 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<string> 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<string> 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.");
}
}
}

26
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<string, string> extraProperties = null)
: base(commandName, workDirectory, module, url, output, target, apiName, source, folder, extraProperties)
{
Provider = provider;
}
}

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