I'm developing a Blazor Server
application that calls an external API. After successful login, I store the JWT token in ProtectedSessionStorage
. While I can retrieve the token fine in Razor components, I get a JavaScript interop exception when trying to access it from a custom DelegatingHandler
.
Error Details:
System.InvalidOperationException: 'JavaScript interop calls cannot be issued at this time. This is because the component is being statically rendered. When prerendering is enabled, JavaScript interop calls can only be performed during the OnAfterRenderAsync lifecycle method.'
Service Registration:
builder.Services.AddScoped<ProtectedSessionStorage>();
builder.Services.AddScoped<TokenService>();
builder.Services.AddScoped<AuthTokenHandler>();
builder.Services.AddHttpClient("AuthenticatedClient", client =>
{
client.BaseAddress = new Uri(BaseUrl);
}).AddHttpMessageHandler<AuthTokenHandler>();
TokenService.cs
public class TokenService(ProtectedSessionStorage sessionStorage)
{
readonly string Key = "AccessToken";
public async Task SetAsync(string value)
{
try
{
await sessionStorage.SetAsync(Key, value);
}
catch { }
}
public async Task<string> GetAsync()
{
try
{
string result = string.Empty;
var tokenResult = await sessionStorage.GetAsync<string>(Key);
if (tokenResult.Success)
{
result = tokenResult.Value ?? string.Empty;
}
return result;
}
catch
{
return string.Empty;
}
}
}
AuthTokenHandler.cs
public class AuthTokenHandler(TokenService tokenService) : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var token = await tokenService.GetAsync();//exception occurs in this line
if (!string.IsNullOrEmpty(token))
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
return await base.SendAsync(request, cancellationToken);
}
}
Razor Component
string token = await tokenService.GetAsync(); //this line can retrieve token
var httpClient = HttpClientFactory.CreateClient("AuthenticatedClient");
var response = await httpClient.GetAsync(RelativeUrl);
if (response.IsSuccessStatusCode)
{
message = "Authorized";
var result = await response.Content.ReadAsStringAsync();
}
else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
message = "Unauthorized";
}
I'm developing a Blazor Server
application that calls an external API. After successful login, I store the JWT token in ProtectedSessionStorage
. While I can retrieve the token fine in Razor components, I get a JavaScript interop exception when trying to access it from a custom DelegatingHandler
.
Error Details:
System.InvalidOperationException: 'JavaScript interop calls cannot be issued at this time. This is because the component is being statically rendered. When prerendering is enabled, JavaScript interop calls can only be performed during the OnAfterRenderAsync lifecycle method.'
Service Registration:
builder.Services.AddScoped<ProtectedSessionStorage>();
builder.Services.AddScoped<TokenService>();
builder.Services.AddScoped<AuthTokenHandler>();
builder.Services.AddHttpClient("AuthenticatedClient", client =>
{
client.BaseAddress = new Uri(BaseUrl);
}).AddHttpMessageHandler<AuthTokenHandler>();
TokenService.cs
public class TokenService(ProtectedSessionStorage sessionStorage)
{
readonly string Key = "AccessToken";
public async Task SetAsync(string value)
{
try
{
await sessionStorage.SetAsync(Key, value);
}
catch { }
}
public async Task<string> GetAsync()
{
try
{
string result = string.Empty;
var tokenResult = await sessionStorage.GetAsync<string>(Key);
if (tokenResult.Success)
{
result = tokenResult.Value ?? string.Empty;
}
return result;
}
catch
{
return string.Empty;
}
}
}
AuthTokenHandler.cs
public class AuthTokenHandler(TokenService tokenService) : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var token = await tokenService.GetAsync();//exception occurs in this line
if (!string.IsNullOrEmpty(token))
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
return await base.SendAsync(request, cancellationToken);
}
}
Razor Component
string token = await tokenService.GetAsync(); //this line can retrieve token
var httpClient = HttpClientFactory.CreateClient("AuthenticatedClient");
var response = await httpClient.GetAsync(RelativeUrl);
if (response.IsSuccessStatusCode)
{
message = "Authorized";
var result = await response.Content.ReadAsStringAsync();
}
else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
message = "Unauthorized";
}
Share
Improve this question
asked yesterday
Anuj KarkiAnuj Karki
6176 silver badges29 bronze badges
1 Answer
Reset to default 0You could try create a CustomHttpClientFactory instead of using DelegatingHandler to pass token parameter when creating httpclient.
public class CustomHttpClientFactory(HttpClient client)
{
public HttpClient CreateClient(string? token)
{
if (!string.IsNullOrEmpty(token))
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
//client.BaseAddress = new Uri(BaseUrl);
return client;
}
}
builder.Services.AddScoped<CustomHttpClientFactory>();
builder.Services.AddHttpClient<CustomHttpClientFactory>();
Then use directly
@inject CustomHttpClientFactory HttpClientFactory
...
string token = await tokenService.GetAsync();
var httpClient = HttpClientFactory.CreateClient(token);