I am trying to setup named HttpClient in Blazor WASM. Problem is that I need to call async method in order to get JWT.
builder.Services.AddHttpClient("auth", async c =>
{
// access the DI container
var serviceProvider = builder.Services.BuildServiceProvider();
// Find the HttpContextAccessor service
var localStorageService = serviceProvider.GetService<ILocalStorageService>();
// Get the bearer token.
if (localStorageService == null) return;
var jwt = await localStorageService.GetJwtAsync();
Console.WriteLine("JWT "+jwt);
if (jwt != null)
{
c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
}
}
);
This doesn't work since parameter c cannot be async function.
Next I tried something like this
builder.Services.AddHttpClient("auth", c =>
{
// access the DI container
var serviceProvider = builder.Services.BuildServiceProvider();
// Find the HttpContextAccessor service
var localStorageService = serviceProvider.GetService<ILocalStorageService>();
// Get the bearer token.
if (localStorageService == null) return;
var jwt = localStorageService.GetJwtAsync().Result;
Console.WriteLine("JWT "+jwt);
if (jwt != null)
{
c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
}
}
);
In this case I get an error
Unhandled exception rendering component: Cannot wait on monitors on this runtime
because I am using .Result
in WASM and it is blocking the main thread.
Any ideas is this possible to implement and how?
Consider writing a custom DelegatingHandler
.
A DelegatingHandler
is a decorator that can be used to add behavior before or after the inner Http Handler will be called by overriding the SendAsync
method.
In your case the handler could look like in the example below:
public class LocalStorageAuthHandler : DelegatingHandler
{
private readonly ILocalStorageService _localStorageService;
public LocalStorageAuthHandler(ILocalStorageService localStorageService)
{
_localStorageService = localStorageService;
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
// Access local storage service and update headers accordingly
return await base.SendAsync(request, cancellationToken);
}
}
Then add the handler to your HttpClient
:
// Transient, Scoped or Singleton depends on your code:
services.AddTransient<LocalStorageAuthHandler>();
services.AddHttpClient("clientName")
.AddHttpMessageHandler<LocalStorageAuthHandler>();
This will use the LocalStorageAuthHandler
with the default http message handler as inner handler.
You can even combine multiple DelegatingHandler
s to build a Chain of Responsibility.
See Make HTTP requests using IHttpClientFactory in ASP.NET Core on Microsoft Learn for details.