Browse Source

Merge pull request #96 from aspnetzero/http-dynamic-proxy

Http dynamic proxy
pull/113/head
Halil İbrahim Kalkan 9 years ago
committed by GitHub
parent
commit
096983c8ac
  1. 21
      Volo.Abp.sln
  2. 3
      src/Volo.Abp.AspNetCore.Mvc/Properties/AssemblyInfo.cs
  3. 1
      src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj
  4. 3
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs
  5. 5
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs
  6. 29
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ActionDescriptorExtensions.cs
  7. 3
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AppServiceControllerOptions.cs
  8. 181
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs
  9. 2
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ControllerAssemblySettingList.cs
  10. 23
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Controllers/AbpApiDefinitionController.cs
  11. 42
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Utils/ArrayMatcher.cs
  12. 1
      src/Volo.Abp.AspNetCore.TestBase/Volo.Abp.AspNetCore.TestBase.csproj
  13. 4
      src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/AbpAspNetCoreIntegratedTestBase.cs
  14. 2
      src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/AbpAspNetCoreTestBaseModule.cs
  15. 22
      src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/DynamicProxying/AspNetCoreTestDynamicProxyHttpClientFactory.cs
  16. 9
      src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/ITestServerAccessor.cs
  17. 10
      src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/TestServerAccessor.cs
  18. 4
      src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpInterceptorAdapter.cs
  19. 2
      src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs
  20. 42
      src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicHttpClientProxyExtensions.cs
  21. 19
      src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj
  22. 16
      src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientOptions.cs
  23. 14
      src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpModule.cs
  24. 65
      src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApplicationApiDescriptionModelManager.cs
  25. 13
      src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DefaultDynamicProxyHttpClientFactory.cs
  26. 20
      src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpClientProxyConfig.cs
  27. 133
      src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs
  28. 11
      src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IApplicationApiDescriptionModelManager.cs
  29. 9
      src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IDynamicProxyHttpClientFactory.cs
  30. 5
      src/Volo.Abp.Http/Volo.Abp.Http.csproj
  31. 53
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs
  32. 57
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ApplicationApiDescriptionModel.cs
  33. 70
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs
  34. 23
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerInterfaceApiDescriptionModel.cs
  35. 7
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/IApiDescriptionModelProvider.cs
  36. 33
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/MethodParameterApiDescriptionModel.cs
  37. 63
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs
  38. 44
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs
  39. 17
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs
  40. 13
      src/Volo.Abp/System/AbpTypeExtensions.cs
  41. 1
      src/Volo.Abp/Volo.Abp.csproj
  42. 2
      src/Volo.Abp/Volo/Abp/Modularity/AbpModuleDescriptor.cs
  43. 2
      src/Volo.Abp/Volo/Abp/Reflection/AssemblyFinder.cs
  44. 2
      src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs
  45. 19
      test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Utils/ArrayMacther_Tests.cs
  46. 2
      test/Volo.Abp.AspNetCore.Tests/Volo/Abp/AspNetCore/AppTestBase.cs
  47. 4
      test/Volo.Abp.Http.Client.Tests/Volo.Abp.Http.Client.Tests.csproj
  48. 1
      test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpTestBase.cs
  49. 7
      test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpTestModule.cs
  50. 1
      test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs
  51. 0
      test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Startup.cs
  52. 2
      test/Volo.Abp.Tests/Volo/Abp/Reflection/AssemblyFinder_Tests.cs
  53. 4
      test/Volo.Abp.Tests/Volo/Abp/Reflection/TypeFinder_Tests.cs

21
Volo.Abp.sln

@ -128,9 +128,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.MemoryDb.Tests", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TestApp.Tests", "test\Volo.Abp.TestApp.Tests\Volo.Abp.TestApp.Tests.csproj", "{4C2F7B03-C598-4432-A43A-B065D9D0712F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Http", "src\Volo.Abp.Http\Volo.Abp.Http.csproj", "{01A70034-D353-4BF9-821D-F2B6F7641532}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Http", "src\Volo.Abp.Http\Volo.Abp.Http.csproj", "{01A70034-D353-4BF9-821D-F2B6F7641532}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Http.Tests", "test\Volo.Abp.Http.Tests\Volo.Abp.Http.Tests.csproj", "{5B86E837-0C89-4058-A84B-0B51F2F573D4}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Http.Client", "src\Volo.Abp.Http.Client\Volo.Abp.Http.Client.csproj", "{D5E2FB37-0194-480A-B952-5FFECC1200EB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Http.Client.Tests", "test\Volo.Abp.Http.Client.Tests\Volo.Abp.Http.Client.Tests.csproj", "{703BD43C-02B9-413F-854C-9CBA0C963196}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -326,10 +328,14 @@ Global
{01A70034-D353-4BF9-821D-F2B6F7641532}.Debug|Any CPU.Build.0 = Debug|Any CPU
{01A70034-D353-4BF9-821D-F2B6F7641532}.Release|Any CPU.ActiveCfg = Release|Any CPU
{01A70034-D353-4BF9-821D-F2B6F7641532}.Release|Any CPU.Build.0 = Release|Any CPU
{5B86E837-0C89-4058-A84B-0B51F2F573D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B86E837-0C89-4058-A84B-0B51F2F573D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B86E837-0C89-4058-A84B-0B51F2F573D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B86E837-0C89-4058-A84B-0B51F2F573D4}.Release|Any CPU.Build.0 = Release|Any CPU
{D5E2FB37-0194-480A-B952-5FFECC1200EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5E2FB37-0194-480A-B952-5FFECC1200EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5E2FB37-0194-480A-B952-5FFECC1200EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5E2FB37-0194-480A-B952-5FFECC1200EB}.Release|Any CPU.Build.0 = Release|Any CPU
{703BD43C-02B9-413F-854C-9CBA0C963196}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{703BD43C-02B9-413F-854C-9CBA0C963196}.Debug|Any CPU.Build.0 = Debug|Any CPU
{703BD43C-02B9-413F-854C-9CBA0C963196}.Release|Any CPU.ActiveCfg = Release|Any CPU
{703BD43C-02B9-413F-854C-9CBA0C963196}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -391,7 +397,8 @@ Global
{D0279C94-E9A3-4A1B-968B-D3BBF3E06FD8} = {37087D1B-3693-4E96-983D-A69F210BDE53}
{4C2F7B03-C598-4432-A43A-B065D9D0712F} = {37087D1B-3693-4E96-983D-A69F210BDE53}
{01A70034-D353-4BF9-821D-F2B6F7641532} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8}
{5B86E837-0C89-4058-A84B-0B51F2F573D4} = {37087D1B-3693-4E96-983D-A69F210BDE53}
{D5E2FB37-0194-480A-B952-5FFECC1200EB} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8}
{703BD43C-02B9-413F-854C-9CBA0C963196} = {37087D1B-3693-4E96-983D-A69F210BDE53}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

3
src/Volo.Abp.AspNetCore.Mvc/Properties/AssemblyInfo.cs

@ -1,4 +1,5 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@ -9,6 +10,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyProduct("Volo.Abp.AspNetCore.Mvc")]
[assembly: AssemblyTrademark("")]
[assembly:InternalsVisibleTo("Volo.Abp.AspNetCore.Mvc.Tests")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.

1
src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj

@ -13,6 +13,7 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore\Volo.Abp.AspNetCore.csproj" />
<ProjectReference Include="..\Volo.Abp.Http\Volo.Abp.Http.csproj" />
</ItemGroup>
<ItemGroup>

3
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs

@ -11,6 +11,7 @@ using Microsoft.Extensions.Options;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Reflection;
namespace Volo.Abp.AspNetCore.Mvc
@ -245,7 +246,7 @@ namespace Volo.Abp.AspNetCore.Mvc
protected virtual string GetModuleNameOrDefault(Type controllerType)
{
return GetControllerSettingOrNull(controllerType)?.ModuleName ??
AbpControllerAssemblySetting.DefaultServiceModuleName;
ModuleApiDescriptionModel.DefaultServiceModuleName;
}
[CanBeNull]

5
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs

@ -6,11 +6,6 @@ namespace Volo.Abp.AspNetCore.Mvc
{
public class AbpControllerAssemblySetting
{
/// <summary>
/// "app".
/// </summary>
public const string DefaultServiceModuleName = "app";
public string ModuleName { get; }
public Assembly Assembly { get; }

29
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ActionDescriptorExtensions.cs

@ -0,0 +1,29 @@
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;
namespace Volo.Abp.AspNetCore.Mvc
{
public static class ActionDescriptorExtensions
{
public static ControllerActionDescriptor AsControllerActionDescriptor(this ActionDescriptor actionDescriptor)
{
if (!actionDescriptor.IsControllerAction())
{
throw new AbpException($"{nameof(actionDescriptor)} should be type of {typeof(ControllerActionDescriptor).AssemblyQualifiedName}");
}
return actionDescriptor as ControllerActionDescriptor;
}
public static MethodInfo GetMethodInfo(this ActionDescriptor actionDescriptor)
{
return actionDescriptor.AsControllerActionDescriptor().MethodInfo;
}
public static bool IsControllerAction(this ActionDescriptor actionDescriptor)
{
return actionDescriptor is ControllerActionDescriptor;
}
}
}

3
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AppServiceControllerOptions.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Http;
using Volo.Abp.Http.Modeling;
namespace Volo.Abp.AspNetCore.Mvc
{
@ -30,7 +31,7 @@ namespace Volo.Abp.AspNetCore.Mvc
public AbpControllerAssemblySettingBuilder CreateFor(
Assembly assembly,
string moduleName = AbpControllerAssemblySetting.DefaultServiceModuleName,
string moduleName = ModuleApiDescriptionModel.DefaultServiceModuleName,
bool useConventionalHttpVerbs = true)
{
var setting = new AbpControllerAssemblySetting(moduleName, assembly, useConventionalHttpVerbs);

181
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs

@ -0,0 +1,181 @@
using System;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.Application.Services;
using Volo.Abp.AspNetCore.Mvc.Utils;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Modeling;
namespace Volo.Abp.AspNetCore.Mvc
{
public class AspNetCoreApiDescriptionModelProvider : IApiDescriptionModelProvider, ISingletonDependency
{
public ILogger<AspNetCoreApiDescriptionModelProvider> Logger { get; set; }
private readonly IApiDescriptionGroupCollectionProvider _descriptionProvider;
private readonly AbpAspNetCoreMvcOptions _options;
public AspNetCoreApiDescriptionModelProvider(
IApiDescriptionGroupCollectionProvider descriptionProvider,
IOptions<AbpAspNetCoreMvcOptions> options)
{
_descriptionProvider = descriptionProvider;
_options = options.Value;
Logger = NullLogger<AspNetCoreApiDescriptionModelProvider>.Instance;
}
public ApplicationApiDescriptionModel CreateModel()
{
var model = ApplicationApiDescriptionModel.Create();
foreach (var descriptionGroupItem in _descriptionProvider.ApiDescriptionGroups.Items)
{
foreach (var apiDescription in descriptionGroupItem.Items)
{
if (!apiDescription.ActionDescriptor.IsControllerAction())
{
continue;
}
AddApiDescriptionToModel(apiDescription, model);
}
}
return model;
}
private void AddApiDescriptionToModel(ApiDescription apiDescription, ApplicationApiDescriptionModel model)
{
var controllerType = apiDescription.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.AsType();
var moduleModel = model.GetOrAddModule(GetModuleName(controllerType));
var controllerModel = moduleModel.GetOrAddController(GetControllerName(apiDescription), controllerType);
var method = apiDescription.ActionDescriptor.GetMethodInfo();
var uniqueMethodName = GetUniqueActionName(method);
if (controllerModel.Actions.ContainsKey(uniqueMethodName))
{
Logger.LogWarning($"Controller '{controllerModel.ControllerName}' contains more than one action with name '{uniqueMethodName}' for module '{moduleModel.Name}'. Ignored: " + method);
return;
}
var actionModel = controllerModel.AddAction(ActionApiDescriptionModel.Create(
method,
uniqueMethodName,
apiDescription.RelativePath,
apiDescription.HttpMethod
));
AddParameterDescriptionsToModel(actionModel, method, apiDescription);
}
private static string GetControllerName(ApiDescription apiDescription)
{
return apiDescription.GroupName?.RemovePostFix(ApplicationService.CommonPostfixes)
?? apiDescription.ActionDescriptor.AsControllerActionDescriptor().ControllerName;
}
private static string GetUniqueActionName(MethodInfo method)
{
var methodNameBuilder = new StringBuilder(method.Name);
var parameters = method.GetParameters();
if (parameters.Any())
{
methodNameBuilder.Append("By");
for (var i = 0; i < parameters.Length; i++)
{
if (i > 0)
{
methodNameBuilder.Append("And");
}
methodNameBuilder.Append(parameters[i].Name.ToPascalCase());
}
}
return methodNameBuilder.ToString();
}
private void AddParameterDescriptionsToModel(ActionApiDescriptionModel actionModel, MethodInfo method, ApiDescription apiDescription)
{
if (!apiDescription.ParameterDescriptions.Any())
{
return;
}
var matchedMethodParamNames = ArrayMatcher.Match(
apiDescription.ParameterDescriptions.Select(p => p.Name).ToArray(),
method.GetParameters().Select(GetMethodParamName).ToArray()
);
for (var i = 0; i < apiDescription.ParameterDescriptions.Count; i++)
{
var parameterDescription = apiDescription.ParameterDescriptions[i];
var matchedMethodParamName = matchedMethodParamNames.Length > i
? matchedMethodParamNames[i]
: parameterDescription.Name;
actionModel.AddParameter(ParameterApiDescriptionModel.Create(
parameterDescription.Name,
matchedMethodParamName,
parameterDescription.Type,
parameterDescription.RouteInfo?.IsOptional ?? false,
parameterDescription.RouteInfo?.DefaultValue,
parameterDescription.RouteInfo?.Constraints?.Select(c => c.GetType().Name).ToArray(),
parameterDescription.Source.Id
)
);
}
}
public string GetMethodParamName(ParameterInfo parameterInfo)
{
var modelNameProvider = parameterInfo.GetCustomAttributes()
.OfType<IModelNameProvider>()
.FirstOrDefault();
if (modelNameProvider == null)
{
return parameterInfo.Name;
}
return modelNameProvider.Name;
}
private string GetModuleName(Type controllerType)
{
if (controllerType == null)
{
return ModuleApiDescriptionModel.DefaultServiceModuleName;
}
foreach (var controllerSetting in _options.AppServiceControllers.ControllerAssemblySettings)
{
if (Equals(controllerType.Assembly, controllerSetting.Assembly))
{
return controllerSetting.ModuleName;
}
}
var areaAttr = controllerType.GetCustomAttributes().OfType<AreaAttribute>().FirstOrDefault();
if (areaAttr != null)
{
return areaAttr.RouteValue;
}
return ModuleApiDescriptionModel.DefaultServiceModuleName;
}
}
}

2
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ControllerAssemblySettingList.cs

@ -10,7 +10,7 @@ namespace Volo.Abp.AspNetCore.Mvc
[CanBeNull]
public AbpControllerAssemblySetting GetSettingOrNull(Type controllerType)
{
return this.FirstOrDefault(controllerSetting => controllerSetting.Assembly == controllerType.GetAssembly());
return this.FirstOrDefault(controllerSetting => controllerSetting.Assembly == controllerType.Assembly);
}
}
}

23
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Controllers/AbpApiDefinitionController.cs

@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Http.Modeling;
namespace Volo.Abp.AspNetCore.Mvc.Controllers
{
[Area("abp")]
public class AbpApiDefinitionController : AbpController
{
private readonly IApiDescriptionModelProvider _modelProvider;
public AbpApiDefinitionController(IApiDescriptionModelProvider modelProvider)
{
_modelProvider = modelProvider;
}
[HttpGet]
[Route("api/abp/api-description")]
public ApplicationApiDescriptionModel Get()
{
return _modelProvider.CreateModel();
}
}
}

42
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Utils/ArrayMatcher.cs

@ -0,0 +1,42 @@
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.Utils
{
internal static class ArrayMatcher
{
public static T[] Match<T>(T[] sourceArray, T[] destinationArray)
{
var result = new List<T>();
var currentMethodParamIndex = 0;
var parentItem = default(T);
foreach (var sourceItem in sourceArray)
{
if (currentMethodParamIndex < destinationArray.Length)
{
var destinationItem = destinationArray[currentMethodParamIndex];
if (EqualityComparer<T>.Default.Equals(sourceItem, destinationItem))
{
parentItem = default(T);
currentMethodParamIndex++;
}
else
{
if (parentItem == null)
{
parentItem = destinationItem;
currentMethodParamIndex++;
}
}
}
var resultItem = EqualityComparer<T>.Default.Equals(parentItem, default(T)) ? sourceItem : parentItem;
result.Add(resultItem);
}
return result.ToArray();
}
}
}

1
src/Volo.Abp.AspNetCore.TestBase/Volo.Abp.AspNetCore.TestBase.csproj

@ -13,6 +13,7 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore\Volo.Abp.AspNetCore.csproj" />
<ProjectReference Include="..\Volo.Abp.Http.Client\Volo.Abp.Http.Client.csproj" />
</ItemGroup>
<ItemGroup>

4
src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/AbpAspNetCoreIntegratedTestBase.cs

@ -5,6 +5,7 @@ using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.AspNetCore.TestBase
{
@ -22,8 +23,9 @@ namespace Volo.Abp.AspNetCore.TestBase
var builder = CreateWebHostBuilder();
Server = CreateTestServer(builder);
Client = Server.CreateClient();
ServiceProvider = Server.Host.Services;
ServiceProvider.GetRequiredService<ITestServerAccessor>().Server = Server;
}
protected virtual IWebHostBuilder CreateWebHostBuilder()

2
src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/AbpAspNetCoreTestBaseModule.cs

@ -1,8 +1,10 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.TestBase
{
[DependsOn(typeof(AbpHttpClientModule))]
[DependsOn(typeof(AbpAspNetCoreModule))]
public class AbpAspNetCoreTestBaseModule : AbpModule
{

22
src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/DynamicProxying/AspNetCoreTestDynamicProxyHttpClientFactory.cs

@ -0,0 +1,22 @@
using System.Net.Http;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client.DynamicProxying;
namespace Volo.Abp.AspNetCore.TestBase.DynamicProxying
{
[Dependency(ReplaceServices = true)]
public class AspNetCoreTestDynamicProxyHttpClientFactory : IDynamicProxyHttpClientFactory, ITransientDependency
{
private readonly ITestServerAccessor _testServerAccessor;
public AspNetCoreTestDynamicProxyHttpClientFactory(ITestServerAccessor testServerAccessor)
{
_testServerAccessor = testServerAccessor;
}
public HttpClient Create()
{
return _testServerAccessor.Server.CreateClient();
}
}
}

9
src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/ITestServerAccessor.cs

@ -0,0 +1,9 @@
using Microsoft.AspNetCore.TestHost;
namespace Volo.Abp.AspNetCore.TestBase
{
public interface ITestServerAccessor
{
TestServer Server { get; set; }
}
}

10
src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/TestServerAccessor.cs

@ -0,0 +1,10 @@
using Microsoft.AspNetCore.TestHost;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.TestBase
{
public class TestServerAccessor : ITestServerAccessor, ISingletonDependency
{
public TestServer Server { get; set; }
}
}

4
src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpInterceptorAdapter.cs

@ -17,7 +17,9 @@ namespace Volo.Abp.Castle.DynamicProxy
public void Intercept(IInvocation invocation)
{
if (invocation.MethodInvocationTarget.IsAsync())
var method = invocation.MethodInvocationTarget ?? invocation.Method;
if (method.IsAsync())
{
InterceptAsyncMethod(invocation);
}

2
src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs

@ -15,7 +15,7 @@ namespace Volo.Abp.Castle.DynamicProxy
public object TargetObject => Invocation.InvocationTarget;
public MethodInfo Method => Invocation.MethodInvocationTarget;
public MethodInfo Method => Invocation.MethodInvocationTarget ?? Invocation.Method;
public object ReturnValue
{

42
src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicHttpClientProxyExtensions.cs

@ -0,0 +1,42 @@
using System;
using Castle.DynamicProxy;
using Volo.Abp.Castle.DynamicProxy;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.DynamicProxying;
using Volo.Abp.Http.Modeling;
namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionDynamicHttpClientProxyExtensions
{
private static readonly ProxyGenerator ProxyGeneratorInstance = new ProxyGenerator();
//TODO: AddHttpClientProxies for adding all services from single assembly!
public static IServiceCollection AddHttpClientProxy<T>(this IServiceCollection services, string baseUrl, string moduleName = ModuleApiDescriptionModel.DefaultServiceModuleName)
{
return services.AddHttpClientProxy(typeof(T), baseUrl, moduleName);
}
public static IServiceCollection AddHttpClientProxy(this IServiceCollection services, Type type, string baseUrl, string moduleName = ModuleApiDescriptionModel.DefaultServiceModuleName)
{
services.Configure<AbpHttpClientOptions>(options =>
{
options.HttpClientProxies[type] = new DynamicHttpClientProxyConfig(moduleName, baseUrl, type);
});
var interceptorType = typeof(DynamicHttpProxyInterceptor<>).MakeGenericType(type);
services.AddTransient(interceptorType);
var interceptorAdapterType = typeof(CastleAbpInterceptorAdapter<>).MakeGenericType(interceptorType);
return services.AddTransient(
type,
serviceProvider => ProxyGeneratorInstance
.CreateInterfaceProxyWithoutTarget(
type,
(IInterceptor) serviceProvider.GetRequiredService(interceptorAdapterType)
)
);
}
}
}

19
src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.Http.Client</AssemblyName>
<PackageId>Volo.Abp.Http.Client</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Castle.Core\Volo.Abp.Castle.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Http\Volo.Abp.Http.csproj" />
</ItemGroup>
</Project>

16
src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientOptions.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Http.Client.DynamicProxying;
namespace Volo.Abp.Http.Client
{
public class AbpHttpClientOptions
{
public Dictionary<Type, DynamicHttpClientProxyConfig> HttpClientProxies { get; set; }
public AbpHttpClientOptions()
{
HttpClientProxies = new Dictionary<Type, DynamicHttpClientProxyConfig>();
}
}
}

14
src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpModule.cs

@ -0,0 +1,14 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace Volo.Abp.Http.Client
{
[DependsOn(typeof(AbpHttpModule))]
public class AbpHttpClientModule : AbpModule
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddAssemblyOf<AbpHttpClientModule>();
}
}
}

65
src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApplicationApiDescriptionModelManager.cs

@ -0,0 +1,65 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Nito.AsyncEx;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Modeling;
namespace Volo.Abp.Http.Client.DynamicProxying
{
public class ApplicationApiDescriptionModelManager : IApplicationApiDescriptionModelManager, ISingletonDependency
{
private readonly IDynamicProxyHttpClientFactory _httpClientFactory;
private readonly Dictionary<string, ApplicationApiDescriptionModel> _cache;
private readonly AsyncLock _asyncLock;
public ApplicationApiDescriptionModelManager(IDynamicProxyHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
_cache = new Dictionary<string, ApplicationApiDescriptionModel>();
_asyncLock = new AsyncLock();
}
public async Task<ApplicationApiDescriptionModel> GetAsync(string baseUrl, CancellationToken cancellationToken = default(CancellationToken))
{
using (await _asyncLock.LockAsync(cancellationToken))
{
var model = _cache.GetOrDefault(baseUrl);
if (model == null)
{
_cache[baseUrl] = model = await GetFromServerAsync(baseUrl);
}
return model;
}
}
private async Task<ApplicationApiDescriptionModel> GetFromServerAsync(string baseUrl)
{
using (var client = _httpClientFactory.Create())
{
var response = await client.GetAsync(baseUrl + "api/abp/api-description");
if (!response.IsSuccessStatusCode)
{
throw new AbpException("Remote service returns error!");
}
var content = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject(
content,
typeof(ApplicationApiDescriptionModel),
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
return (ApplicationApiDescriptionModel)result;
}
}
}
}

13
src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DefaultDynamicProxyHttpClientFactory.cs

@ -0,0 +1,13 @@
using System.Net.Http;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Http.Client.DynamicProxying
{
public class DefaultDynamicProxyHttpClientFactory : IDynamicProxyHttpClientFactory, ITransientDependency
{
public HttpClient Create()
{
return new HttpClient();
}
}
}

20
src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpClientProxyConfig.cs

@ -0,0 +1,20 @@
using System;
namespace Volo.Abp.Http.Client.DynamicProxying
{
public class DynamicHttpClientProxyConfig
{
public string BaseUrl { get; }
public string ModuleName { get; }
public Type Type { get; }
public DynamicHttpClientProxyConfig(string moduleName, string baseUrl, Type type)
{
BaseUrl = baseUrl;
ModuleName = moduleName;
Type = type;
}
}
}

133
src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs

@ -0,0 +1,133 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Threading;
namespace Volo.Abp.Http.Client.DynamicProxying
{
public class DynamicHttpProxyInterceptor<TService> : AbpInterceptor, ITransientDependency
{
private static readonly MethodInfo GenericInterceptAsyncMethod;
private readonly IDynamicProxyHttpClientFactory _httpClientFactory;
private readonly IApplicationApiDescriptionModelManager _discoverManager;
private readonly AbpHttpClientOptions _options;
static DynamicHttpProxyInterceptor()
{
GenericInterceptAsyncMethod = typeof(DynamicHttpProxyInterceptor<TService>)
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
.First(m => m.Name == nameof(InterceptAsync) && m.IsGenericMethodDefinition);
}
public DynamicHttpProxyInterceptor(
IDynamicProxyHttpClientFactory httpClientFactory,
IOptions<AbpHttpClientOptions> options,
IApplicationApiDescriptionModelManager discoverManager)
{
_httpClientFactory = httpClientFactory;
_discoverManager = discoverManager;
_options = options.Value;
}
public override void Intercept(IAbpMethodInvocation invocation)
{
AsyncHelper.RunSync(() => InterceptAsync(invocation));
}
public override Task InterceptAsync(IAbpMethodInvocation invocation)
{
invocation.ReturnValue = GenericInterceptAsyncMethod
.MakeGenericMethod(invocation.Method.ReturnType.GenericTypeArguments[0])
.Invoke(this, new object[] { invocation });
return Task.CompletedTask;
}
private async Task<T> InterceptAsync<T>(IAbpMethodInvocation invocation)
{
var config = _options.HttpClientProxies.GetOrDefault(typeof(TService));
if (config == null)
{
throw new AbpException($"Could not get DynamicHttpClientProxyConfig for {typeof(T).FullName}.");
}
var apiDescriptionModel = await _discoverManager.GetAsync(config.BaseUrl);
var action = FindAction(apiDescriptionModel, invocation.Method, config);
using (var client = _httpClientFactory.Create())
{
var response = await client.GetAsync(config.BaseUrl + action.Url);
if (!response.IsSuccessStatusCode)
{
throw new AbpException("Remote service returns error!");
}
var content = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject(
content,
typeof(T),
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
return (T)result;
}
}
private ActionApiDescriptionModel FindAction(ApplicationApiDescriptionModel apiDescriptionModel, MethodInfo method, DynamicHttpClientProxyConfig config)
{
var methodParameters = method.GetParameters().ToArray();
foreach (var module in apiDescriptionModel.Modules.Values)
{
if (module.Name != config.ModuleName)
{
continue;
}
foreach (var controller in module.Controllers.Values)
{
if (controller.Interfaces.All(i => i.TypeAsString != typeof(TService).FullName))
{
continue;
}
foreach (var action in controller.Actions.Values)
{
if (action.NameOnClass == method.Name && action.ParametersOnMethod.Count == methodParameters.Length)
{
var found = true;
for (int i = 0; i < methodParameters.Length; i++)
{
if (action.ParametersOnMethod[i].TypeAsString != methodParameters[i].ParameterType.FullName)
{
found = false;
break;
}
}
if (found)
{
return action;
}
}
}
}
}
throw new AbpException("Could not found remote action for method: " + method);
}
}
}

11
src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IApplicationApiDescriptionModelManager.cs

@ -0,0 +1,11 @@
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Http.Modeling;
namespace Volo.Abp.Http.Client.DynamicProxying
{
public interface IApplicationApiDescriptionModelManager
{
Task<ApplicationApiDescriptionModel> GetAsync(string baseUrl, CancellationToken cancellationToken = default(CancellationToken));
}
}

9
src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IDynamicProxyHttpClientFactory.cs

@ -0,0 +1,9 @@
using System.Net.Http;
namespace Volo.Abp.Http.Client.DynamicProxying
{
public interface IDynamicProxyHttpClientFactory
{
HttpClient Create();
}
}

5
src/Volo.Abp.Http/Volo.Abp.Http.csproj

@ -12,11 +12,8 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Castle.Core\Volo.Abp.Castle.Core.csproj" />
<ProjectReference Include="..\Volo.Abp\Volo.Abp.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Volo\Abp\Http\DynamicProxying\Oas\" />
</ItemGroup>
</Project>

53
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class ActionApiDescriptionModel
{
public string UniqueName { get; set; }
public string NameOnClass { get; set; }
public string HttpMethod { get; set; }
public string Url { get; set; }
public IList<MethodParameterApiDescriptionModel> ParametersOnMethod { get; set; }
public IList<ParameterApiDescriptionModel> Parameters { get; set; }
public ReturnValueApiDescriptionModel ReturnValue { get; set; }
private ActionApiDescriptionModel()
{
}
public static ActionApiDescriptionModel Create(MethodInfo method, string uniqueName, string url, string httpMethod = null)
{
return new ActionApiDescriptionModel
{
UniqueName = uniqueName,
NameOnClass = method.Name,
Url = url,
HttpMethod = httpMethod,
ReturnValue = new ReturnValueApiDescriptionModel(method.ReturnType),
Parameters = new List<ParameterApiDescriptionModel>(),
ParametersOnMethod = method
.GetParameters()
.Select(MethodParameterApiDescriptionModel.Create)
.ToList()
};
}
public ParameterApiDescriptionModel AddParameter(ParameterApiDescriptionModel parameter)
{
Parameters.Add(parameter);
return parameter;
}
}
}

57
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ApplicationApiDescriptionModel.cs

@ -0,0 +1,57 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class ApplicationApiDescriptionModel
{
public IDictionary<string, ModuleApiDescriptionModel> Modules { get; set; }
private ApplicationApiDescriptionModel()
{
}
public static ApplicationApiDescriptionModel Create()
{
return new ApplicationApiDescriptionModel
{
//TODO: Why ConcurrentDictionary?
Modules = new ConcurrentDictionary<string, ModuleApiDescriptionModel>()
};
}
public ModuleApiDescriptionModel AddModule(ModuleApiDescriptionModel module)
{
if (Modules.ContainsKey(module.Name))
{
throw new AbpException("There is already a module with same name: " + module.Name);
}
return Modules[module.Name] = module;
}
public ModuleApiDescriptionModel GetOrAddModule(string name)
{
return Modules.GetOrAdd(name, () => ModuleApiDescriptionModel.Create(name));
}
public ApplicationApiDescriptionModel CreateSubModel(string[] modules = null, string[] controllers = null, string[] actions = null)
{
var subModel = new ApplicationApiDescriptionModel();
foreach (var module in Modules.Values)
{
if (modules == null || modules.Contains(module.Name))
{
subModel.AddModule(module.CreateSubModel(controllers, actions));
}
}
return subModel;
}
}
}

70
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class ControllerApiDescriptionModel
{
public string ControllerName { get; set; }
public string TypeAsString { get; set; }
public List<ControllerInterfaceApiDescriptionModel> Interfaces { get; set; }
public Dictionary<string, ActionApiDescriptionModel> Actions { get; set; }
private ControllerApiDescriptionModel()
{
}
public static ControllerApiDescriptionModel Create(string controllerName, Type type)
{
return new ControllerApiDescriptionModel
{
ControllerName = controllerName,
TypeAsString = type.FullName,
Actions = new Dictionary<string, ActionApiDescriptionModel>(),
Interfaces = type
.GetInterfaces()
.Select(ControllerInterfaceApiDescriptionModel.Create)
.ToList()
};
}
public ActionApiDescriptionModel AddAction(ActionApiDescriptionModel action)
{
if (Actions.ContainsKey(action.UniqueName))
{
throw new AbpException(
$"Can not add more than one action with same name to the same controller. Controller: {ControllerName}, Action: {action.UniqueName}."
);
}
return Actions[action.UniqueName] = action;
}
public ControllerApiDescriptionModel CreateSubModel(string[] actions)
{
var subModel = new ControllerApiDescriptionModel
{
TypeAsString = TypeAsString,
Interfaces = Interfaces,
ControllerName = ControllerName,
Actions = new Dictionary<string, ActionApiDescriptionModel>()
};
foreach (var action in Actions.Values)
{
if (actions == null || actions.Contains(action.UniqueName))
{
subModel.AddAction(action);
}
}
return subModel;
}
}
}

23
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerInterfaceApiDescriptionModel.cs

@ -0,0 +1,23 @@
using System;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class ControllerInterfaceApiDescriptionModel
{
public string TypeAsString { get; set; }
private ControllerInterfaceApiDescriptionModel()
{
}
public static ControllerInterfaceApiDescriptionModel Create(Type type)
{
return new ControllerInterfaceApiDescriptionModel
{
TypeAsString = type.FullName
};
}
}
}

7
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/IApiDescriptionModelProvider.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.Http.Modeling
{
public interface IApiDescriptionModelProvider
{
ApplicationApiDescriptionModel CreateModel();
}
}

33
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/MethodParameterApiDescriptionModel.cs

@ -0,0 +1,33 @@
using System;
using System.Reflection;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class MethodParameterApiDescriptionModel
{
public string Name { get; set; }
public string TypeAsString { get; set; }
public bool IsOptional { get; set; }
public object DefaultValue { get; set; }
private MethodParameterApiDescriptionModel()
{
}
public static MethodParameterApiDescriptionModel Create(ParameterInfo parameterInfo)
{
return new MethodParameterApiDescriptionModel
{
Name = parameterInfo.Name,
TypeAsString = parameterInfo.ParameterType.FullName,
IsOptional = parameterInfo.IsOptional,
DefaultValue = parameterInfo.HasDefaultValue ? parameterInfo.DefaultValue : null
};
}
}
}

63
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class ModuleApiDescriptionModel
{
/// <summary>
/// "app".
/// </summary>
public const string DefaultServiceModuleName = "app";
public string Name { get; set; }
public IDictionary<string, ControllerApiDescriptionModel> Controllers { get; set; }
private ModuleApiDescriptionModel()
{
}
public static ModuleApiDescriptionModel Create(string name)
{
return new ModuleApiDescriptionModel
{
Name = name,
Controllers = new Dictionary<string, ControllerApiDescriptionModel>()
};
}
public ControllerApiDescriptionModel AddController(ControllerApiDescriptionModel controller)
{
if (Controllers.ContainsKey(controller.ControllerName))
{
throw new AbpException($"There is already a controller with name: {controller.ControllerName} in module: {Name}");
}
return Controllers[controller.ControllerName] = controller;
}
public ControllerApiDescriptionModel GetOrAddController(string name, Type type)
{
return Controllers.GetOrAdd(name, () => ControllerApiDescriptionModel.Create(name, type));
}
public ModuleApiDescriptionModel CreateSubModel(string[] controllers, string[] actions)
{
var subModel = ModuleApiDescriptionModel.Create(Name);
foreach (var controller in Controllers.Values)
{
if (controllers == null || controllers.Contains(controller.ControllerName))
{
subModel.AddController(controller.CreateSubModel(actions));
}
}
return subModel;
}
}
}

44
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs

@ -0,0 +1,44 @@
using System;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class ParameterApiDescriptionModel
{
public string NameOnMethod { get; set; }
public string Name { get; set; }
public Type Type { get; set; }
public string TypeAsString { get; set; }
public bool IsOptional { get; set; }
public object DefaultValue { get; set; }
public string[] ConstraintTypes { get; set; }
public string BindingSourceId { get; set; }
private ParameterApiDescriptionModel()
{
}
public static ParameterApiDescriptionModel Create(string name, string nameOnMethod, Type type, bool isOptional = false, object defaultValue = null, string[] constraintTypes = null, string bindingSourceId = null)
{
return new ParameterApiDescriptionModel
{
Name = name,
NameOnMethod = nameOnMethod,
Type = type,
TypeAsString = type.FullName,
IsOptional = isOptional,
DefaultValue = defaultValue,
ConstraintTypes = constraintTypes,
BindingSourceId = bindingSourceId
};
}
}
}

17
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs

@ -0,0 +1,17 @@
using System;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class ReturnValueApiDescriptionModel
{
public Type Type { get; }
public string TypeAsString { get; }
public ReturnValueApiDescriptionModel(Type type)
{
Type = type;
TypeAsString = type.FullName;
}
}
}

13
src/Volo.Abp/System/AbpTypeExtensions.cs

@ -1,13 +0,0 @@
using System.Reflection;
namespace System
{
public static class AbpTypeExtensions
{
//TODO: This method can be removed because not needed anymore!
public static Assembly GetAssembly(this Type type)
{
return type.GetTypeInfo().Assembly;
}
}
}

1
src/Volo.Abp/Volo.Abp.csproj

@ -16,6 +16,7 @@
<PackageReference Include="Microsoft.Extensions.Options" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Nito.AsyncEx.Coordination" Version="1.0.2" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.4.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.4.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />

2
src/Volo.Abp/Volo/Abp/Modularity/AbpModuleDescriptor.cs

@ -33,7 +33,7 @@ namespace Volo.Abp.Modularity
}
Type = type;
Assembly = type.GetAssembly();
Assembly = type.Assembly;
Instance = instance;
IsLoadedAsPlugIn = isLoadedAsPlugIn;

2
src/Volo.Abp/Volo/Abp/Reflection/AssemblyFinder.cs

@ -30,7 +30,7 @@ namespace Volo.Abp.Reflection
foreach (var module in _moduleContainer.Modules)
{
assemblies.Add(module.Type.GetAssembly());
assemblies.Add(module.Type.Assembly);
}
return assemblies.Distinct().ToImmutableList();

2
src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs

@ -32,7 +32,7 @@ namespace Volo.Abp.Uow
{
if (!UnitOfWorkHelper.IsUnitOfWorkMethod(invocation.Method))
{
invocation.Proceed();
await invocation.ProceedAsync();
return;
}

19
test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Utils/ArrayMacther_Tests.cs

@ -0,0 +1,19 @@
using Xunit;
namespace Volo.Abp.AspNetCore.Mvc.Utils
{
public class ArrayMacther_Tests
{
[Theory]
[InlineData(
new[] { "p1", "p2.a1", "p2.a2.b1", "p2.a2.b2", "p3", "p4", "p5.c1", "p5.c2" },
new[] { "p1", "p2", "p3", "p4", "p5" },
new[] { "p1", "p2", "p2", "p2", "p3", "p4", "p5", "p5" })
]
public void Should_Find_Correct_Items(string[] sourceArray, string[] destinationArray, string[] expectedArray)
{
var result = ArrayMatcher.Match(sourceArray, destinationArray);
Assert.Equal(expectedArray, result);
}
}
}

2
test/Volo.Abp.AspNetCore.Tests/Volo/Abp/AspNetCore/AppTestBase.cs

@ -6,7 +6,7 @@ using Newtonsoft.Json.Serialization;
using Shouldly;
using Volo.Abp.AspNetCore.TestBase;
namespace Volo.Abp.AspNetCore.App
namespace Volo.Abp.AspNetCore
{
public abstract class AbpAspNetCoreTestBase<TStartup> : AbpAspNetCoreIntegratedTestBase<TStartup>
where TStartup : class

4
test/Volo.Abp.Http.Tests/Volo.Abp.Http.Tests.csproj → test/Volo.Abp.Http.Client.Tests/Volo.Abp.Http.Client.Tests.csproj

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>Volo.Abp.Http.Tests</AssemblyName>
<PackageId>Volo.Abp.Http.Tests</PackageId>
<PackageId>Volo.Abp.Http.Client.Tests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
@ -12,7 +12,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.Http\Volo.Abp.Http.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Http.Client\Volo.Abp.Http.Client.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.Tests\Volo.Abp.AspNetCore.Mvc.Tests.csproj" />
</ItemGroup>

1
test/Volo.Abp.Http.Tests/Volo/Abp/Http/AbpHttpTestBase.cs → test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpTestBase.cs

@ -1,3 +1,4 @@
using Volo.Abp.AspNetCore;
using Volo.Abp.AspNetCore.App;
namespace Volo.Abp.Http

7
test/Volo.Abp.Http.Tests/Volo/Abp/Http/AbpHttpTestModule.cs → test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpTestModule.cs

@ -1,15 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.App;
using Volo.Abp.Http.Client;
using Volo.Abp.Modularity;
using Volo.Abp.TestApp.Application;
namespace Volo.Abp.Http
{
[DependsOn(typeof(AbpAspNetCoreMvcTestModule), typeof(AbpHttpModule))]
[DependsOn(typeof(AbpAspNetCoreMvcTestModule), typeof(AbpHttpClientModule))]
public class AbpHttpTestModule : AbpModule
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddAssemblyOf<AbpAspNetCoreMvcTestModule>();
services.AddAssemblyOf<AbpHttpTestModule>();
services.AddHttpClientProxy<IPeopleAppService>("/");
}
}
}

1
test/Volo.Abp.Http.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs → test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs

@ -13,7 +13,6 @@ namespace Volo.Abp.Http.DynamicProxying
public PersonAppServiceClientProxy_Tests()
{
//TODO: Should actually test the proxy!
_peopleAppService = ServiceProvider.GetRequiredService<IPeopleAppService>();
}

0
test/Volo.Abp.Http.Tests/Volo/Abp/Http/Startup.cs → test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Startup.cs

2
test/Volo.Abp.Tests/Volo/Abp/Reflection/AssemblyFinder_Tests.cs

@ -30,7 +30,7 @@ namespace Volo.Abp.Reflection
foreach (var moduleType in moduleTypes)
{
assemblyFinder.Assemblies.ShouldContain(moduleType.GetAssembly());
assemblyFinder.Assemblies.ShouldContain(moduleType.Assembly);
}
}

4
test/Volo.Abp.Tests/Volo/Abp/Reflection/TypeFinder_Tests.cs

@ -18,8 +18,8 @@ namespace Volo.Abp.Reflection
var fakeAssemblyFinder = Substitute.For<IAssemblyFinder>();
fakeAssemblyFinder.Assemblies.Returns(new List<Assembly>
{
typeof(AbpKernelModule).GetAssembly(),
typeof(TypeFinder_Tests).GetAssembly()
typeof(AbpKernelModule).Assembly,
typeof(TypeFinder_Tests).Assembly
});
//Act

Loading…
Cancel
Save