@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq ;
using System.Net.Http ;
using System.Reflection ;
using System.Threading ;
using System.Threading.Tasks ;
using Microsoft.Extensions.Logging ;
using Microsoft.Extensions.Logging.Abstractions ;
@ -15,21 +16,25 @@ using Volo.Abp.Http.ProxyScripting.Generators;
using Volo.Abp.Json ;
using Volo.Abp.Reflection ;
using Volo.Abp.Threading ;
using Volo.Abp.Tracing ;
namespace Volo.Abp.Http.Client.DynamicProxying
{
//TODO: Somehow capture cancellationtoken and pass to other methods...?
public class DynamicHttpProxyInterceptor < TService > : AbpInterceptor , ITransientDependency
{
private static MethodInfo GenericInterceptAsyncMethod { get ; }
// ReSharper disable once StaticMemberInGenericType
protected static MethodInfo GenericInterceptAsyncMethod { get ; }
protected ICancellationTokenProvider CancellationTokenProvider { get ; }
protected ICorrelationIdProvider CorrelationIdProvider { get ; }
protected CorrelationIdOptions CorrelationIdOptions { get ; }
protected IDynamicProxyHttpClientFactory HttpClientFactory { get ; }
protected IApiDescriptionFinder ApiDescriptionFinder { get ; }
protected RemoteServiceOptions RemoteServiceOptions { get ; }
protected AbpHttpClientOptions ClientOptions { get ; }
protected IJsonSerializer JsonSerializer { get ; }
protected IRemoteServiceHttpClientAuthenticator ClientAuthenticator { get ; }
private readonly IDynamicProxyHttpClientFactory _ httpClientFactory ;
private readonly IApiDescriptionFinder _ apiDescriptionFinder ;
private readonly RemoteServiceOptions _ remoteServiceOptions ;
private readonly AbpHttpClientOptions _ clientOptions ;
private readonly IJsonSerializer _ jsonSerializer ;
private readonly IRemoteServiceHttpClientAuthenticator _ clientAuthenticator ;
public ILogger < DynamicHttpProxyInterceptor < TService > > Logger { get ; set ; }
@ -46,14 +51,20 @@ namespace Volo.Abp.Http.Client.DynamicProxying
IOptionsSnapshot < RemoteServiceOptions > remoteServiceOptions ,
IApiDescriptionFinder apiDescriptionFinder ,
IJsonSerializer jsonSerializer ,
IRemoteServiceHttpClientAuthenticator clientAuthenticator )
IRemoteServiceHttpClientAuthenticator clientAuthenticator ,
ICancellationTokenProvider cancellationTokenProvider ,
ICorrelationIdProvider correlationIdProvider ,
IOptions < CorrelationIdOptions > correlationIdOptions )
{
_ httpClientFactory = httpClientFactory ;
_ apiDescriptionFinder = apiDescriptionFinder ;
_ jsonSerializer = jsonSerializer ;
_ clientAuthenticator = clientAuthenticator ;
_ clientOptions = clientOptions . Value ;
_ remoteServiceOptions = remoteServiceOptions . Value ;
CancellationTokenProvider = cancellationTokenProvider ;
CorrelationIdProvider = correlationIdProvider ;
CorrelationIdOptions = correlationIdOptions . Value ;
HttpClientFactory = httpClientFactory ;
ApiDescriptionFinder = apiDescriptionFinder ;
JsonSerializer = jsonSerializer ;
ClientAuthenticator = clientAuthenticator ;
ClientOptions = clientOptions . Value ;
RemoteServiceOptions = remoteServiceOptions . Value ;
Logger = NullLogger < DynamicHttpProxyInterceptor < TService > > . Instance ;
}
@ -62,11 +73,11 @@ namespace Volo.Abp.Http.Client.DynamicProxying
{
if ( invocation . Method . ReturnType = = typeof ( void ) )
{
AsyncHelper . RunSync ( ( ) = > MakeRequest ( invocation ) ) ;
AsyncHelper . RunSync ( ( ) = > MakeRequestAsync ( invocation ) ) ;
}
else
{
var responseAsString = AsyncHelper . RunSync ( ( ) = > MakeRequest ( invocation ) ) ;
var responseAsString = AsyncHelper . RunSync ( ( ) = > MakeRequestAsync ( invocation ) ) ;
//TODO: Think on that
if ( TypeHelper . IsPrimitiveExtended ( invocation . Method . ReturnType , true ) )
@ -75,7 +86,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying
}
else
{
invocation . ReturnValue = _ j sonSerializer. Deserialize (
invocation . ReturnValue = J sonSerializer. Deserialize (
invocation . Method . ReturnType ,
responseAsString
) ;
@ -87,7 +98,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying
{
if ( invocation . Method . ReturnType . GenericTypeArguments . IsNullOrEmpty ( ) )
{
return MakeRequest ( invocation ) ;
return MakeRequestAsync ( invocation ) ;
}
invocation . ReturnValue = GenericInterceptAsyncMethod
@ -99,7 +110,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying
private async Task < T > MakeRequestAndGetResultAsync < T > ( IAbpMethodInvocation invocation )
{
var responseAsString = await MakeRequest ( invocation ) ;
var responseAsString = await MakeRequestAsync ( invocation ) ;
//TODO: Think on that
if ( TypeHelper . IsPrimitiveExtended ( typeof ( T ) , true ) )
@ -107,28 +118,28 @@ namespace Volo.Abp.Http.Client.DynamicProxying
return ( T ) Convert . ChangeType ( responseAsString , typeof ( T ) ) ;
}
return _ j sonSerializer. Deserialize < T > ( responseAsString ) ;
return J sonSerializer. Deserialize < T > ( responseAsString ) ;
}
private async Task < string > MakeRequest ( IAbpMethodInvocation invocation )
private async Task < string > MakeRequestAsync ( IAbpMethodInvocation invocation )
{
using ( var client = _ h ttpClientFactory. Create ( ) )
using ( var client = H ttpClientFactory. Create ( ) )
{
var clientConfig = _ c lientOptions. HttpClientProxies . GetOrDefault ( typeof ( TService ) ) ? ? throw new AbpException ( $"Could not get DynamicHttpClientProxyConfig for {typeof(TService).FullName}." ) ;
var remoteServiceConfig = _ r emoteServiceOptions. RemoteServices . GetConfigurationOrDefault ( clientConfig . RemoteServiceName ) ;
var clientConfig = C lientOptions. HttpClientProxies . GetOrDefault ( typeof ( TService ) ) ? ? throw new AbpException ( $"Could not get DynamicHttpClientProxyConfig for {typeof(TService).FullName}." ) ;
var remoteServiceConfig = R emoteServiceOptions. RemoteServices . GetConfigurationOrDefault ( clientConfig . RemoteServiceName ) ;
var action = await _ a piDescriptionFinder. FindActionAsync ( remoteServiceConfig . BaseUrl , typeof ( TService ) , invocation . Method ) ;
var action = await A piDescriptionFinder. FindActionAsync ( remoteServiceConfig . BaseUrl , typeof ( TService ) , invocation . Method ) ;
var apiVersion = GetApiVersionInfo ( action ) ;
var url = remoteServiceConfig . BaseUrl + UrlBuilder . GenerateUrlWithParameters ( action , invocation . ArgumentsDictionary , apiVersion ) ;
var requestMessage = new HttpRequestMessage ( action . GetHttpMethod ( ) , url )
{
Content = RequestPayloadBuilder . BuildContent ( action , invocation . ArgumentsDictionary , _ j sonSerializer, apiVersion )
Content = RequestPayloadBuilder . BuildContent ( action , invocation . ArgumentsDictionary , J sonSerializer, apiVersion )
} ;
AddHeaders ( invocation , action , requestMessage , apiVersion ) ;
await _ c lientAuthenticator. Authenticate (
await C lientAuthenticator. Authenticate (
new RemoteServiceHttpClientAuthenticateContext (
client ,
requestMessage ,
@ -137,7 +148,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying
)
) ;
var response = await client . SendAsync ( requestMessage ) ;
var response = await client . SendAsync ( requestMessage , GetCancellationToken ( ) ) ;
if ( ! response . IsSuccessStatusCode )
{
@ -176,8 +187,9 @@ namespace Volo.Abp.Http.Client.DynamicProxying
return action . SupportedVersions . Last ( ) ; //TODO: Ensure to get the latest version!
}
private static void AddHeaders ( IAbpMethodInvocation invocation , ActionApiDescriptionModel action , HttpRequestMessage requestMessage , ApiVersionInfo apiVersion )
protected virtual void AddHeaders ( IAbpMethodInvocation invocation , ActionApiDescriptionModel action , HttpRequestMessage requestMessage , ApiVersionInfo apiVersion )
{
//API Version
if ( ! apiVersion . Version . IsNullOrEmpty ( ) )
{
//TODO: What about other media types?
@ -186,8 +198,8 @@ namespace Volo.Abp.Http.Client.DynamicProxying
requestMessage . Headers . Add ( "api-version" , apiVersion . Version ) ;
}
//Header parameters
var headers = action . Parameters . Where ( p = > p . BindingSourceId = = ParameterBindingSources . Header ) . ToArray ( ) ;
foreach ( var headerParameter in headers )
{
var value = HttpActionParameterHelper . FindParameterValue ( invocation . ArgumentsDictionary , headerParameter ) ;
@ -196,22 +208,25 @@ namespace Volo.Abp.Http.Client.DynamicProxying
requestMessage . Headers . Add ( headerParameter . Name , value . ToString ( ) ) ;
}
}
//CorrelationId
requestMessage . Headers . Add ( CorrelationIdOptions . HttpHeaderName , CorrelationIdProvider . Get ( ) ) ;
}
private string GetConfiguredApiVersion ( )
{
var clientConfig = _ c lientOptions. HttpClientProxies . GetOrDefault ( typeof ( TService ) )
var clientConfig = C lientOptions. HttpClientProxies . GetOrDefault ( typeof ( TService ) )
? ? throw new AbpException ( $"Could not get DynamicHttpClientProxyConfig for {typeof(TService).FullName}." ) ;
return _ r emoteServiceOptions. RemoteServices . GetOrDefault ( clientConfig . RemoteServiceName ) ? . Version
? ? _ r emoteServiceOptions. RemoteServices . Default ? . Version ;
return R emoteServiceOptions. RemoteServices . GetOrDefault ( clientConfig . RemoteServiceName ) ? . Version
? ? R emoteServiceOptions. RemoteServices . Default ? . Version ;
}
private async Task ThrowExceptionForResponseAsync ( HttpResponseMessage response )
{
if ( response . Headers . Contains ( AbpHttpConsts . AbpErrorFormat ) )
{
var errorResponse = _ j sonSerializer. Deserialize < RemoteServiceErrorResponse > (
var errorResponse = J sonSerializer. Deserialize < RemoteServiceErrorResponse > (
await response . Content . ReadAsStringAsync ( )
) ;
@ -220,5 +235,10 @@ namespace Volo.Abp.Http.Client.DynamicProxying
throw new AbpException ( $"Remote service returns error! HttpStatusCode: {response.StatusCode}, ReasonPhrase: {response.ReasonPhrase}" ) ;
}
protected virtual CancellationToken GetCancellationToken ( )
{
return CancellationTokenProvider . Token ;
}
}
}