Blazor Server DelegateHandler does not receive correct (circuit-)scoped service instance
See original GitHub issueIs there an existing issue for this?
- I have searched the existing issues
Describe the bug
I have simple login process.
This works correctly in Windows Forms and .NET MAUI application because they don’t have any circuit scope in DI.
For username and password I receive token. I store token in circuit-scoped service “AuthState”.
Blazor Server login page code:
private async Task LogInInternal(LoginRequest data)
{
InfoMessage = "Logging in...";
var session = await Client.LogInAsync(data);
AuthState.Session = session;
}
I add the token as HTTP header for every request using the ApiRequestAuthInterceptor
which is a DelegateHandler
(allows customizing outgoing HTTP requests), I inject the “AuthState” object.
public static class RestApiClientSetup
{
public static IServiceCollection AddApiClient<T>(this IServiceCollection services, string apiBaseUrl)
where T : class //and where T has HttpClient constructor parameter
{
//.AddHttpClient -> NuGet package Microsoft.Extensions.Http
services.AddTransient<ApiRequestAuthInterceptor>();
services.AddHttpClient<T>(httpClient =>
{
httpClient.BaseAddress = new Uri(apiBaseUrl);
}).AddHttpMessageHandler<ApiRequestAuthInterceptor>();
return services;
}
}
services.AddScoped<AuthState>();
/// <summary>
/// Must be registered as transient
/// </summary>
public class ApiRequestAuthInterceptor : DelegatingHandler
{
private readonly AuthState authState;
public ApiRequestAuthInterceptor(AuthState authState)
{
this.authState = authState;
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
string sessionID = authState.Session?.SessionID;
if (sessionID != null)
{
request.Headers.Authorization = new AuthenticationHeaderValue("SessionID", sessionID);
}
if (sessionID is null)
{
request.Headers.Authorization = null;
}
return await base.SendAsync(request, cancellationToken);
}
}
When I log in, I have AuthState with ID 2 (using static counter from 1 incremented in constructor) in Blazor Server Page but when I do another call from the same page, the ApiRequestAuthInterceptor sees AuthState with ID 1 (incorrect).
public class AuthState
{
private static int counter;
public int ID { get; private set; }
public AuthState()
{
counter++;
ID = counter;
Trace.WriteLine($"Created AuthState instance #{counter}");
}
public Session Session { get; set; }
}
It works correctly when I register AuthState as Singleton when I only do local testing with 1 browser tab but that will break with more tabs open because they would all get the same data if AuthState was Singleton.
_Host
page looks like this if needed (I tried changing render-mode
to Server
):
@page "/"
@namespace SchoolSocialSite.Web.Blazor.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = "_Layout";
}
<component type="typeof(App)" render-mode="Server" />
Expected Behavior
Both Blazor Server Page and the DelegateHandler see the same AuthState which is scoped for the active circuit.
Steps To Reproduce
mentioned above
Exceptions (if any)
No response
.NET Version
7.0.302 but using .NET 6 in the project, thus probably 6.0.16
Anything else?
No response
Issue Analytics
- State:
- Created 4 months ago
- Comments:7 (3 by maintainers)
@janseris thanks for contacting us.
This is something that we made possible in .NET 8.0 via new APIs on top of CircuitHandler. https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-8-preview-3/
This issue has been resolved and has not had any activity for 1 day. It will be closed for housekeeping purposes.
See our Issue Management Policies for more information.