AugusteVN.HttpClient.Delegator
1.1.0
dotnet add package AugusteVN.HttpClient.Delegator --version 1.1.0
NuGet\Install-Package AugusteVN.HttpClient.Delegator -Version 1.1.0
<PackageReference Include="AugusteVN.HttpClient.Delegator" Version="1.1.0" />
<PackageVersion Include="AugusteVN.HttpClient.Delegator" Version="1.1.0" />
<PackageReference Include="AugusteVN.HttpClient.Delegator" />
paket add AugusteVN.HttpClient.Delegator --version 1.1.0
#r "nuget: AugusteVN.HttpClient.Delegator, 1.1.0"
#:package AugusteVN.HttpClient.Delegator@1.1.0
#addin nuget:?package=AugusteVN.HttpClient.Delegator&version=1.1.0
#tool nuget:?package=AugusteVN.HttpClient.Delegator&version=1.1.0
Delegating Handler
Adds a custom HttpClient message handler / delegating handler that can add headers to the request. The delegator gets those values from a keyed singleton and registers a resolver for it.
This way, any caller of that HttpClient can access or alter those header values on the 'shared' singleton.
It holds a Local set and a Global set of headers. The local values are only applicable to the HttpClient you added the delegator to.
The global values are applied on all HttpClients. Local values can override global values, not the other way around.
The interface of the resolver 'IHttpClientFactory<T>' implements the original 'IHttpClientFactory', hides its methods but uses those under the hood.
So, you can use the .CreateClient but also have the singleton values available by just one dependency injection instead of two.
It contains what was covered in this video: Authorize HttpClient with Delegating Handler C# .NET
Sandbox, work in progress
builder.Services.AddHttpClient(nameof(TUtils), client => client.BaseAddress = new Uri("https://localhost:3000"))
.AddDelegator<TUtils>();
builder.Services.AddHttpClient(nameof(TPetService), client => client.BaseAddress = new Uri("https://localhost:3005"))
.AddDelegator<TPetService>();
builder.Services.AddScoped<AuthService>();
builder.Services.AddScoped<PetService>();
var httpClientName = nameof(TUtils);
/*
* Adds another resolver that can resolve the correct named HttpClient and its 'state' singleton.
* To be able to inject 'IHttpClientFactory<T>' where you can't access 'TUtils' e.g. in a higher-level, stand-alone module.
*/
builder.Services.AddHttpClientStateResolver<ModuleService>(httpClientName);
builder.Services.AddScoped<ModuleService>();
var app = builder.Build();
app.MapGet("/", (AuthService auth, ModuleService module, PetService pet) =>
{
auth.AuthorizeClient("<access-token>");
auth.PrintLocalState(); // Auth Local, client name: [TUtils], headers: [[x-module, bar]]
auth.PrintGlobalState(); // Auth Global, client names: [TPetService,TUtils], headers: [[x-module, foo],[Authorization, Bearer <access-token>]]
module.PrintLocalState(); // Module Local, client name: [TUtils], headers: [[x-module, bar]]
module.PrintGlobalState(); // Module Global, client names: [TPetService,TUtils], headers: [[x-module, foo],[Authorization, Bearer <access-token>]]
pet.PrintLocalState(); // Pet Local, client name: [TPetService], headers: [[x-pet, foo]]
pet.PrintGlobalState(); // Pet Global, client names: [TPetService,TUtils], headers: [[x-module, foo],[Authorization, Bearer <access-token>]]
Results.Ok();
});
app.Run();
public record TUtils; // Only used as client name.
public record TPetService; // Only used as client name.
public class AuthService
{
private readonly HttpClient _httpClient;
private readonly IHttpClientFactory<TUtils> _httpState;
public AuthService(IHttpClientFactory<TUtils> httpState)
{
_httpClient = httpState.CreateClient();
_httpState = httpState;
}
public void AuthorizeClient(string? token)
{
_httpState.Global.AddHeader("Authorization", $"Bearer {token}");
}
public void PrintLocalState()
{
Console.WriteLine($"Auth Local, client name: [{_httpState.Local.ClientName}], headers: [{string.Join(',', _httpState.Local.Headers)}]");
}
public void PrintGlobalState()
{
Console.WriteLine($"Auth Global, client names: [{string.Join(',', _httpState.Global.ClientNames)}], headers: [{string.Join(',', _httpState.Global.Headers)}]");
}
}
public class PetService
{
private readonly HttpClient _httpClient;
private readonly IHttpClientFactory<TPetService> _httpState;
public PetService(IHttpClientFactory<TPetService> httpState)
{
_httpClient = httpState.CreateClient();
_httpState = httpState;
_httpState.Local.AddHeader("x-pet", "foo");
}
public void PrintLocalState()
{
Console.WriteLine($"Pet Local, client name: [{_httpState.Local.ClientName}], headers: [{string.Join(',', _httpState.Local.Headers)}]");
}
public void PrintGlobalState()
{
Console.WriteLine($"Pet Global, client names: [{string.Join(',', _httpState.Global.ClientNames)}], headers: [{string.Join(',', _httpState.Global.Headers)}]");
}
}
public class ModuleService
{
private readonly HttpClient _httpClient;
private readonly IHttpClientFactory<ModuleService> _httpState;
public ModuleService(IHttpClientFactory<ModuleService> httpState)
{
_httpClient = httpState.CreateClient();
_httpState = httpState;
_httpState.Local.AddHeader("x-module", "bar");
_httpState.Global.AddHeader("x-module", "foo");
}
public void PrintLocalState()
{
Console.WriteLine($"Module Local, client name: [{_httpState.Local.ClientName}], headers: [{string.Join(',', _httpState.Local.Headers)}]");
}
public void PrintGlobalState()
{
Console.WriteLine($"Module Global, client names: [{string.Join(',', _httpState.Global.ClientNames)}], headers: [{string.Join(',', _httpState.Global.Headers)}]");
}
}
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Http (>= 10.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Upgraded to .NET 10.