using System;
using System.Linq;
using System.Reflection;
using Castle.DynamicProxy;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Castle.DynamicProxy;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.DynamicProxying;
using Volo.Abp.Http.Client.Proxying;
using Volo.Abp.Validation;
namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionHttpClientProxyExtensions
{
private static readonly ProxyGenerator ProxyGeneratorInstance = new ProxyGenerator();
///
/// Registers Static HTTP Client Proxies for all public interfaces
/// extend the interface in the
/// given .
///
/// Service collection
/// The assembly containing the service interfaces
///
/// The name of the remote service configuration to be used by the Static HTTP Client proxies.
/// See .
///
public static IServiceCollection AddStaticHttpClientProxies(
[NotNull] this IServiceCollection services,
[NotNull] Assembly assembly,
[NotNull] string remoteServiceConfigurationName = RemoteServiceConfigurationDictionary.DefaultName)
{
Check.NotNull(services, nameof(assembly));
var serviceTypes = assembly.GetTypes().Where(IsSuitableForClientProxying).ToArray();
foreach (var serviceType in serviceTypes)
{
AddHttpClientFactory(services, remoteServiceConfigurationName);
services.Configure(options =>
{
options.HttpClientProxies[serviceType] = new HttpClientProxyConfig(serviceType, remoteServiceConfigurationName);
});
}
return services;
}
///
/// Registers HTTP Client Proxies for all public interfaces
/// extend the interface in the
/// given .
///
/// Service collection
/// The assembly containing the service interfaces
///
/// The name of the remote service configuration to be used by the HTTP Client proxies.
/// See .
///
///
/// True, to register the HTTP client proxy as the default implementation for the services.
///
public static IServiceCollection AddHttpClientProxies(
[NotNull] this IServiceCollection services,
[NotNull] Assembly assembly,
[NotNull] string remoteServiceConfigurationName = RemoteServiceConfigurationDictionary.DefaultName,
bool asDefaultServices = true)
{
Check.NotNull(services, nameof(assembly));
var serviceTypes = assembly.GetTypes().Where(IsSuitableForClientProxying).ToArray();
foreach (var serviceType in serviceTypes)
{
services.AddHttpClientProxy(
serviceType,
remoteServiceConfigurationName,
asDefaultServices
);
}
return services;
}
///
/// Registers HTTP Client Proxy for given service type .
///
/// Type of the service
/// Service collection
///
/// The name of the remote service configuration to be used by the HTTP Client proxy.
/// See .
///
///
/// True, to register the HTTP client proxy as the default implementation for the service .
///
public static IServiceCollection AddHttpClientProxy(
[NotNull] this IServiceCollection services,
[NotNull] string remoteServiceConfigurationName = RemoteServiceConfigurationDictionary.DefaultName,
bool asDefaultService = true)
{
return services.AddHttpClientProxy(
typeof(T),
remoteServiceConfigurationName,
asDefaultService
);
}
///
/// Registers HTTP Client Proxy for given service .
///
/// Service collection
/// Type of the service
///
/// The name of the remote service configuration to be used by the HTTP Client proxy.
/// See .
///
///
/// True, to register the HTTP client proxy as the default implementation for the service .
///
public static IServiceCollection AddHttpClientProxy(
[NotNull] this IServiceCollection services,
[NotNull] Type type,
[NotNull] string remoteServiceConfigurationName = RemoteServiceConfigurationDictionary.DefaultName,
bool asDefaultService = true)
{
Check.NotNull(services, nameof(services));
Check.NotNull(type, nameof(type));
Check.NotNullOrWhiteSpace(remoteServiceConfigurationName, nameof(remoteServiceConfigurationName));
AddHttpClientFactory(services, remoteServiceConfigurationName);
services.Configure(options =>
{
options.HttpClientProxies[type] = new HttpClientProxyConfig(type, remoteServiceConfigurationName);
});
var interceptorType = typeof(DynamicHttpProxyInterceptor<>).MakeGenericType(type);
services.AddTransient(interceptorType);
var interceptorAdapterType = typeof(AbpAsyncDeterminationInterceptor<>).MakeGenericType(interceptorType);
var validationInterceptorAdapterType =
typeof(AbpAsyncDeterminationInterceptor<>).MakeGenericType(typeof(ValidationInterceptor));
if (asDefaultService)
{
services.AddTransient(
type,
serviceProvider => ProxyGeneratorInstance
.CreateInterfaceProxyWithoutTarget(
type,
(IInterceptor)serviceProvider.GetRequiredService(validationInterceptorAdapterType),
(IInterceptor)serviceProvider.GetRequiredService(interceptorAdapterType)
)
);
}
services.AddTransient(
typeof(IHttpClientProxy<>).MakeGenericType(type),
serviceProvider =>
{
var service = ProxyGeneratorInstance
.CreateInterfaceProxyWithoutTarget(
type,
(IInterceptor)serviceProvider.GetRequiredService(validationInterceptorAdapterType),
(IInterceptor)serviceProvider.GetRequiredService(interceptorAdapterType)
);
return Activator.CreateInstance(
typeof(HttpClientProxy<>).MakeGenericType(type),
service
);
});
return services;
}
private static IServiceCollection AddHttpClientFactory(
[NotNull] this IServiceCollection services,
[NotNull] string remoteServiceConfigurationName = RemoteServiceConfigurationDictionary.DefaultName)
{
var preOptions = services.ExecutePreConfiguredActions();
if (preOptions.ConfiguredProxyClients.Contains(remoteServiceConfigurationName))
{
return services;
}
var clientBuilder = services.AddHttpClient(remoteServiceConfigurationName, (provider, client) =>
{
foreach (var clientBuildAction in preOptions.ProxyClientActions)
{
clientBuildAction(remoteServiceConfigurationName, provider, client);
}
});
foreach (var clientBuildAction in preOptions.ProxyClientBuildActions)
{
clientBuildAction(remoteServiceConfigurationName, clientBuilder);
}
services.PreConfigure(options =>
{
options.ConfiguredProxyClients.Add(remoteServiceConfigurationName);
});
return services;
}
///
/// Checks wether the type is suitable to use with the proxying.
/// Currently the type is checked statically against some fixed conditions.
///
/// Type to check
/// True, if the type is suitable for proxying. Otherwise false.
private static bool IsSuitableForClientProxying(Type type)
{
//TODO: Add option to change type filter
return type.IsInterface
&& type.IsPublic
&& !type.IsGenericType
&& typeof(IRemoteService).IsAssignableFrom(type);
}
}
}