diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index ca1ebef793..64132608aa 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/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 ) ); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/Angular/AngularServiceProxyGenerator.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/Angular/AngularServiceProxyGenerator.cs index dc70b55524..4af24ba846 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/Angular/AngularServiceProxyGenerator.cs +++ b/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 , ITransientDependency { public const string Name = "NG"; - public CliService CliService { get; } - public ILogger 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.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)) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/CSharp/CSharpServiceProxyGenerator.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/CSharp/CSharpServiceProxyGenerator.cs index c3222985fa..c26ed8f376 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/CSharp/CSharpServiceProxyGenerator.cs +++ b/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, ITransientDependency { public const string Name = "CSHARP"; @@ -25,15 +24,18 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp private const string MethodPlaceholder = ""; private const string ClassName = ""; private const string ServiceInterface = ""; - private const string ServicePostfix = "APPSERVICE"; + private const string ServicePostfix = "AppService"; private const string DefaultNamespace = "ClientProxies"; private const string 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}" + $"{Environment.NewLine}" + $"{Environment.NewLine}namespace " + $"{Environment.NewLine}{{" + - $"{Environment.NewLine} public partial class : ClientProxyBase, " + + $"{Environment.NewLine} [Dependency(ReplaceServices = true)]" + + $"{Environment.NewLine} [ExposeServices(typeof())]" + + $"{Environment.NewLine} public partial class : ClientProxyBase<>, " + $"{Environment.NewLine} {{" + $"{Environment.NewLine} " + $"{Environment.NewLine} }}" + @@ -48,19 +50,19 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp private readonly List _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 Logger { get; set; } - public CSharpServiceProxyGenerator( CliHttpClientFactory cliHttpClientFactory, IJsonSerializer jsonSerializer) : base(cliHttpClientFactory, jsonSerializer) { - Logger = NullLogger.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(); - 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 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(_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 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 usingNamespaceList) + private void GenerateSynchronizationMethod(ActionApiDescriptionModel action, string returnTypeName, StringBuilder methodBuilder, List usingNamespaceList) { - methodBuilder.AppendLine($"public {returnTypeName} {method.Name}()"); + methodBuilder.AppendLine($"public {returnTypeName} {action.Name}()"); - foreach (var parameter in method.GetParameters()) + foreach (var parameter in action.Parameters.GroupBy(x => x.Name).Select( x=> x.First())) { - methodBuilder.Replace("", $"{GetRealTypeName(usingNamespaceList, parameter.ParameterType)} {parameter.Name}, "); + methodBuilder.Replace("", $"{GetRealTypeName(usingNamespaceList, parameter.Type)} {parameter.Name}, "); } methodBuilder.Replace("", 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 usingNamespaceList) { - methodBuilder.AppendLine($"public async {returnTypeName} {method.Name}()"); + var returnSign = returnTypeName == "void" ? "Task": $"Task<{returnTypeName}>"; + + methodBuilder.AppendLine($"public async {returnSign} {action.Name}()"); - foreach (var parameter in method.GetParameters()) + foreach (var parameter in action.ParametersOnMethod) { - methodBuilder.Replace("", $"{GetRealTypeName(usingNamespaceList, parameter.ParameterType)} {parameter.Name}, "); + methodBuilder.Replace("", $"{GetRealTypeName(usingNamespaceList, parameter.Type)} {parameter.Name}, "); } methodBuilder.Replace("", 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}\", );"); + methodBuilder.AppendLine($" await RequestAsync(nameof({action.Name}), );"); } else { - methodBuilder.AppendLine($" return await MakeRequestAsync<{serviceName}, {returnTypeName.Replace("Task<", string.Empty)}(\"{method.Name}\", );"); + methodBuilder.AppendLine($" return await RequestAsync<{returnTypeName}>(nameof({action.Name}), );"); } - foreach (var parameter in method.GetParameters()) + foreach (var parameter in action.ParametersOnMethod) { methodBuilder.Replace("", $"{parameter.Name}, "); } - methodBuilder.Replace(", ", string.Empty); + methodBuilder.Replace("", 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 usingNamespaceList, Type type) + private string GetRealTypeName(List 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 usingNamespaceList, Type type) + private static void AddUsingNamespace(List 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 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(); - - 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)) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/JavaScript/JavaScriptServiceProxyGenerator.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/JavaScript/JavaScriptServiceProxyGenerator.cs index 521ee6fc5c..485c5b7f0a 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/JavaScript/JavaScriptServiceProxyGenerator.cs +++ b/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, 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 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.Instance; } public override async Task GenerateProxyAsync(GenerateProxyArgs args) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/ServiceProxyGeneratorBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/ServiceProxyGeneratorBase.cs index b90fd88f1e..a183efa35b 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxy/ServiceProxyGeneratorBase.cs +++ b/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 : IServiceProxyGenerator where T: IServiceProxyGenerator { public IJsonSerializer JsonSerializer { get; } public CliHttpClientFactory CliHttpClientFactory { get; } + public ILogger Logger { get; set; } + protected ServiceProxyGeneratorBase(CliHttpClientFactory cliHttpClientFactory, IJsonSerializer jsonSerializer) { CliHttpClientFactory = cliHttpClientFactory; JsonSerializer = jsonSerializer; + Logger = NullLogger.Instance; } public abstract Task GenerateProxyAsync(GenerateProxyArgs args); diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxyBase.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxyBase.cs deleted file mode 100644 index 386a20efac..0000000000 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxyBase.cs +++ /dev/null @@ -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(); - protected IJsonSerializer JsonSerializer => LazyServiceProvider.LazyGetRequiredService(); - protected IApiDescriptionCache ApiDescriptionCache => LazyServiceProvider.LazyGetRequiredService(); - protected IVirtualFileProvider VirtualFileProvider => LazyServiceProvider.LazyGetRequiredService(); - - protected static readonly Dictionary ActionApiDescriptionModels = new Dictionary(); - - private static object SyncLock = new object(); - - protected virtual async Task MakeRequestAsync(string methodName, params object[] arguments) - { - await HttpProxyExecuter.MakeRequestAsync(await BuildHttpProxyExecuterContext(methodName, arguments)); - } - - protected virtual async Task MakeRequestAsync(string methodName, params object[] arguments) - { - return await HttpProxyExecuter.MakeRequestAndGetResultAsync(await BuildHttpProxyExecuterContext(methodName, arguments)); - } - - protected virtual async Task BuildHttpProxyExecuterContext(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 BuildArguments(ActionApiDescriptionModel action, object[] arguments) - { - var parameters = action.Parameters.GroupBy(x => x.NameOnMethod).Select(x => x.Key).ToList(); - var dict = new Dictionary(); - - for (var i = 0; i < parameters.Count; i++) - { - dict[parameters[i]] = arguments[i]; - } - - return dict; - } - - protected virtual async Task GetApplicationApiDescriptionModel() - { - var applicationApiDescription = ApplicationApiDescriptionModel.Create(); - - var fileInfoList = new List(); - GetGenerateProxyFileInfos(fileInfoList); - - foreach (var fileInfo in fileInfoList) - { - using (var streamReader = new StreamReader(fileInfo.CreateReadStream())) - { - var content = await streamReader.ReadToEndAsync(); - - var subApplicationApiDescription = JsonSerializer.Deserialize(content); - - foreach (var module in subApplicationApiDescription.Modules) - { - if (!applicationApiDescription.Modules.ContainsKey(module.Key)) - { - applicationApiDescription.AddModule(module.Value); - } - } - } - } - - return applicationApiDescription; - } - - private void GetGenerateProxyFileInfos(List 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())); - } - } - } - } - } -} diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyApiDescriptionFinder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyApiDescriptionFinder.cs new file mode 100644 index 0000000000..c12a74f013 --- /dev/null +++ b/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 ActionApiDescriptionModels { get; } + protected ApplicationApiDescriptionModel ApplicationApiDescriptionModel { get; set; } + + public ClientProxyApiDescriptionFinder( + IVirtualFileProvider virtualFileProvider, + IJsonSerializer jsonSerializer) + { + VirtualFileProvider = virtualFileProvider; + JsonSerializer = jsonSerializer; + ActionApiDescriptionModels = new Dictionary(); + + Initial(); + } + + public Task FindActionAsync(string action) + { + return Task.FromResult(ActionApiDescriptionModels[action]); + } + + public Task 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(); + GetGenerateProxyFileInfos(fileInfoList); + + foreach (var fileInfo in fileInfoList) + { + using (var streamReader = new StreamReader(fileInfo.CreateReadStream())) + { + var content = streamReader.ReadToEnd(); + + var subApplicationApiDescription = JsonSerializer.Deserialize(content); + + foreach (var module in subApplicationApiDescription.Modules) + { + if (!applicationApiDescription.Modules.ContainsKey(module.Key)) + { + applicationApiDescription.AddModule(module.Value); + } + } + } + } + + return applicationApiDescription; + } + + private void GetGenerateProxyFileInfos(List 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())); + } + } + } + } + } +} diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs new file mode 100644 index 0000000000..85d8242ad1 --- /dev/null +++ b/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 : ITransientDependency + { + public IAbpLazyServiceProvider LazyServiceProvider { get; set; } + + protected IHttpProxyExecuter HttpProxyExecuter => LazyServiceProvider.LazyGetRequiredService(); + protected IClientProxyApiDescriptionFinder ClientProxyApiDescriptionFinder => LazyServiceProvider.LazyGetRequiredService(); + + protected virtual async Task RequestAsync(string methodName, params object[] arguments) + { + await HttpProxyExecuter.MakeRequestAsync(await BuildHttpProxyExecuterContext(methodName, arguments)); + } + + protected virtual async Task RequestAsync(string methodName, params object[] arguments) + { + return await HttpProxyExecuter.MakeRequestAndGetResultAsync(await BuildHttpProxyExecuterContext(methodName, arguments)); + } + + protected virtual async Task 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 BuildArguments(ActionApiDescriptionModel action, object[] arguments) + { + var parameters = action.Parameters.GroupBy(x => x.NameOnMethod).Select(x => x.Key).ToList(); + var dict = new Dictionary(); + + for (var i = 0; i < parameters.Count; i++) + { + dict[parameters[i]] = arguments[i]; + } + + return dict; + } + } +} diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IClientProxyApiDescriptionFinder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IClientProxyApiDescriptionFinder.cs new file mode 100644 index 0000000000..2cb30d1926 --- /dev/null +++ b/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 FindActionAsync(string action); + + Task GetApiDescriptionAsync(); + } +} diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs index 2901161259..a83d4c4c87 100644 --- a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs +++ b/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 supportedVersions, bool? allowAnonymous = null) + public static ActionApiDescriptionModel Create([NotNull] string uniqueName, [NotNull] MethodInfo method, [NotNull] string url, [CanBeNull] string httpMethod, [NotNull] IList 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 }; } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj index 4faf7dcee3..8c5379654f 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj @@ -5,7 +5,6 @@ netstandard2.0 MyCompanyName.MyProjectName - true