Skip to content

DefaultHttpClientFactory working incorrectly with Scoped dependencies #42344

@mrpmorris

Description

@mrpmorris

Describe the bug

When using Microsoft.Extensions.Http AddHttpClient and AddHttpMessageHandler, the instance of T belongs to a different ServiceProvider than the ServiceProvider used to generate a Blazor page - even though T is registered as scoped.

This seems to be a problem with DefaultHttpClientFactory being registered as Singleton instead of Scoped. In Blazor every user connection equates to a Scoped container, so when our Http handling delegates have dependencies we really need them to be created in the correct Scoped container.

The Blazor team suggested this should be reported here. Perhaps we could have the option of registering it scoped or something?

dotnet/aspnetcore#23847

To Reproduce

Create a new Blazor WASM project

Add NuGet reference Microsoft.Extensions.Http

Create a class as follows

	public class MyDelegatingHandler : DelegatingHandler
	{
		private readonly IServiceProvider ServiceProvider;

		public MyDelegatingHandler(IServiceProvider serviceProvider)
		{
			ServiceProvider = serviceProvider;
		}

		protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
		{
			Console.WriteLine("Http service provider hash = " + ServiceProvider.GetHashCode());
			return base.SendAsync(request, cancellationToken);
		}
	}

In Program.cs remove the registration of HttpClient and replace it with the following

builder.Services.AddScoped<MyDelegatingHandler>();
builder.Services
    .AddHttpClient("local", c => c.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
    .AddHttpMessageHandler<MyDelegatingHandler>();

Edit FetchData.razor
Remove the injection of HttpClient and relace it with

@inject IHttpClientFactory HttpClientFactory
@inject IServiceProvider ServiceProvider

Replace OnInitializedAsync with the following code

protected override async Task OnInitializedAsync()
{
	var httpClient = HttpClientFactory.CreateClient("local");
	Console.WriteLine("Page service provider hash = " + ServiceProvider.GetHashCode());
	forecasts = await httpClient.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
}

Now run the app, open the console window in the browser, and navigate to the Fetch Data page.

Expected

The hash code for the page and the http delegate should be the same.

Actual

They are different, therefore they are both running within separate IServiceProvider containers.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions