mirror of
https://github.com/AlexMacocian/Net.Sdk.Web.Extensions.git
synced 2026-07-15 13:39:29 +00:00
HttpClientBuilder implementation
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
using AspNetCore.Extensions;
|
||||
using AspNetCore.Extensions.Options;
|
||||
using Microsoft.CorrelationVector;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Core.Extensions;
|
||||
|
||||
namespace Net.Sdk.Web.Extensions.Http;
|
||||
|
||||
public sealed class CorrelationVectorHandler : DelegatingHandler
|
||||
{
|
||||
private readonly CorrelationVectorOptions options;
|
||||
private readonly IHttpContextAccessor httpContextAccessor;
|
||||
|
||||
public CorrelationVectorHandler(
|
||||
IOptions<CorrelationVectorOptions> options,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
this.options = options.ThrowIfNull().Value.ThrowIfNull();
|
||||
this.httpContextAccessor = httpContextAccessor.ThrowIfNull();
|
||||
}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (this.httpContextAccessor.HttpContext?.GetCorrelationVector() is CorrelationVector cv)
|
||||
{
|
||||
cv.Increment();
|
||||
request.Headers.Add(this.options.Header, cv.Value);
|
||||
this.httpContextAccessor.HttpContext.SetCorrelationVector(cv);
|
||||
}
|
||||
|
||||
var response = await base.SendAsync(request, cancellationToken);
|
||||
if (response.Headers.TryGetValues(this.options.Header, out var cvHeaders) &&
|
||||
cvHeaders.FirstOrDefault() is string responseCv)
|
||||
{
|
||||
this.httpContextAccessor.HttpContext?.SetCorrelationVector(CorrelationVector.Parse(responseCv));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
using System.Core.Extensions;
|
||||
|
||||
namespace Net.Sdk.Web.Extensions.Http;
|
||||
|
||||
public sealed class HttpClientBuilder<T>
|
||||
where T : class
|
||||
{
|
||||
private readonly WebApplicationBuilder app;
|
||||
private readonly List<Func<IServiceProvider, DelegatingHandler>> handlers = [];
|
||||
private readonly List<Func<IServiceProvider, (string Name, IEnumerable<string> Values)>> defaultRequestHeaders = [];
|
||||
private readonly List<string> redactedLoggedHeaderNames = [];
|
||||
private readonly List<Func<string, bool>> headerRedactHandlers = [];
|
||||
|
||||
private bool defaultLogger = false;
|
||||
private Uri? baseAddress;
|
||||
private long maxResponseBufferSize = 2147483647L; //2GB default HttpClient value [https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.maxresponsecontentbuffersize?view=net-6.0]
|
||||
private TimeSpan timeout = TimeSpan.FromSeconds(100); //100 seconds default HttpClient value [https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.timeout?view=net-6.0]
|
||||
|
||||
internal HttpClientBuilder(WebApplicationBuilder app)
|
||||
{
|
||||
this.app = app.ThrowIfNull();
|
||||
}
|
||||
|
||||
public HttpClientBuilder<T> WithBaseAddress(Uri baseAddress)
|
||||
{
|
||||
baseAddress.ThrowIfNull();
|
||||
|
||||
this.baseAddress = baseAddress;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder<T> WithDelegatingHandler(Func<IServiceProvider, DelegatingHandler> handlerFactory)
|
||||
{
|
||||
handlerFactory.ThrowIfNull();
|
||||
|
||||
this.handlers.Add(handlerFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder<T> WithDefaultRequestHeaders(Func<IServiceProvider, (string Name, IEnumerable<string> Values)> headerFactory)
|
||||
{
|
||||
headerFactory.ThrowIfNull();
|
||||
|
||||
this.defaultRequestHeaders.Add(headerFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder<T> WithMaxResponseBufferSize(long responseBufferSize)
|
||||
{
|
||||
this.maxResponseBufferSize = responseBufferSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder<T> WithRedactedLoggerHeaderName(string headerName)
|
||||
{
|
||||
this.redactedLoggedHeaderNames.Add(headerName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder<T> WithRedactedLoggerHeaderName(Func<string, bool> handler)
|
||||
{
|
||||
this.headerRedactHandlers.Add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder<T> WithTimeout(TimeSpan timeout)
|
||||
{
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder<T> AddDefaultLogger()
|
||||
{
|
||||
this.defaultLogger = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WebApplicationBuilder Build()
|
||||
{
|
||||
this.CreateBuilder();
|
||||
return this.app;
|
||||
}
|
||||
|
||||
public IHttpClientBuilder CreateBuilder()
|
||||
{
|
||||
var builder = this.app.Services.AddHttpClient(typeof(T).Name);
|
||||
foreach (var handler in this.handlers)
|
||||
{
|
||||
builder.AddHttpMessageHandler(handler);
|
||||
}
|
||||
|
||||
builder.ConfigureHttpClient((sp, client) =>
|
||||
{
|
||||
client.BaseAddress = this.baseAddress;
|
||||
client.Timeout = this.timeout;
|
||||
client.MaxResponseContentBufferSize = this.maxResponseBufferSize;
|
||||
foreach (var header in this.defaultRequestHeaders)
|
||||
{
|
||||
(var name, var values) = header(sp);
|
||||
client.DefaultRequestHeaders.Add(name, values);
|
||||
}
|
||||
});
|
||||
|
||||
if (this.defaultLogger)
|
||||
{
|
||||
builder.AddDefaultLogger();
|
||||
}
|
||||
|
||||
if (this.redactedLoggedHeaderNames.Count > 0)
|
||||
{
|
||||
builder.RedactLoggedHeaders(this.redactedLoggedHeaderNames);
|
||||
}
|
||||
|
||||
foreach (var handler in this.headerRedactHandlers)
|
||||
{
|
||||
builder.RedactLoggedHeaders(handler);
|
||||
}
|
||||
|
||||
this.app.Services.AddScoped<IHttpClient<T>>(sp =>
|
||||
{
|
||||
var clientBuilder = sp.GetRequiredService<IHttpClientFactory>();
|
||||
var innerClient = clientBuilder.CreateClient(typeof(T).Name);
|
||||
return new HttpClient<T>(innerClient);
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Library</OutputType>
|
||||
<IsPackable>true</IsPackable>
|
||||
<Version>0.1</Version>
|
||||
<Version>0.5</Version>
|
||||
<Authors>Alexandru Macocian</Authors>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||
@@ -26,7 +26,8 @@
|
||||
<PackageReference Include="Microsoft.CorrelationVector" Version="1.0.42" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="SystemExtensions.NetCore" Version="1.0.1" />
|
||||
<PackageReference Include="SystemExtensions.NetStandard" Version="1.6.3" />
|
||||
<PackageReference Include="SystemExtensions.NetStandard" Version="1.6.4" />
|
||||
<PackageReference Include="SystemExtensions.NetStandard.DependencyInjection" Version="1.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using AspNetCore.Extensions.Attributes;
|
||||
using AspNetCore.Extensions.Middleware;
|
||||
using Net.Sdk.Web.Extensions.Http;
|
||||
using System.Core.Extensions;
|
||||
using System.Extensions;
|
||||
|
||||
@@ -20,6 +22,36 @@ public static class WebApplicationBuilderExtensions
|
||||
return configurationManager.GetRequiredSection(GetOptionsName<TOptions>());
|
||||
}
|
||||
|
||||
public static WebApplicationBuilder WithCorrelationVector(this WebApplicationBuilder builder)
|
||||
{
|
||||
builder.ThrowIfNull();
|
||||
builder.Services.AddScoped<CorrelationVectorMiddleware>();
|
||||
builder.Services.AddScoped<CorrelationVectorHandler>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static Net.Sdk.Web.Extensions.Http.HttpClientBuilder<T> RegisterHttpClient<T>(this WebApplicationBuilder builder)
|
||||
where T : class
|
||||
{
|
||||
_ = builder.ThrowIfNull();
|
||||
return new Net.Sdk.Web.Extensions.Http.HttpClientBuilder<T>(builder);
|
||||
}
|
||||
|
||||
public static Net.Sdk.Web.Extensions.Http.HttpClientBuilder<T> WithCorrelationVector<T>(this Net.Sdk.Web.Extensions.Http.HttpClientBuilder<T> httpClientBuilder)
|
||||
where T : class
|
||||
{
|
||||
return httpClientBuilder.ThrowIfNull()
|
||||
.WithDelegatingHandler(sp => sp.GetRequiredService<CorrelationVectorHandler>());
|
||||
}
|
||||
|
||||
public static IHttpClientBuilder WithCorrelationVector<T>(this IHttpClientBuilder httpClientBuilder)
|
||||
where T : class
|
||||
{
|
||||
return httpClientBuilder.ThrowIfNull()
|
||||
.AddHttpMessageHandler<CorrelationVectorHandler>();
|
||||
}
|
||||
|
||||
private static string GetOptionsName<TOptions>()
|
||||
{
|
||||
var maybeAttribute = typeof(TOptions).GetCustomAttributes(false).OfType<OptionsNameAttribute>().FirstOrDefault();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using AspNetCore.Extensions.Middleware;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using System.Core.Extensions;
|
||||
@@ -10,6 +11,14 @@ namespace AspNetCore.Extensions.Websockets.Extensions;
|
||||
|
||||
public static class WebApplicationExtensions
|
||||
{
|
||||
public static WebApplication UseCorrelationVector(this WebApplication webApplication)
|
||||
{
|
||||
webApplication.ThrowIfNull()
|
||||
.UseMiddleware<CorrelationVectorMiddleware>();
|
||||
|
||||
return webApplication;
|
||||
}
|
||||
|
||||
public static WebApplication MapWebSocket<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TWebSocketRoute>(this WebApplication app, string route)
|
||||
where TWebSocketRoute : WebSocketRouteBase
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user