From df9325a329d65bbe83ca2cfb61802ed3d3fd5ebc Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Sat, 9 Feb 2019 22:07:07 +0300 Subject: [PATCH] Use CorrelationId and CancellationToken in the DynamicHttpProxyInterceptor --- .../Tracing/AspNetCoreCorrelationIdOptions.cs | 7 -- .../AspNetCoreCorrelationIdProvider.cs | 8 +- .../Volo/Abp/Tracing/CorrelationIdOptions.cs | 7 ++ .../Tracing/DefaultCorrelationIdProvider.cs | 18 ++++ .../Abp/Tracing/ICorrelationIdProvider.cs | 5 +- .../Abp/Tracing/NullCorrelationIdProvider.cs | 12 --- .../Volo.Abp.Http.Client.csproj | 1 + .../Abp/Http/Client/AbpHttpClientModule.cs | 9 +- .../DynamicHttpProxyInterceptor.cs | 92 +++++++++++-------- ...delRemoteServiceHttpClientAuthenticator.cs | 3 +- 10 files changed, 98 insertions(+), 64 deletions(-) delete mode 100644 framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AspNetCoreCorrelationIdOptions.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Tracing/CorrelationIdOptions.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Tracing/DefaultCorrelationIdProvider.cs delete mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Tracing/NullCorrelationIdProvider.cs diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AspNetCoreCorrelationIdOptions.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AspNetCoreCorrelationIdOptions.cs deleted file mode 100644 index 8699156a40..0000000000 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AspNetCoreCorrelationIdOptions.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Volo.Abp.AspNetCore.Tracing -{ - public class AspNetCoreCorrelationIdOptions - { - public string HeaderName { get; set; } = "X-Correlation-Id"; - } -} diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AspNetCoreCorrelationIdProvider.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AspNetCoreCorrelationIdProvider.cs index b88e2a5d60..78383d1252 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Tracing/AspNetCoreCorrelationIdProvider.cs +++ b/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 { protected IHttpContextAccessor HttpContextAccessor { get; } - protected AspNetCoreCorrelationIdOptions Options { get; } + protected CorrelationIdOptions Options { get; } public AspNetCoreCorrelationIdProvider( IHttpContextAccessor httpContextAccessor, - IOptions options) + IOptions options) { HttpContextAccessor = httpContextAccessor; Options = options.Value; @@ -29,12 +29,12 @@ namespace Volo.Abp.AspNetCore.Tracing lock (HttpContextAccessor.HttpContext.Request.Headers) { - string correlationId = HttpContextAccessor.HttpContext.Request.Headers[Options.HeaderName]; + string correlationId = HttpContextAccessor.HttpContext.Request.Headers[Options.HttpHeaderName]; if (correlationId.IsNullOrEmpty()) { correlationId = CreateNewCorrelationId(); - HttpContextAccessor.HttpContext.Request.Headers[Options.HeaderName] = correlationId; + HttpContextAccessor.HttpContext.Request.Headers[Options.HttpHeaderName] = correlationId; } return correlationId; diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Tracing/CorrelationIdOptions.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Tracing/CorrelationIdOptions.cs new file mode 100644 index 0000000000..f97d240b05 --- /dev/null +++ b/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"; + } +} diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Tracing/DefaultCorrelationIdProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Tracing/DefaultCorrelationIdProvider.cs new file mode 100644 index 0000000000..24102112a7 --- /dev/null +++ b/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"); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Tracing/ICorrelationIdProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Tracing/ICorrelationIdProvider.cs index 7081c26d6a..082da95779 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Tracing/ICorrelationIdProvider.cs +++ b/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 { + [NotNull] string Get(); } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Tracing/NullCorrelationIdProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Tracing/NullCorrelationIdProvider.cs deleted file mode 100644 index 99b9d5b919..0000000000 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Tracing/NullCorrelationIdProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.Tracing -{ - public class NullCorrelationIdProvider : ICorrelationIdProvider, ISingletonDependency - { - public string Get() - { - return null; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj b/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj index dd9122b666..a93dc4fcb9 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj +++ b/framework/src/Volo.Abp.Http.Client/Volo.Abp.Http.Client.csproj @@ -20,6 +20,7 @@ + \ No newline at end of file diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs index 0331c51fff..f9240bd4a8 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs @@ -1,17 +1,20 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Castle; using Volo.Abp.Modularity; +using Volo.Abp.Threading; namespace Volo.Abp.Http.Client { - [DependsOn(typeof(AbpHttpModule))] - [DependsOn(typeof(AbpCastleCoreModule))] + [DependsOn( + typeof(AbpHttpModule), + typeof(AbpCastleCoreModule), + typeof(AbpThreadingModule) + )] public class AbpHttpClientModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { var configuration = context.Services.GetConfiguration(); - Configure(configuration); } } diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index e97f6d6323..b108ca92f6 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/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.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 : 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> Logger { get; set; } @@ -46,14 +51,20 @@ namespace Volo.Abp.Http.Client.DynamicProxying IOptionsSnapshot remoteServiceOptions, IApiDescriptionFinder apiDescriptionFinder, IJsonSerializer jsonSerializer, - IRemoteServiceHttpClientAuthenticator clientAuthenticator) + IRemoteServiceHttpClientAuthenticator clientAuthenticator, + ICancellationTokenProvider cancellationTokenProvider, + ICorrelationIdProvider correlationIdProvider, + IOptions 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>.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 = _jsonSerializer.Deserialize( + invocation.ReturnValue = JsonSerializer.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 MakeRequestAndGetResultAsync(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 _jsonSerializer.Deserialize(responseAsString); + return JsonSerializer.Deserialize(responseAsString); } - private async Task MakeRequest(IAbpMethodInvocation invocation) + private async Task 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 remoteServiceConfig = _remoteServiceOptions.RemoteServices.GetConfigurationOrDefault(clientConfig.RemoteServiceName); + 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 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 url = remoteServiceConfig.BaseUrl + UrlBuilder.GenerateUrlWithParameters(action, invocation.ArgumentsDictionary, apiVersion); 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); - await _clientAuthenticator.Authenticate( + await ClientAuthenticator.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 = _clientOptions.HttpClientProxies.GetOrDefault(typeof(TService)) + var clientConfig = ClientOptions.HttpClientProxies.GetOrDefault(typeof(TService)) ?? throw new AbpException($"Could not get DynamicHttpClientProxyConfig for {typeof(TService).FullName}."); - return _remoteServiceOptions.RemoteServices.GetOrDefault(clientConfig.RemoteServiceName)?.Version - ?? _remoteServiceOptions.RemoteServices.Default?.Version; + return RemoteServiceOptions.RemoteServices.GetOrDefault(clientConfig.RemoteServiceName)?.Version + ?? RemoteServiceOptions.RemoteServices.Default?.Version; } private async Task ThrowExceptionForResponseAsync(HttpResponseMessage response) { if (response.Headers.Contains(AbpHttpConsts.AbpErrorFormat)) { - var errorResponse = _jsonSerializer.Deserialize( + var errorResponse = JsonSerializer.Deserialize( 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; + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs index ee1dd28e85..ad956aee1e 100644 --- a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs +++ b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs @@ -15,7 +15,6 @@ namespace Volo.Abp.IdentityModel public class IdentityModelHttpClientAuthenticator : IIdentityModelHttpClientAuthenticator, ITransientDependency { public ILogger Logger { get; set; } - protected IdentityClientOptions ClientOptions { get; } public IdentityModelHttpClientAuthenticator( @@ -84,6 +83,8 @@ namespace Volo.Abp.IdentityModel protected virtual async Task GetTokenResponse(DiscoveryResponse discoveryResponse, IdentityClientConfiguration configuration) { + //TODO: Pass cancellation token + var tokenClient = new TokenClient(discoveryResponse.TokenEndpoint, configuration.ClientId, configuration.ClientSecret); switch (configuration.GrantType)