Skip to content

Is dynamic IHttpClientFactory registration a bug or a feature? #36378

Open
@daiplusplus

Description

@daiplusplus

Is your feature request related to a problem? Please describe.

My application needs runtime-defined named HttpClient configuration, and I'm using IHttpClientFactory. My application also needs to configure different DelegatingHandler chains unique to each named configuration (for injecting configuration-specific Bearer tokens and access_token refresh logic).

My application is a headless Windows service that stores 1...N-many named HttpClient configurations on-disk and it uses an injected-service to read the stored configurations, so the IServiceProvider isn't available to read the configuration while it's still calling IServiceCollection.AddHttpClient<MyClient>( ... ) (and the idea of maintaining multiple root IServiceProvider instances isn't appealing either).

Ostensibly, the IHttpClientFactory requires all named HttpClients to be set-up during service-configuration - which would make it unsuitable for my application - however I noticed that DefaultHttpClientFactory's pool of HttpMessageHandler instances can be added-to even after configuration has completed - this does not seem to be documented either way - so I'm not sure if it's a bug (i.e. it should be immutable) or a feature (for runtime-configured HttpClient and HttpMessageHandlerBuilder instances).

Describe the solution you'd like

  • A statement from the PM responsible declaring the immutability of IHttpClient's configuration.
  • If it is immutable, then fix the bug.
  • If it's mutable, then make it easier to add dynamic configuration of HttpClient without jumping through hoops with IConfigureNamedOptions.

Describe alternatives you've considered

Right now my application depends on the current behaviour for dynamic HttpClient configuration, like so:

	internal class DynamicHttpClientFactoryConfiguration : IConfigureNamedOptions<HttpClientFactoryOptions>
	{
		public void Configure( String httpClientName, HttpClientFactoryOptions options )
		{
			IAccessTokenRenewerFactoryStore store = this.sp.GetRequiredService<IAccessTokenRenewerFactoryStore>();

			if( store.TryGetConfiguration( httpClientName: httpClientName, out MyHttpClientConfiguration cfg, out AccessTokenRenewer atr ) )
			{
				options.HttpClientActions.Add( httpClient =>
				{
					httpClient.BaseAddress = cfg.Authority;
				} );

				options.HttpMessageHandlerBuilderActions.Add( httpMessageHandlerBuilder =>
				{
					AccessTokenRenewDelegatingHandler delegatingHandler = new AccessTokenRenewDelegatingHandler( atr );
					httpMessageHandlerBuilder.AdditionalHandlers.Add( delegatingHandler );
				} );
			}
		}
	}

And it "just works" whenever code anywhere in my project calls IHttpClientFactory.CreateClient( configurationName ) (provided that configurationName exists in my IAccessTokenRenewerFactoryStore).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions