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; 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( var actionModel = controllerModel.AddAction(
uniqueMethodName, uniqueMethodName,
ActionApiDescriptionModel.Create( ActionApiDescriptionModel.Create(
@ -121,7 +129,8 @@ namespace Volo.Abp.AspNetCore.Mvc
apiDescription.RelativePath, apiDescription.RelativePath,
apiDescription.HttpMethod, apiDescription.HttpMethod,
GetSupportedVersions(controllerType, method, setting), 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 Newtonsoft.Json.Linq;
using NuGet.Versioning; using NuGet.Versioning;
using Volo.Abp.Cli.Commands; using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.Utils; using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace Volo.Abp.Cli.ServiceProxy.Angular namespace Volo.Abp.Cli.ServiceProxy.Angular
{ {
public class AngularServiceProxyGenerator : IServiceProxyGenerator , ITransientDependency public class AngularServiceProxyGenerator : ServiceProxyGeneratorBase<AngularServiceProxyGenerator> , ITransientDependency
{ {
public const string Name = "NG"; public const string Name = "NG";
public CliService CliService { get; } private readonly CliService _cliService;
public ILogger<AngularServiceProxyGenerator> Logger { get; set; }
public AngularServiceProxyGenerator(CliService cliService) public AngularServiceProxyGenerator(
CliHttpClientFactory cliHttpClientFactory,
IJsonSerializer jsonSerializer,
CliService cliService) :
base(cliHttpClientFactory, jsonSerializer)
{ {
CliService = cliService; _cliService = cliService;
Logger = NullLogger<AngularServiceProxyGenerator>.Instance;
} }
public async Task GenerateProxyAsync(GenerateProxyArgs args) public override async Task GenerateProxyAsync(GenerateProxyArgs args)
{ {
CheckAngularJsonFile(); CheckAngularJsonFile();
await CheckNgSchematicsAsync(); await CheckNgSchematicsAsync();
@ -91,14 +95,14 @@ namespace Volo.Abp.Cli.ServiceProxy.Angular
return; return;
} }
var cliVersion = await CliService.GetCurrentCliVersionAsync(typeof(CliService).Assembly); var cliVersion = await _cliService.GetCurrentCliVersionAsync(typeof(CliService).Assembly);
if (semanticSchematicsVersion < cliVersion) if (semanticSchematicsVersion < cliVersion)
{ {
Logger.LogWarning("\"@abp/ng.schematics\" version is lower than ABP Cli version."); Logger.LogWarning("\"@abp/ng.schematics\" version is lower than ABP Cli version.");
} }
} }
private void CheckAngularJsonFile() private static void CheckAngularJsonFile()
{ {
var angularPath = $"angular.json"; var angularPath = $"angular.json";
if (!File.Exists(angularPath)) 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.Threading.Tasks;
using System.Xml; using System.Xml;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Commands; using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Http; using Volo.Abp.Cli.Http;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -17,7 +16,7 @@ using Volo.Abp.Modularity;
namespace Volo.Abp.Cli.ServiceProxy.CSharp namespace Volo.Abp.Cli.ServiceProxy.CSharp
{ {
public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase, ITransientDependency public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServiceProxyGenerator>, ITransientDependency
{ {
public const string Name = "CSHARP"; public const string Name = "CSHARP";
@ -25,15 +24,18 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
private const string MethodPlaceholder = "<method placeholder>"; private const string MethodPlaceholder = "<method placeholder>";
private const string ClassName = "<className>"; private const string ClassName = "<className>";
private const string ServiceInterface = "<serviceInterface>"; private const string ServiceInterface = "<serviceInterface>";
private const string ServicePostfix = "APPSERVICE"; private const string ServicePostfix = "AppService";
private const string DefaultNamespace = "ClientProxies"; private const string DefaultNamespace = "ClientProxies";
private const string Namespace = "<namespace>"; 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" + private readonly string _clientProxyTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" +
$"{Environment.NewLine}<using placeholder>" + $"{Environment.NewLine}<using placeholder>" +
$"{Environment.NewLine}" + $"{Environment.NewLine}" +
$"{Environment.NewLine}namespace <namespace>" + $"{Environment.NewLine}namespace <namespace>" +
$"{Environment.NewLine}{{" + $"{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} {{" +
$"{Environment.NewLine} <method placeholder>" + $"{Environment.NewLine} <method placeholder>" +
$"{Environment.NewLine} }}" + $"{Environment.NewLine} }}" +
@ -48,19 +50,19 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
private readonly List<string> _usingNamespaceList = new() private readonly List<string> _usingNamespaceList = new()
{ {
"using System;", "using System;",
"using System.Threading.Tasks;",
"using Volo.Abp.DependencyInjection;",
"using Volo.Abp.Application.Dtos;", "using Volo.Abp.Application.Dtos;",
"using Volo.Abp.Http.Client;", "using Volo.Abp.Http.Client;",
"using Volo.Abp.Http.Client.ClientProxying;",
"using Volo.Abp.Http.Modeling;" "using Volo.Abp.Http.Modeling;"
}; };
public ILogger<CSharpServiceProxyGenerator> Logger { get; set; }
public CSharpServiceProxyGenerator( public CSharpServiceProxyGenerator(
CliHttpClientFactory cliHttpClientFactory, CliHttpClientFactory cliHttpClientFactory,
IJsonSerializer jsonSerializer) : IJsonSerializer jsonSerializer) :
base(cliHttpClientFactory, jsonSerializer) base(cliHttpClientFactory, jsonSerializer)
{ {
Logger = NullLogger<CSharpServiceProxyGenerator>.Instance;
} }
public override async Task GenerateProxyAsync(GenerateProxyArgs args) 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 assemblyFilePath = Path.Combine(args.WorkDirectory, "bin", "Debug", GetTargetFrameworkVersion(projectFilePath), $"{projectName}.dll");
var startupModule = GetStartupModule(assemblyFilePath); var startupModule = GetStartupModule(assemblyFilePath);
var appServiceTypes = new List<Type>();
FindAppServiceTypesRecursively(startupModule, appServiceTypes);
appServiceTypes = appServiceTypes.Distinct().ToList();
var applicationApiDescriptionModel = await GetApplicationApiDescriptionModelAsync(args); var applicationApiDescriptionModel = await GetApplicationApiDescriptionModelAsync(args);
foreach (var controller in applicationApiDescriptionModel.Modules[args.Module].Controllers) foreach (var controller in applicationApiDescriptionModel.Modules[args.Module].Controllers)
{ {
if (ShouldGenerateProxy(controller.Value)) 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( private async Task GenerateClientProxyFileAsync(
GenerateProxyArgs args, GenerateProxyArgs args,
ControllerApiDescriptionModel controllerApiDescription, ControllerApiDescriptionModel controllerApiDescription,
List<Type> appServiceTypes,
string rootNamespace) string rootNamespace)
{ {
var appServiceType = appServiceTypes.FirstOrDefault(x => x.FullName == controllerApiDescription.Interfaces.Last().Type); var appServiceTypeFullName = controllerApiDescription.Interfaces.Last().Type;
var appServiceTypeName = appServiceTypeFullName.Split('.').Last();
if (appServiceType == null)
{
return;
}
var folder = args.Folder.IsNullOrWhiteSpace()? DefaultNamespace : args.Folder; var folder = args.Folder.IsNullOrWhiteSpace()? DefaultNamespace : args.Folder;
var usingNamespaceList = new List<string>(_usingNamespaceList); var usingNamespaceList = new List<string>(_usingNamespaceList);
@ -138,23 +131,20 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
var clientProxyName = $"{controllerApiDescription.ControllerName}ClientProxy"; var clientProxyName = $"{controllerApiDescription.ControllerName}ClientProxy";
var clientProxyBuilder = new StringBuilder(_clientProxyTemplate); var clientProxyBuilder = new StringBuilder(_clientProxyTemplate);
var fileNamespace = $"{rootNamespace}.{folder.Replace('/', '.')}"; var fileNamespace = $"{rootNamespace}.{folder.Replace('/', '.')}";
usingNamespaceList.Add($"using {appServiceType.Namespace};"); usingNamespaceList.Add($"using {GetTypeNamespace(appServiceTypeFullName)};");
clientProxyBuilder.Replace(ClassName, clientProxyName); clientProxyBuilder.Replace(ClassName, clientProxyName);
clientProxyBuilder.Replace(Namespace, fileNamespace); clientProxyBuilder.Replace(Namespace, fileNamespace);
clientProxyBuilder.Replace(ServiceInterface, appServiceType.Name); clientProxyBuilder.Replace(ServiceInterface, appServiceTypeName);
var methods = appServiceType.GetInterfaces().SelectMany(x => x.GetMethods()).ToList(); foreach (var action in controllerApiDescription.Actions.Values)
methods.AddRange(appServiceType.GetMethods());
foreach (var method in methods)
{ {
var actionApiDescription = controllerApiDescription.Actions.Values.FirstOrDefault(x => x.Name == method.Name); if (!ShouldGenerateMethod(appServiceTypeFullName, action))
if (actionApiDescription == null)
{ {
continue; continue;
} }
GenerateMethod(actionApiDescription, appServiceType.Name, method, clientProxyBuilder, usingNamespaceList); GenerateMethod(action, clientProxyBuilder, usingNamespaceList);
} }
foreach (var usingNamespace in usingNamespaceList) foreach (var usingNamespace in usingNamespaceList)
@ -188,43 +178,45 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
clientProxyBuilder.Replace(Namespace, fileNamespace); clientProxyBuilder.Replace(Namespace, fileNamespace);
filePath = filePath.Replace(".cs", ".partial.cs"); 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( private void GenerateMethod(
ActionApiDescriptionModel actionApiDescription, ActionApiDescriptionModel action,
string serviceName,
MethodInfo method,
StringBuilder clientProxyBuilder, StringBuilder clientProxyBuilder,
List<string> usingNamespaceList) List<string> usingNamespaceList)
{ {
var methodBuilder = new StringBuilder(); 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}"); clientProxyBuilder.Replace(MethodPlaceholder, $"{methodBuilder} {Environment.NewLine} {MethodPlaceholder}");
return; return;
} }
GenerateAsynchronousMethod(actionApiDescription, serviceName ,method, returnTypeName, methodBuilder, usingNamespaceList); GenerateAsynchronousMethod(action, returnTypeName, methodBuilder, usingNamespaceList);
clientProxyBuilder.Replace(MethodPlaceholder, $"{methodBuilder} {Environment.NewLine} {MethodPlaceholder}"); 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); methodBuilder.Replace("<args>", string.Empty);
@ -237,18 +229,18 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
} }
private void GenerateAsynchronousMethod( private void GenerateAsynchronousMethod(
ActionApiDescriptionModel actionApiDescription, ActionApiDescriptionModel action,
string serviceName,
MethodInfo method,
string returnTypeName, string returnTypeName,
StringBuilder methodBuilder, StringBuilder methodBuilder,
List<string> usingNamespaceList) 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); methodBuilder.Replace("<args>", string.Empty);
@ -256,21 +248,21 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
methodBuilder.AppendLine(" {"); 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 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>", $"{parameter.Name}, <args>");
} }
methodBuilder.Replace(", <args>", string.Empty); methodBuilder.Replace("<args>", string.Empty);
methodBuilder.Replace(", )", ")"); methodBuilder.Replace(", )", ")");
methodBuilder.AppendLine(" }"); methodBuilder.AppendLine(" }");
} }
@ -283,40 +275,63 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
} }
var serviceInterface = controllerApiDescription.Interfaces.Last(); 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(); var fullName = string.Empty;
stringBuilder.Append(type.Name.Substring(0, type.Name.IndexOf('`')));
stringBuilder.Append('<'); foreach (var item in typeNames)
var appendComma = false;
foreach (var arg in type.GetGenericArguments())
{ {
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(); 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};"; var rootNamespace = $"using {GetTypeNamespace(typeName)};";
if (usingNamespaceList.Contains(type.Namespace) || usingNamespaceList.Any(x => rootNamespace.StartsWith(x))) if (usingNamespaceList.Contains(rootNamespace))
{ {
return; return;
} }
@ -338,31 +353,6 @@ namespace Volo.Abp.Cli.ServiceProxy.CSharp
return typeName; 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) private static string CheckWorkDirectory(string directory)
{ {
if (!Directory.Exists(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.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Commands; using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Http; using Volo.Abp.Cli.Http;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -12,7 +11,7 @@ using Volo.Abp.Json;
namespace Volo.Abp.Cli.ServiceProxy.JavaScript namespace Volo.Abp.Cli.ServiceProxy.JavaScript
{ {
public class JavaScriptServiceProxyGenerator : ServiceProxyGeneratorBase, ITransientDependency public class JavaScriptServiceProxyGenerator : ServiceProxyGeneratorBase<JavaScriptServiceProxyGenerator>, ITransientDependency
{ {
public const string Name = "JS"; public const string Name = "JS";
private const string EventTriggerScript = "abp.event.trigger('abp.serviceProxyScriptInitialized');"; private const string EventTriggerScript = "abp.event.trigger('abp.serviceProxyScriptInitialized');";
@ -20,8 +19,6 @@ namespace Volo.Abp.Cli.ServiceProxy.JavaScript
private readonly JQueryProxyScriptGenerator _jQueryProxyScriptGenerator; private readonly JQueryProxyScriptGenerator _jQueryProxyScriptGenerator;
public ILogger<JavaScriptServiceProxyGenerator> Logger { get; set; }
public JavaScriptServiceProxyGenerator( public JavaScriptServiceProxyGenerator(
CliHttpClientFactory cliHttpClientFactory, CliHttpClientFactory cliHttpClientFactory,
IJsonSerializer jsonSerializer, IJsonSerializer jsonSerializer,
@ -29,7 +26,6 @@ namespace Volo.Abp.Cli.ServiceProxy.JavaScript
base(cliHttpClientFactory, jsonSerializer) base(cliHttpClientFactory, jsonSerializer)
{ {
_jQueryProxyScriptGenerator = jQueryProxyScriptGenerator; _jQueryProxyScriptGenerator = jQueryProxyScriptGenerator;
Logger = NullLogger<JavaScriptServiceProxyGenerator>.Instance;
} }
public override async Task GenerateProxyAsync(GenerateProxyArgs args) 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 System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Http; using Volo.Abp.Cli.Http;
using Volo.Abp.Http.Modeling; using Volo.Abp.Http.Modeling;
using Volo.Abp.Json; using Volo.Abp.Json;
namespace Volo.Abp.Cli.ServiceProxy namespace Volo.Abp.Cli.ServiceProxy
{ {
public abstract class ServiceProxyGeneratorBase : IServiceProxyGenerator public abstract class ServiceProxyGeneratorBase<T> : IServiceProxyGenerator where T: IServiceProxyGenerator
{ {
public IJsonSerializer JsonSerializer { get; } public IJsonSerializer JsonSerializer { get; }
public CliHttpClientFactory CliHttpClientFactory { get; } public CliHttpClientFactory CliHttpClientFactory { get; }
public ILogger<T> Logger { get; set; }
protected ServiceProxyGeneratorBase(CliHttpClientFactory cliHttpClientFactory, IJsonSerializer jsonSerializer) protected ServiceProxyGeneratorBase(CliHttpClientFactory cliHttpClientFactory, IJsonSerializer jsonSerializer)
{ {
CliHttpClientFactory = cliHttpClientFactory; CliHttpClientFactory = cliHttpClientFactory;
JsonSerializer = jsonSerializer; JsonSerializer = jsonSerializer;
Logger = NullLogger<T>.Instance;
} }
public abstract Task GenerateProxyAsync(GenerateProxyArgs args); 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 bool? AllowAnonymous { get; set; }
public string DeclaringFrom { get; set; }
public ActionApiDescriptionModel() 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(uniqueName, nameof(uniqueName));
Check.NotNull(method, nameof(method)); Check.NotNull(method, nameof(method));
@ -53,7 +55,8 @@ namespace Volo.Abp.Http.Modeling
.Select(MethodParameterApiDescriptionModel.Create) .Select(MethodParameterApiDescriptionModel.Create)
.ToList(), .ToList(),
SupportedVersions = supportedVersions, 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> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>MyCompanyName.MyProjectName</RootNamespace> <RootNamespace>MyCompanyName.MyProjectName</RootNamespace>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

Loading…
Cancel
Save