Browse Source

Add DeclaringFrom to ActionApiDescriptionModel

pull/9905/head
liangshiwei 5 years ago
parent
commit
8c7c3404db
  1. 11
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs
  2. 22
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/Angular/AngularServiceProxyGenerator.cs
  3. 184
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/CSharp/CSharpServiceProxyGenerator.cs
  4. 6
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/JavaScript/JavaScriptServiceProxyGenerator.cs
  5. 7
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/ServiceProxyGeneratorBase.cs
  6. 128
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxyBase.cs
  7. 105
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyApiDescriptionFinder.cs
  8. 47
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs
  9. 12
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IClientProxyApiDescriptionFinder.cs
  10. 7
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs
  11. 1
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj

11
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs

@ -113,6 +113,14 @@ namespace Volo.Abp.AspNetCore.Mvc
allowAnonymous = false;
}
var declaringFrom = controllerType.FullName;
var interfaces = controllerType.GetInterfaces().ToList();
foreach (var interfaceType in interfaces.Where(interfaceType => interfaceType.GetMethods().Any(x => x.Name == method.Name)))
{
declaringFrom = TypeHelper.GetFullNameHandlingNullableAndGenerics(interfaceType);
break;
}
var actionModel = controllerModel.AddAction(
uniqueMethodName,
ActionApiDescriptionModel.Create(
@ -121,7 +129,8 @@ namespace Volo.Abp.AspNetCore.Mvc
apiDescription.RelativePath,
apiDescription.HttpMethod,
GetSupportedVersions(controllerType, method, setting),
allowAnonymous
allowAnonymous,
declaringFrom
)
);

22
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/Angular/AngularServiceProxyGenerator.cs

@ -6,25 +6,29 @@ using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json.Linq;
using NuGet.Versioning;
using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace Volo.Abp.Cli.ServiceProxy.Angular
{
public class AngularServiceProxyGenerator : IServiceProxyGenerator , ITransientDependency
public class AngularServiceProxyGenerator : ServiceProxyGeneratorBase<AngularServiceProxyGenerator> , ITransientDependency
{
public const string Name = "NG";
public CliService CliService { get; }
public ILogger<AngularServiceProxyGenerator> Logger { get; set; }
private readonly CliService _cliService;
public AngularServiceProxyGenerator(CliService cliService)
public AngularServiceProxyGenerator(
CliHttpClientFactory cliHttpClientFactory,
IJsonSerializer jsonSerializer,
CliService cliService) :
base(cliHttpClientFactory, jsonSerializer)
{
CliService = cliService;
Logger = NullLogger<AngularServiceProxyGenerator>.Instance;
_cliService = cliService;
}
public async Task GenerateProxyAsync(GenerateProxyArgs args)
public override async Task GenerateProxyAsync(GenerateProxyArgs args)
{
CheckAngularJsonFile();
await CheckNgSchematicsAsync();
@ -91,14 +95,14 @@ namespace Volo.Abp.Cli.ServiceProxy.Angular
return;
}
var cliVersion = await CliService.GetCurrentCliVersionAsync(typeof(CliService).Assembly);
var cliVersion = await _cliService.GetCurrentCliVersionAsync(typeof(CliService).Assembly);
if (semanticSchematicsVersion < cliVersion)
{
Logger.LogWarning("\"@abp/ng.schematics\" version is lower than ABP Cli version.");
}
}
private void CheckAngularJsonFile()
private static void CheckAngularJsonFile()
{
var angularPath = $"angular.json";
if (!File.Exists(angularPath))

184
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/CSharp/CSharpServiceProxyGenerator.cs

@ -7,7 +7,6 @@ using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Http;
using Volo.Abp.DependencyInjection;
@ -17,7 +16,7 @@ using Volo.Abp.Modularity;
namespace Volo.Abp.Cli.ServiceProxy.CSharp
{
public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase, ITransientDependency
public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServiceProxyGenerator>, ITransientDependency
{
public const string Name = "CSHARP";
@ -25,15 +24,18 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
private const string MethodPlaceholder = "<method placeholder>";
private const string ClassName = "<className>";
private const string ServiceInterface = "<serviceInterface>";
private const string ServicePostfix = "APPSERVICE";
private const string ServicePostfix = "AppService";
private const string DefaultNamespace = "ClientProxies";
private const string Namespace = "<namespace>";
private const string AppServicePrefix = "Volo.Abp.Application.Services";
private readonly string _clientProxyTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" +
$"{Environment.NewLine}<using placeholder>" +
$"{Environment.NewLine}" +
$"{Environment.NewLine}namespace <namespace>" +
$"{Environment.NewLine}{{" +
$"{Environment.NewLine} public partial class <className> : ClientProxyBase, <serviceInterface>" +
$"{Environment.NewLine} [Dependency(ReplaceServices = true)]" +
$"{Environment.NewLine} [ExposeServices(typeof(<serviceInterface>))]" +
$"{Environment.NewLine} public partial class <className> : ClientProxyBase<<serviceInterface>>, <serviceInterface>" +
$"{Environment.NewLine} {{" +
$"{Environment.NewLine} <method placeholder>" +
$"{Environment.NewLine} }}" +
@ -48,19 +50,19 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
private readonly List<string> _usingNamespaceList = new()
{
"using System;",
"using System.Threading.Tasks;",
"using Volo.Abp.DependencyInjection;",
"using Volo.Abp.Application.Dtos;",
"using Volo.Abp.Http.Client;",
"using Volo.Abp.Http.Client.ClientProxying;",
"using Volo.Abp.Http.Modeling;"
};
public ILogger<CSharpServiceProxyGenerator> Logger { get; set; }
public CSharpServiceProxyGenerator(
CliHttpClientFactory cliHttpClientFactory,
IJsonSerializer jsonSerializer) :
base(cliHttpClientFactory, jsonSerializer)
{
Logger = NullLogger<CSharpServiceProxyGenerator>.Instance;
}
public override async Task GenerateProxyAsync(GenerateProxyArgs args)
@ -78,17 +80,13 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
var assemblyFilePath = Path.Combine(args.WorkDirectory, "bin", "Debug", GetTargetFrameworkVersion(projectFilePath), $"{projectName}.dll");
var startupModule = GetStartupModule(assemblyFilePath);
var appServiceTypes = new List<Type>();
FindAppServiceTypesRecursively(startupModule, appServiceTypes);
appServiceTypes = appServiceTypes.Distinct().ToList();
var applicationApiDescriptionModel = await GetApplicationApiDescriptionModelAsync(args);
foreach (var controller in applicationApiDescriptionModel.Modules[args.Module].Controllers)
{
if (ShouldGenerateProxy(controller.Value))
{
await GenerateClientProxyFileAsync(args, controller.Value, appServiceTypes, startupModule.Namespace);
await GenerateClientProxyFileAsync(args, controller.Value, startupModule.Namespace);
}
}
@ -122,15 +120,10 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
private async Task GenerateClientProxyFileAsync(
GenerateProxyArgs args,
ControllerApiDescriptionModel controllerApiDescription,
List<Type> appServiceTypes,
string rootNamespace)
{
var appServiceType = appServiceTypes.FirstOrDefault(x => x.FullName == controllerApiDescription.Interfaces.Last().Type);
if (appServiceType == null)
{
return;
}
var appServiceTypeFullName = controllerApiDescription.Interfaces.Last().Type;
var appServiceTypeName = appServiceTypeFullName.Split('.').Last();
var folder = args.Folder.IsNullOrWhiteSpace()? DefaultNamespace : args.Folder;
var usingNamespaceList = new List<string>(_usingNamespaceList);
@ -138,23 +131,20 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
var clientProxyName = $"{controllerApiDescription.ControllerName}ClientProxy";
var clientProxyBuilder = new StringBuilder(_clientProxyTemplate);
var fileNamespace = $"{rootNamespace}.{folder.Replace('/', '.')}";
usingNamespaceList.Add($"using {appServiceType.Namespace};");
usingNamespaceList.Add($"using {GetTypeNamespace(appServiceTypeFullName)};");
clientProxyBuilder.Replace(ClassName, clientProxyName);
clientProxyBuilder.Replace(Namespace, fileNamespace);
clientProxyBuilder.Replace(ServiceInterface, appServiceType.Name);
clientProxyBuilder.Replace(ServiceInterface, appServiceTypeName);
var methods = appServiceType.GetInterfaces().SelectMany(x => x.GetMethods()).ToList();
methods.AddRange(appServiceType.GetMethods());
foreach (var method in methods)
foreach (var action in controllerApiDescription.Actions.Values)
{
var actionApiDescription = controllerApiDescription.Actions.Values.FirstOrDefault(x => x.Name == method.Name);
if (actionApiDescription == null)
if (!ShouldGenerateMethod(appServiceTypeFullName, action))
{
continue;
}
GenerateMethod(actionApiDescription, appServiceType.Name, method, clientProxyBuilder, usingNamespaceList);
GenerateMethod(action, clientProxyBuilder, usingNamespaceList);
}
foreach (var usingNamespace in usingNamespaceList)
@ -188,43 +178,45 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
clientProxyBuilder.Replace(Namespace, fileNamespace);
filePath = filePath.Replace(".cs", ".partial.cs");
using (var writer = new StreamWriter(filePath))
if (!File.Exists(filePath))
{
await writer.WriteAsync(clientProxyBuilder.ToString());
}
using (var writer = new StreamWriter(filePath))
{
await writer.WriteAsync(clientProxyBuilder.ToString());
}
Logger.LogInformation($"Create {filePath.Replace(args.WorkDirectory, string.Empty).TrimStart('\\')}");
Logger.LogInformation($"Create {filePath.Replace(args.WorkDirectory, string.Empty).TrimStart('\\')}");
}
}
private void GenerateMethod(
ActionApiDescriptionModel actionApiDescription,
string serviceName,
MethodInfo method,
ActionApiDescriptionModel action,
StringBuilder clientProxyBuilder,
List<string> usingNamespaceList)
{
var methodBuilder = new StringBuilder();
var returnTypeName = GetRealTypeName(usingNamespaceList, method.ReturnType);
var returnTypeName = GetRealTypeName(usingNamespaceList, action.ReturnValue.Type);
if(!typeof(Task).IsAssignableFrom(method.ReturnType))
if(!action.Name.EndsWith("Async"))
{
GenerateSynchronizationMethod(method, returnTypeName, methodBuilder, usingNamespaceList);
GenerateSynchronizationMethod(action, returnTypeName, methodBuilder, usingNamespaceList);
clientProxyBuilder.Replace(MethodPlaceholder, $"{methodBuilder} {Environment.NewLine} {MethodPlaceholder}");
return;
}
GenerateAsynchronousMethod(actionApiDescription, serviceName ,method, returnTypeName, methodBuilder, usingNamespaceList);
GenerateAsynchronousMethod(action, returnTypeName, methodBuilder, usingNamespaceList);
clientProxyBuilder.Replace(MethodPlaceholder, $"{methodBuilder} {Environment.NewLine} {MethodPlaceholder}");
}
private void GenerateSynchronizationMethod(MethodInfo method, string returnTypeName, StringBuilder methodBuilder, List<string> usingNamespaceList)
private void GenerateSynchronizationMethod(ActionApiDescriptionModel action, string returnTypeName, StringBuilder methodBuilder, List<string> usingNamespaceList)
{
methodBuilder.AppendLine($"public {returnTypeName} {method.Name}(<args>)");
methodBuilder.AppendLine($"public {returnTypeName} {action.Name}(<args>)");
foreach (var parameter in method.GetParameters())
foreach (var parameter in action.Parameters.GroupBy(x => x.Name).Select( x=> x.First()))
{
methodBuilder.Replace("<args>", $"{GetRealTypeName(usingNamespaceList, parameter.ParameterType)} {parameter.Name}, <args>");
methodBuilder.Replace("<args>", $"{GetRealTypeName(usingNamespaceList, parameter.Type)} {parameter.Name}, <args>");
}
methodBuilder.Replace("<args>", string.Empty);
@ -237,18 +229,18 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
}
private void GenerateAsynchronousMethod(
ActionApiDescriptionModel actionApiDescription,
string serviceName,
MethodInfo method,
ActionApiDescriptionModel action,
string returnTypeName,
StringBuilder methodBuilder,
List<string> usingNamespaceList)
{
methodBuilder.AppendLine($"public async {returnTypeName} {method.Name}(<args>)");
var returnSign = returnTypeName == "void" ? "Task": $"Task<{returnTypeName}>";
methodBuilder.AppendLine($"public async {returnSign} {action.Name}(<args>)");
foreach (var parameter in method.GetParameters())
foreach (var parameter in action.ParametersOnMethod)
{
methodBuilder.Replace("<args>", $"{GetRealTypeName(usingNamespaceList, parameter.ParameterType)} {parameter.Name}, <args>");
methodBuilder.Replace("<args>", $"{GetRealTypeName(usingNamespaceList, parameter.Type)} {parameter.Name}, <args>");
}
methodBuilder.Replace("<args>", string.Empty);
@ -256,21 +248,21 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
methodBuilder.AppendLine(" {");
if (method.ReturnType.GenericTypeArguments.IsNullOrEmpty())
if (returnTypeName == "void")
{
methodBuilder.AppendLine($" await MakeRequestAsync<{serviceName}>(\"{method.Name}\", <args>);");
methodBuilder.AppendLine($" await RequestAsync(nameof({action.Name}), <args>);");
}
else
{
methodBuilder.AppendLine($" return await MakeRequestAsync<{serviceName}, {returnTypeName.Replace("Task<", string.Empty)}(\"{method.Name}\", <args>);");
methodBuilder.AppendLine($" return await RequestAsync<{returnTypeName}>(nameof({action.Name}), <args>);");
}
foreach (var parameter in method.GetParameters())
foreach (var parameter in action.ParametersOnMethod)
{
methodBuilder.Replace("<args>", $"{parameter.Name}, <args>");
}
methodBuilder.Replace(", <args>", string.Empty);
methodBuilder.Replace("<args>", string.Empty);
methodBuilder.Replace(", )", ")");
methodBuilder.AppendLine(" }");
}
@ -283,40 +275,63 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
}
var serviceInterface = controllerApiDescription.Interfaces.Last();
return serviceInterface.Type.ToUpper().EndsWith(ServicePostfix);
return serviceInterface.Type.EndsWith(ServicePostfix);
}
private static bool ShouldGenerateMethod(string appServiceTypeName, ActionApiDescriptionModel action)
{
return action.DeclaringFrom.StartsWith(AppServicePrefix) || action.DeclaringFrom.StartsWith(appServiceTypeName);
}
private static string GetTypeNamespace(string typeFullName)
{
return typeFullName.Substring(0, typeFullName.LastIndexOf('.'));
}
private string GetRealTypeName(List<string> usingNamespaceList, Type type)
private string GetRealTypeName(List<string> usingNamespaceList, string typeName)
{
AddUsingNamespace(usingNamespaceList, type);
var filter = new []{"<", ",", ">"};
var stringBuilder = new StringBuilder();
var typeNames = typeName.Split('.');
if (!type.IsGenericType)
if (typeNames.All(x => !filter.Any(x.Contains)))
{
return NormalizeTypeName(type.Name);
AddUsingNamespace(usingNamespaceList, typeName);
return NormalizeTypeName(typeNames.Last());
}
var stringBuilder = new StringBuilder();
stringBuilder.Append(type.Name.Substring(0, type.Name.IndexOf('`')));
stringBuilder.Append('<');
var appendComma = false;
foreach (var arg in type.GetGenericArguments())
var fullName = string.Empty;
foreach (var item in typeNames)
{
if (appendComma)
if (filter.Any(x => item.Contains(x)))
{
stringBuilder.Append(',');
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}";
}
stringBuilder.Append(GetRealTypeName(usingNamespaceList, arg));
appendComma = true;
}
stringBuilder.Append('>');
return stringBuilder.ToString();
}
private void AddUsingNamespace(List<string> usingNamespaceList, Type type)
private static void AddUsingNamespace(List<string> usingNamespaceList, string typeName)
{
var rootNamespace = $"using {type.Namespace};";
if (usingNamespaceList.Contains(type.Namespace) || usingNamespaceList.Any(x => rootNamespace.StartsWith(x)))
var rootNamespace = $"using {GetTypeNamespace(typeName)};";
if (usingNamespaceList.Contains(rootNamespace))
{
return;
}
@ -338,31 +353,6 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
return typeName;
}
private void FindAppServiceTypesRecursively(
Type module,
List<Type> appServiceTypes)
{
var types = module.Assembly
.GetTypes()
.Where(t => t.IsInterface)
.Where(t => typeof(IRemoteService).IsAssignableFrom(t))
.ToList();
appServiceTypes.AddRange(types);
var dependencyDescriptors = module
.GetCustomAttributes()
.OfType<IDependedTypesProvider>();
foreach (var descriptor in dependencyDescriptors)
{
foreach (var dependedModuleType in descriptor.GetDependedTypes().Where(x=>x.Name.EndsWith("HttpApiClientModule") || x.Name.EndsWith("ApplicationContractsModule")))
{
FindAppServiceTypesRecursively(dependedModuleType, appServiceTypes);
}
}
}
private static string CheckWorkDirectory(string directory)
{
if (!Directory.Exists(directory))

6
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/JavaScript/JavaScriptServiceProxyGenerator.cs

@ -3,7 +3,6 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Http;
using Volo.Abp.DependencyInjection;
@ -12,7 +11,7 @@ using Volo.Abp.Json;
namespace Volo.Abp.Cli.ServiceProxy.JavaScript
{
public class JavaScriptServiceProxyGenerator : ServiceProxyGeneratorBase, ITransientDependency
public class JavaScriptServiceProxyGenerator : ServiceProxyGeneratorBase<JavaScriptServiceProxyGenerator>, ITransientDependency
{
public const string Name = "JS";
private const string EventTriggerScript = "abp.event.trigger('abp.serviceProxyScriptInitialized');";
@ -20,8 +19,6 @@ namespace Volo.Abp.Cli.ServiceProxy.JavaScript
private readonly JQueryProxyScriptGenerator _jQueryProxyScriptGenerator;
public ILogger<JavaScriptServiceProxyGenerator> Logger { get; set; }
public JavaScriptServiceProxyGenerator(
CliHttpClientFactory cliHttpClientFactory,
IJsonSerializer jsonSerializer,
@ -29,7 +26,6 @@ namespace Volo.Abp.Cli.ServiceProxy.JavaScript
base(cliHttpClientFactory, jsonSerializer)
{
_jQueryProxyScriptGenerator = jQueryProxyScriptGenerator;
Logger = NullLogger<JavaScriptServiceProxyGenerator>.Instance;
}
public override async Task GenerateProxyAsync(GenerateProxyArgs args)

7
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/ServiceProxyGeneratorBase.cs

@ -1,20 +1,25 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Http;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Json;
namespace Volo.Abp.Cli.ServiceProxy
{
public abstract class ServiceProxyGeneratorBase : IServiceProxyGenerator
public abstract class ServiceProxyGeneratorBase<T> : IServiceProxyGenerator where T: IServiceProxyGenerator
{
public IJsonSerializer JsonSerializer { get; }
public CliHttpClientFactory CliHttpClientFactory { get; }
public ILogger<T> Logger { get; set; }
protected ServiceProxyGeneratorBase(CliHttpClientFactory cliHttpClientFactory, IJsonSerializer jsonSerializer)
{
CliHttpClientFactory = cliHttpClientFactory;
JsonSerializer = jsonSerializer;
Logger = NullLogger<T>.Instance;
}
public abstract Task GenerateProxyAsync(GenerateProxyArgs args);

128
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxyBase.cs

@ -1,128 +0,0 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.FileProviders;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client.DynamicProxying;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Json;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Http.Client
{
public class ClientProxyBase : ITransientDependency
{
public const string ApiDescriptionCacheKey = "client-proxy";
public IAbpLazyServiceProvider LazyServiceProvider { get; set; }
protected IHttpProxyExecuter HttpProxyExecuter => LazyServiceProvider.LazyGetRequiredService<IHttpProxyExecuter>();
protected IJsonSerializer JsonSerializer => LazyServiceProvider.LazyGetRequiredService<IJsonSerializer>();
protected IApiDescriptionCache ApiDescriptionCache => LazyServiceProvider.LazyGetRequiredService<ApiDescriptionCache>();
protected IVirtualFileProvider VirtualFileProvider => LazyServiceProvider.LazyGetRequiredService<VirtualFileProvider>();
protected static readonly Dictionary<string, ActionApiDescriptionModel> ActionApiDescriptionModels = new Dictionary<string, ActionApiDescriptionModel>();
private static object SyncLock = new object();
protected virtual async Task MakeRequestAsync<TService>(string methodName, params object[] arguments)
{
await HttpProxyExecuter.MakeRequestAsync(await BuildHttpProxyExecuterContext<TService>(methodName, arguments));
}
protected virtual async Task<T> MakeRequestAsync<TService,T>(string methodName, params object[] arguments)
{
return await HttpProxyExecuter.MakeRequestAndGetResultAsync<T>(await BuildHttpProxyExecuterContext<TService>(methodName, arguments));
}
protected virtual async Task<HttpProxyExecuterContext> BuildHttpProxyExecuterContext<TService>(string methodName, params object[] arguments)
{
var actionDescriptionKey = $"{typeof(TService).Name}.{methodName}";
if (!ActionApiDescriptionModels.ContainsKey(actionDescriptionKey))
{
var apiDescription = await ApiDescriptionCache.GetAsync(ApiDescriptionCacheKey, GetApplicationApiDescriptionModel);
var controllers = apiDescription.Modules.Select(x=>x.Value).SelectMany(x => x.Controllers.Values).ToList();
lock (SyncLock)
{
foreach (var controller in controllers.Where(x => x.Interfaces.Any()))
{
var appServiceType = controller.Interfaces.Last().Type.Split('.').Last();
foreach (var actionItem in controller.Actions.Values)
{
if (!ActionApiDescriptionModels.ContainsKey($"{appServiceType}.{actionItem.Name}"))
{
ActionApiDescriptionModels.Add($"{appServiceType}.{actionItem.Name}", actionItem);
}
}
}
}
}
var action = ActionApiDescriptionModels[actionDescriptionKey];
return new HttpProxyExecuterContext(action, BuildArguments(action, arguments), typeof(TService));
}
protected virtual Dictionary<string, object> BuildArguments(ActionApiDescriptionModel action, object[] arguments)
{
var parameters = action.Parameters.GroupBy(x => x.NameOnMethod).Select(x => x.Key).ToList();
var dict = new Dictionary<string, object>();
for (var i = 0; i < parameters.Count; i++)
{
dict[parameters[i]] = arguments[i];
}
return dict;
}
protected virtual async Task<ApplicationApiDescriptionModel> GetApplicationApiDescriptionModel()
{
var applicationApiDescription = ApplicationApiDescriptionModel.Create();
var fileInfoList = new List<IFileInfo>();
GetGenerateProxyFileInfos(fileInfoList);
foreach (var fileInfo in fileInfoList)
{
using (var streamReader = new StreamReader(fileInfo.CreateReadStream()))
{
var content = await streamReader.ReadToEndAsync();
var subApplicationApiDescription = JsonSerializer.Deserialize<ApplicationApiDescriptionModel>(content);
foreach (var module in subApplicationApiDescription.Modules)
{
if (!applicationApiDescription.Modules.ContainsKey(module.Key))
{
applicationApiDescription.AddModule(module.Value);
}
}
}
}
return applicationApiDescription;
}
private void GetGenerateProxyFileInfos(List<IFileInfo> fileInfoList, string path = "")
{
foreach (var directoryContent in VirtualFileProvider.GetDirectoryContents(path))
{
if (directoryContent.IsDirectory)
{
GetGenerateProxyFileInfos(fileInfoList, directoryContent.PhysicalPath);
}
else
{
if (directoryContent.Name.EndsWith("generate-proxy.json"))
{
fileInfoList.Add(VirtualFileProvider.GetFileInfo(directoryContent.GetVirtualOrPhysicalPathOrNull()));
}
}
}
}
}
}

105
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyApiDescriptionFinder.cs

@ -0,0 +1,105 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.FileProviders;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Json;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Http.Client.ClientProxying
{
public class ClientProxyApiDescriptionFinder : IClientProxyApiDescriptionFinder, ISingletonDependency
{
protected IVirtualFileProvider VirtualFileProvider { get; }
protected IJsonSerializer JsonSerializer { get; }
protected Dictionary<string, ActionApiDescriptionModel> ActionApiDescriptionModels { get; }
protected ApplicationApiDescriptionModel ApplicationApiDescriptionModel { get; set; }
public ClientProxyApiDescriptionFinder(
IVirtualFileProvider virtualFileProvider,
IJsonSerializer jsonSerializer)
{
VirtualFileProvider = virtualFileProvider;
JsonSerializer = jsonSerializer;
ActionApiDescriptionModels = new Dictionary<string, ActionApiDescriptionModel>();
Initial();
}
public Task<ActionApiDescriptionModel> FindActionAsync(string action)
{
return Task.FromResult(ActionApiDescriptionModels[action]);
}
public Task<ApplicationApiDescriptionModel> GetApiDescriptionAsync()
{
return Task.FromResult(ApplicationApiDescriptionModel);
}
private void Initial()
{
ApplicationApiDescriptionModel = GetApplicationApiDescriptionModel();
var controllers = ApplicationApiDescriptionModel.Modules.Select(x=>x.Value).SelectMany(x => x.Controllers.Values).ToList();
foreach (var controller in controllers.Where(x => x.Interfaces.Any()))
{
var appServiceType = controller.Interfaces.Last().Type;
foreach (var actionItem in controller.Actions.Values)
{
if (!ActionApiDescriptionModels.ContainsKey($"{appServiceType}.{actionItem.Name}"))
{
ActionApiDescriptionModels.Add($"{appServiceType}.{actionItem.Name}", actionItem);
}
}
}
}
private ApplicationApiDescriptionModel GetApplicationApiDescriptionModel()
{
var applicationApiDescription = ApplicationApiDescriptionModel.Create();
var fileInfoList = new List<IFileInfo>();
GetGenerateProxyFileInfos(fileInfoList);
foreach (var fileInfo in fileInfoList)
{
using (var streamReader = new StreamReader(fileInfo.CreateReadStream()))
{
var content = streamReader.ReadToEnd();
var subApplicationApiDescription = JsonSerializer.Deserialize<ApplicationApiDescriptionModel>(content);
foreach (var module in subApplicationApiDescription.Modules)
{
if (!applicationApiDescription.Modules.ContainsKey(module.Key))
{
applicationApiDescription.AddModule(module.Value);
}
}
}
}
return applicationApiDescription;
}
private void GetGenerateProxyFileInfos(List<IFileInfo> fileInfoList, string path = "")
{
foreach (var directoryContent in VirtualFileProvider.GetDirectoryContents(path))
{
if (directoryContent.IsDirectory)
{
GetGenerateProxyFileInfos(fileInfoList, directoryContent.PhysicalPath);
}
else
{
if (directoryContent.Name.EndsWith("generate-proxy.json"))
{
fileInfoList.Add(VirtualFileProvider.GetFileInfo(directoryContent.GetVirtualOrPhysicalPathOrNull()));
}
}
}
}
}
}

47
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs

@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Modeling;
namespace Volo.Abp.Http.Client.ClientProxying
{
public class ClientProxyBase<TService> : ITransientDependency
{
public IAbpLazyServiceProvider LazyServiceProvider { get; set; }
protected IHttpProxyExecuter HttpProxyExecuter => LazyServiceProvider.LazyGetRequiredService<IHttpProxyExecuter>();
protected IClientProxyApiDescriptionFinder ClientProxyApiDescriptionFinder => LazyServiceProvider.LazyGetRequiredService<IClientProxyApiDescriptionFinder>();
protected virtual async Task RequestAsync(string methodName, params object[] arguments)
{
await HttpProxyExecuter.MakeRequestAsync(await BuildHttpProxyExecuterContext(methodName, arguments));
}
protected virtual async Task<T> RequestAsync<T>(string methodName, params object[] arguments)
{
return await HttpProxyExecuter.MakeRequestAndGetResultAsync<T>(await BuildHttpProxyExecuterContext(methodName, arguments));
}
protected virtual async Task<HttpProxyExecuterContext> BuildHttpProxyExecuterContext(string methodName, params object[] arguments)
{
var actionDescriptionKey = $"{typeof(TService).FullName}.{methodName}";
var action = await ClientProxyApiDescriptionFinder.FindActionAsync(actionDescriptionKey);
return new HttpProxyExecuterContext(action, BuildArguments(action, arguments), typeof(TService));
}
protected virtual Dictionary<string, object> BuildArguments(ActionApiDescriptionModel action, object[] arguments)
{
var parameters = action.Parameters.GroupBy(x => x.NameOnMethod).Select(x => x.Key).ToList();
var dict = new Dictionary<string, object>();
for (var i = 0; i < parameters.Count; i++)
{
dict[parameters[i]] = arguments[i];
}
return dict;
}
}
}

12
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IClientProxyApiDescriptionFinder.cs

@ -0,0 +1,12 @@
using System.Threading.Tasks;
using Volo.Abp.Http.Modeling;
namespace Volo.Abp.Http.Client.ClientProxying
{
public interface IClientProxyApiDescriptionFinder
{
Task<ActionApiDescriptionModel> FindActionAsync(string action);
Task<ApplicationApiDescriptionModel> GetApiDescriptionAsync();
}
}

7
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs

@ -28,12 +28,14 @@ namespace Volo.Abp.Http.Modeling
public bool? AllowAnonymous { get; set; }
public string DeclaringFrom { get; set; }
public ActionApiDescriptionModel()
{
}
public static ActionApiDescriptionModel Create([NotNull] string uniqueName, [NotNull] MethodInfo method, [NotNull] string url, [CanBeNull] string httpMethod, [NotNull] IList<string> supportedVersions, bool? allowAnonymous = null)
public static ActionApiDescriptionModel Create([NotNull] string uniqueName, [NotNull] MethodInfo method, [NotNull] string url, [CanBeNull] string httpMethod, [NotNull] IList<string> supportedVersions, bool? allowAnonymous = null, string declaringFrom = null)
{
Check.NotNull(uniqueName, nameof(uniqueName));
Check.NotNull(method, nameof(method));
@ -53,7 +55,8 @@ namespace Volo.Abp.Http.Modeling
.Select(MethodParameterApiDescriptionModel.Create)
.ToList(),
SupportedVersions = supportedVersions,
AllowAnonymous = allowAnonymous
AllowAnonymous = allowAnonymous,
DeclaringFrom = declaringFrom
};
}

1
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj

@ -5,7 +5,6 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>MyCompanyName.MyProjectName</RootNamespace>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>

Loading…
Cancel
Save