diff --git a/CorrelationId.sln b/CorrelationId.sln index 7448ff8..e351b3d 100644 --- a/CorrelationId.sln +++ b/CorrelationId.sln @@ -36,6 +36,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "net48", "net48", "{0D3CBD88 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net48MvcSample", "samples\net48\Net48MvcSample\Net48MvcSample.csproj", "{DED5BFD9-29F6-4291-B863-1995C33D686B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CorrelationId.Net48.Tests", "test\CorrelationId.Net48.Tests\CorrelationId.Net48.Tests.csproj", "{A4137EAE-590B-476C-838F-A2D4A45AAE4A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -62,6 +64,10 @@ Global {DED5BFD9-29F6-4291-B863-1995C33D686B}.Debug|Any CPU.Build.0 = Debug|Any CPU {DED5BFD9-29F6-4291-B863-1995C33D686B}.Release|Any CPU.ActiveCfg = Release|Any CPU {DED5BFD9-29F6-4291-B863-1995C33D686B}.Release|Any CPU.Build.0 = Release|Any CPU + {A4137EAE-590B-476C-838F-A2D4A45AAE4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4137EAE-590B-476C-838F-A2D4A45AAE4A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4137EAE-590B-476C-838F-A2D4A45AAE4A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4137EAE-590B-476C-838F-A2D4A45AAE4A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -74,6 +80,7 @@ Global {0E2E2678-4DBB-4560-AF0E-86ECAF8D9B6F} = {E28C5481-A68F-44AF-983F-EA127E70621A} {0D3CBD88-90B9-4364-9DE4-CA1FCC9CB023} = {34C0F65A-8BF2-40DA-B0E7-844930EE2A7B} {DED5BFD9-29F6-4291-B863-1995C33D686B} = {0D3CBD88-90B9-4364-9DE4-CA1FCC9CB023} + {A4137EAE-590B-476C-838F-A2D4A45AAE4A} = {EAF27B74-0B27-4BEE-9F82-DD5812B21B17} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C0A37404-C50B-472E-9491-DDE2A4BDA882} diff --git a/samples/net48/Net48MvcSample/Controllers/ValuesController.cs b/samples/net48/Net48MvcSample/Controllers/ValuesController.cs new file mode 100644 index 0000000..45ae3aa --- /dev/null +++ b/samples/net48/Net48MvcSample/Controllers/ValuesController.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Http; + +namespace Net48MvcSample.Controllers +{ + public class ValuesController : ApiController + { + public IEnumerable Get() + { + var correlationId = HttpContext.Current.Request.Headers["X-Correlation-Id"]; + var response = new[] { "value1", "value2", correlationId }; + return response; + } + } +} diff --git a/samples/net48/Net48MvcSample/Controllers/WeatherForecastController.cs b/samples/net48/Net48MvcSample/Controllers/WeatherForecastController.cs deleted file mode 100644 index c3ff6e1..0000000 --- a/samples/net48/Net48MvcSample/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web.Http; - -namespace Net48MvcSample.Controllers -{ - public class WeatherForecastController : ApiController - { - private static readonly string[] _summaries = { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - public IEnumerable Get() - { - var rng = new Random(); - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateTime.Now.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = _summaries[rng.Next(_summaries.Length)] - }); - } - } -} diff --git a/samples/net48/Net48MvcSample/Net48MvcSample.csproj b/samples/net48/Net48MvcSample/Net48MvcSample.csproj index 235c8a0..67dc484 100644 --- a/samples/net48/Net48MvcSample/Net48MvcSample.csproj +++ b/samples/net48/Net48MvcSample/Net48MvcSample.csproj @@ -122,12 +122,11 @@ - + Global.asax - @@ -162,7 +161,7 @@ True 5000 / - http://localhost:5000/ + http://localhost:31488/ False False diff --git a/samples/net48/Net48MvcSample/WeatherForecast.cs b/samples/net48/Net48MvcSample/WeatherForecast.cs deleted file mode 100644 index 3ebc181..0000000 --- a/samples/net48/Net48MvcSample/WeatherForecast.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using Newtonsoft.Json; - -namespace Net48MvcSample -{ - public class WeatherForecast - { - [JsonProperty] - public DateTime Date { get; set; } - - [JsonProperty] - public int TemperatureC { get; set; } - - [JsonProperty] - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - [JsonProperty] - public string Summary { get; set; } - } -} diff --git a/test/CorrelationId.Net48.Tests/CorrelationId.Net48.Tests.csproj b/test/CorrelationId.Net48.Tests/CorrelationId.Net48.Tests.csproj new file mode 100644 index 0000000..b2fe4a2 --- /dev/null +++ b/test/CorrelationId.Net48.Tests/CorrelationId.Net48.Tests.csproj @@ -0,0 +1,108 @@ + + + + + Debug + AnyCPU + {A4137EAE-590B-476C-838F-A2D4A45AAE4A} + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Properties + CorrelationId.Net48.Tests + CorrelationId.Net48.Tests + v4.8 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + {865886fc-ebbe-49e1-8f49-fecd0408ef6e} + CorrelationId + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/CorrelationId.Net48.Tests/CorrelationIdMiddlewareWrapper.cs b/test/CorrelationId.Net48.Tests/CorrelationIdMiddlewareWrapper.cs new file mode 100644 index 0000000..4b52ce2 --- /dev/null +++ b/test/CorrelationId.Net48.Tests/CorrelationIdMiddlewareWrapper.cs @@ -0,0 +1,73 @@ +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CorrelationId.Abstractions; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; + +namespace CorrelationId.Net48.Tests +{ + /// + /// We don't have any middleware in .net framework so we wrap it in a HttpMessageHandler instead + /// + public class CorrelationIdMiddlewareWrapper : DelegatingHandler + { + private readonly CorrelationIdMiddleware _correlationIdMiddleware; + private readonly ICorrelationContextFactory _correlationContextFactory; + private readonly IOptions _correlationIdOptions; + + public CorrelationIdMiddlewareWrapper(CorrelationIdMiddleware correlationIdMiddleware, + ICorrelationContextFactory correlationContextFactory, IOptions correlationIdOptions) + { + _correlationIdMiddleware = correlationIdMiddleware; + _correlationContextFactory = correlationContextFactory; + _correlationIdOptions = correlationIdOptions; + } + + protected override async Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken) + { + var httpContext = GetHttpContext(); + await _correlationIdMiddleware.Invoke(httpContext, _correlationContextFactory); + + var correlationIds = + httpContext.Request.Headers[_correlationIdOptions.Value.RequestHeader]; + + foreach (var correlationId in correlationIds) + { + request.Headers.Add(_correlationIdOptions.Value.RequestHeader, correlationId); + } + + var response = await base.SendAsync(request, cancellationToken); + return response; + } + + private HttpContext GetHttpContext() + { + var httpContext = new DefaultHttpContext(); + + if (System.Web.HttpContext.Current == null) + { + return httpContext; + } + + var correlationIds = + System.Web.HttpContext.Current.Request.Headers + .GetValues(_correlationIdOptions.Value.RequestHeader); + + if (correlationIds == null) + { + return httpContext; + } + + foreach (var correlationId in correlationIds) + { + httpContext.Request.Headers + .Add(_correlationIdOptions.Value.RequestHeader, correlationId); + } + + return httpContext; + } + } +} \ No newline at end of file diff --git a/test/CorrelationId.Net48.Tests/CorrelationIdServiceCollectionExtensions.cs b/test/CorrelationId.Net48.Tests/CorrelationIdServiceCollectionExtensions.cs new file mode 100644 index 0000000..a5cd1fe --- /dev/null +++ b/test/CorrelationId.Net48.Tests/CorrelationIdServiceCollectionExtensions.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; +using CorrelationId.Abstractions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace CorrelationId.Net48.Tests +{ + public static class CorrelationIdServiceCollectionExtensions + { + public static void AddCorrelationIdToHttpClientBuilder(this ServiceCollection serviceCollection, IHttpClientBuilder httpClientBuilder) + { + serviceCollection.AddTransient(x => + { + return new CorrelationIdMiddleware( + next => + { + var correlationContextAccessor = x.GetService(); + // copied from CorrelationId.HttpClient.CorrelationIdHandler + if (!string.IsNullOrEmpty(correlationContextAccessor?.CorrelationContext?.CorrelationId) && + !next.Request.Headers.ContainsKey(correlationContextAccessor.CorrelationContext.Header)) + { + next.Request.Headers.Add(correlationContextAccessor.CorrelationContext.Header, + correlationContextAccessor.CorrelationContext.CorrelationId); + } + return Task.CompletedTask; + }, + x.GetService>(), + x.GetService>(), + x.GetService()); + }); + + serviceCollection.AddTransient(); + httpClientBuilder.AddHttpMessageHandler(); + } + } +} \ No newline at end of file diff --git a/test/CorrelationId.Net48.Tests/HttpClientBuilderTests.cs b/test/CorrelationId.Net48.Tests/HttpClientBuilderTests.cs new file mode 100644 index 0000000..16f95e8 --- /dev/null +++ b/test/CorrelationId.Net48.Tests/HttpClientBuilderTests.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using CorrelationId.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; +using Refit; +using Xunit; +using Xunit.Abstractions; + +namespace CorrelationId.Net48.Tests +{ + public class HttpClientBuilderTests + { + private readonly ITestOutputHelper _testOutputHelper; + private readonly INet48MvcSampleApiClient _net48MvcSampleApiClient; + + public HttpClientBuilderTests(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + var serviceCollection = new ServiceCollection(); + serviceCollection.AddDefaultCorrelationId(options => + { + options.AddToLoggingScope = true; + options.IgnoreRequestHeader = false; + options.IncludeInResponse = true; + options.RequestHeader = "X-Correlation-Id"; + options.ResponseHeader = "X-Correlation-Id"; + options.UpdateTraceIdentifier = false; + }); + + string net48MvcSampleBaseUrl = "http://localhost:31488"; + + var httpClientBuilder = serviceCollection.AddRefitClient() + .ConfigureHttpClient(httpClient => + { + httpClient.BaseAddress = + new Uri(net48MvcSampleBaseUrl); + }); + + serviceCollection.AddCorrelationIdToHttpClientBuilder(httpClientBuilder); + var serviceProvider = serviceCollection.BuildServiceProvider(); + _net48MvcSampleApiClient = serviceProvider.GetService(); + } + + [Fact] + public async Task CallTo_Net48Service_ShouldSetCorrelationId() + { + var response = await _net48MvcSampleApiClient.GetAsync(); + + Assert.True(response.IsSuccessStatusCode, response.Error?.Content); + + Assert.NotNull(response.Content); + _testOutputHelper.WriteLine(string.Join(", ", response.Content)); + + Assert.Equal(3, response.Content.Count()); + Assert.NotEmpty(response.Content.Last()); + } + } +} \ No newline at end of file diff --git a/test/CorrelationId.Net48.Tests/INet48MvcSampleApiClient.cs b/test/CorrelationId.Net48.Tests/INet48MvcSampleApiClient.cs new file mode 100644 index 0000000..88083cf --- /dev/null +++ b/test/CorrelationId.Net48.Tests/INet48MvcSampleApiClient.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Refit; + +namespace CorrelationId.Net48.Tests +{ + public interface INet48MvcSampleApiClient + { + [Get("/api/values")] + Task>> GetAsync(); + } +} \ No newline at end of file diff --git a/test/CorrelationId.Net48.Tests/Properties/AssemblyInfo.cs b/test/CorrelationId.Net48.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8988d9c --- /dev/null +++ b/test/CorrelationId.Net48.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("CorrelationId.Net48.Tests")] +[assembly: AssemblyProduct("CorrelationId.Net48.Tests")] +[assembly: Guid("A4137EAE-590B-476C-838F-A2D4A45AAE4A")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file