Browse Source

Use CorrelationId and CancellationToken in the DynamicHttpProxyInterceptor

pull/818/head
Halil ibrahim Kalkan 8 years ago
parent
commit
df9325a329
  1. 7
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AspNetCoreCorrelationIdOptions.cs
  2. 8
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AspNetCoreCorrelationIdProvider.cs
  3. 7
      framework/src/Volo.Abp.Core/Volo/Abp/Tracing/CorrelationIdOptions.cs
  4. 18
      framework/src/Volo.Abp.Core/Volo/Abp/Tracing/DefaultCorrelationIdProvider.cs
  5. 5
      framework/src/Volo.Abp.Core/Volo/Abp/Tracing/ICorrelationIdProvider.cs
  6. 12
      framework/src/Volo.Abp.Core/Volo/Abp/Tracing/NullCorrelationIdProvider.cs
  7. 1
      framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj
  8. 9
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs
  9. 92
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs
  10. 3
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs

7
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AspNetCoreCorrelationIdOptions.cs

@ -1,7 +0,0 @@
namespace Volo.Abp.AspNetCore.Tracing
{
public class AspNetCoreCorrelationIdOptions
{
public string HeaderName { get; set; } = "X-Correlation-Id";
}
}

8
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AspNetCoreCorrelationIdProvider.cs

@ -10,11 +10,11 @@ namespace Volo.Abp.AspNetCore.Tracing
public class AspNetCoreCorrelationIdProvider : ICorrelationIdProvider, ITransientDependency public class AspNetCoreCorrelationIdProvider : ICorrelationIdProvider, ITransientDependency
{ {
protected IHttpContextAccessor HttpContextAccessor { get; } protected IHttpContextAccessor HttpContextAccessor { get; }
protected AspNetCoreCorrelationIdOptions Options { get; } protected CorrelationIdOptions Options { get; }
public AspNetCoreCorrelationIdProvider( public AspNetCoreCorrelationIdProvider(
IHttpContextAccessor httpContextAccessor, IHttpContextAccessor httpContextAccessor,
IOptions<AspNetCoreCorrelationIdOptions> options) IOptions<CorrelationIdOptions> options)
{ {
HttpContextAccessor = httpContextAccessor; HttpContextAccessor = httpContextAccessor;
Options = options.Value; Options = options.Value;
@ -29,12 +29,12 @@ namespace Volo.Abp.AspNetCore.Tracing
lock (HttpContextAccessor.HttpContext.Request.Headers) lock (HttpContextAccessor.HttpContext.Request.Headers)
{ {
string correlationId = HttpContextAccessor.HttpContext.Request.Headers[Options.HeaderName]; string correlationId = HttpContextAccessor.HttpContext.Request.Headers[Options.HttpHeaderName];
if (correlationId.IsNullOrEmpty()) if (correlationId.IsNullOrEmpty())
{ {
correlationId = CreateNewCorrelationId(); correlationId = CreateNewCorrelationId();
HttpContextAccessor.HttpContext.Request.Headers[Options.HeaderName] = correlationId; HttpContextAccessor.HttpContext.Request.Headers[Options.HttpHeaderName] = correlationId;
} }
return correlationId; return correlationId;

7
framework/src/Volo.Abp.Core/Volo/Abp/Tracing/CorrelationIdOptions.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.Tracing
{
public class CorrelationIdOptions
{
public string HttpHeaderName { get; set; } = "X-Correlation-Id";
}
}

18
framework/src/Volo.Abp.Core/Volo/Abp/Tracing/DefaultCorrelationIdProvider.cs

@ -0,0 +1,18 @@
using System;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Tracing
{
public class DefaultCorrelationIdProvider : ICorrelationIdProvider, ISingletonDependency
{
public string Get()
{
return CreateNewCorrelationId();
}
protected virtual string CreateNewCorrelationId()
{
return Guid.NewGuid().ToString("N");
}
}
}

5
framework/src/Volo.Abp.Core/Volo/Abp/Tracing/ICorrelationIdProvider.cs

@ -1,7 +1,10 @@
namespace Volo.Abp.Tracing using JetBrains.Annotations;
namespace Volo.Abp.Tracing
{ {
public interface ICorrelationIdProvider public interface ICorrelationIdProvider
{ {
[NotNull]
string Get(); string Get();
} }
} }

12
framework/src/Volo.Abp.Core/Volo/Abp/Tracing/NullCorrelationIdProvider.cs

@ -1,12 +0,0 @@
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Tracing
{
public class NullCorrelationIdProvider : ICorrelationIdProvider, ISingletonDependency
{
public string Get()
{
return null;
}
}
}

1
framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj

@ -20,6 +20,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Abp.Castle.Core\Volo.Abp.Castle.Core.csproj" /> <ProjectReference Include="..\Volo.Abp.Castle.Core\Volo.Abp.Castle.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Http\Volo.Abp.Http.csproj" /> <ProjectReference Include="..\Volo.Abp.Http\Volo.Abp.Http.csproj" />
<ProjectReference Include="..\Volo.Abp.Threading\Volo.Abp.Threading.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

9
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs

@ -1,17 +1,20 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Castle; using Volo.Abp.Castle;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace Volo.Abp.Http.Client namespace Volo.Abp.Http.Client
{ {
[DependsOn(typeof(AbpHttpModule))] [DependsOn(
[DependsOn(typeof(AbpCastleCoreModule))] typeof(AbpHttpModule),
typeof(AbpCastleCoreModule),
typeof(AbpThreadingModule)
)]
public class AbpHttpClientModule : AbpModule public class AbpHttpClientModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
Configure<RemoteServiceOptions>(configuration); Configure<RemoteServiceOptions>(configuration);
} }
} }

92
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs

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

3
framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs

@ -15,7 +15,6 @@ namespace Volo.Abp.IdentityModel
public class IdentityModelHttpClientAuthenticator : IIdentityModelHttpClientAuthenticator, ITransientDependency public class IdentityModelHttpClientAuthenticator : IIdentityModelHttpClientAuthenticator, ITransientDependency
{ {
public ILogger<IdentityModelHttpClientAuthenticator> Logger { get; set; } public ILogger<IdentityModelHttpClientAuthenticator> Logger { get; set; }
protected IdentityClientOptions ClientOptions { get; } protected IdentityClientOptions ClientOptions { get; }
public IdentityModelHttpClientAuthenticator( public IdentityModelHttpClientAuthenticator(
@ -84,6 +83,8 @@ namespace Volo.Abp.IdentityModel
protected virtual async Task<TokenResponse> GetTokenResponse(DiscoveryResponse discoveryResponse, IdentityClientConfiguration configuration) protected virtual async Task<TokenResponse> GetTokenResponse(DiscoveryResponse discoveryResponse, IdentityClientConfiguration configuration)
{ {
//TODO: Pass cancellation token
var tokenClient = new TokenClient(discoveryResponse.TokenEndpoint, configuration.ClientId, configuration.ClientSecret); var tokenClient = new TokenClient(discoveryResponse.TokenEndpoint, configuration.ClientId, configuration.ClientSecret);
switch (configuration.GrantType) switch (configuration.GrantType)

Loading…
Cancel
Save