public async Task DeleteItemAsync(Guid id)
using HttpResponseMessage httpResponse =
await _httpClient.DeleteAsync($"/api/items/{id}");
httpResponse.EnsureSuccessStatusCode();
In the preceding code, the
DeleteItemAsync
method calls
DeleteAsync
. Because HTTP DELETE requests typically contain no body, the
DeleteAsync
method doesn't provide an overload that accepts an instance of
HttpContent
.
To learn more about using different HTTP verbs with
HttpClient
, see
HttpClient
.
HttpClient
lifetime management
A new
HttpClient
instance is returned each time
CreateClient
is called on the
IHttpClientFactory
. One
HttpClientHandler
instance is created per client name. The factory manages the lifetimes of the
HttpClientHandler
instances.
IHttpClientFactory
caches the
HttpClientHandler
instances created by the factory to reduce resource consumption. An
HttpClientHandler
instance may be reused from the cache when creating a new
HttpClient
instance if its lifetime hasn't expired.
Caching of handlers is desirable as each handler typically manages its own underlying HTTP connection pool. Creating more handlers than necessary can result in socket exhaustion and connection delays. Some handlers also keep connections open indefinitely, which can prevent the handler from reacting to DNS changes.
The default handler lifetime is two minutes. To override the default value, call
SetHandlerLifetime
for each client, on the
IServiceCollection
:
services.AddHttpClient("Named.Client")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
Important
HttpClient
instances created by IHttpClientFactory
are intended to be short-lived.
Recycling and recreating HttpMessageHandler
's when their lifetime expires is essential for IHttpClientFactory
to ensure the handlers react to DNS changes. HttpClient
is tied to a specific handler instance upon its creation, so new HttpClient
instances should be requested in a timely manner to ensure the client will get the updated handler.
Disposing of such HttpClient
instances created by the factory will not lead to socket exhaustion, as its disposal will not trigger disposal of the HttpMessageHandler
. IHttpClientFactory
tracks and disposes of resources used to create HttpClient
instances, specifically the HttpMessageHandler
instances, as soon their lifetime expires and there's no HttpClient
using them anymore.
Keeping a single HttpClient
instance alive for a long duration is a common pattern that can be used as an alternative to IHttpClientFactory
, however, this pattern requires additional setup, such as PooledConnectionLifetime
. You can use either long-lived clients with PooledConnectionLifetime
, or short-lived clients created by IHttpClientFactory
. For information about which strategy to use in your app, see Guidelines for using HTTP clients.
It may be necessary to control the configuration of the inner HttpMessageHandler used by a client.
An IHttpClientBuilder is returned when adding named or typed clients. The ConfigurePrimaryHttpMessageHandler extension method can be used to define a delegate on the IServiceCollection
. The delegate is used to create and configure the primary HttpMessageHandler
used by that client:
.ConfigurePrimaryHttpMessageHandler(() =>
return new HttpClientHandler
AllowAutoRedirect = false,
UseDefaultCredentials = true
Configuring the HttClientHandler
lets you specify a proxy for the HttpClient
instance among various other properties of the handler. For more information, see Proxy per client.
Additional configuration
There are several additional configuration options for controlling the IHttpClientHandler
:
Method
Description
AddTypedClient
Configures the binding between the TClient
and the named HttpClient
associated with the IHttpClientBuilder
.
ConfigureHttpClient
Adds a delegate that will be used to configure a named HttpClient
.
ConfigureHttpMessageHandlerBuilder
Adds a delegate that will be used to configure message handlers using HttpMessageHandlerBuilder for a named HttpClient
.
ConfigurePrimaryHttpMessageHandler
Configures the primary HttpMessageHandler
from the dependency injection container for a named HttpClient
.
RedactLoggedHeaders
Sets the collection of HTTP header names for which values should be redacted before logging.
SetHandlerLifetime
Sets the length of time that a HttpMessageHandler
instance can be reused. Each named client can have its own configured handler lifetime value.
Using IHttpClientFactory together with SocketsHttpHandler
The SocketsHttpHandler
implementation of HttpMessageHandler
was added in .NET Core 2.1, which allows PooledConnectionLifetime
to be configured. This setting is used to ensure that the handler reacts to DNS changes, so using SocketsHttpHandler
is considered to be an alternative to using IHttpClientFactory
. For more information, see Guidelines for using HTTP clients.
However, SocketsHttpHandler
and IHttpClientFactory
can be used together improve configurability. By using both of these APIs, you benefit from configurability on both a low level (for example, using LocalCertificateSelectionCallback
for dynamic certificate selection) and a high level (for example, leveraging DI integration and several client configurations).
To use both APIs:
Specify SocketsHttpHandler
as PrimaryHandler
and set up its PooledConnectionLifetime
(for example, to a value that was previously in HandlerLifetime
).
As SocketsHttpHandler
will handle connection pooling and recycling, then handler recycling at the IHttpClientFactory
level is not needed anymore. You can disable it by setting HandlerLifetime
to Timeout.InfiniteTimeSpan
.
services.AddHttpClient(name)
.ConfigurePrimaryHttpMessageHandler(() =>
return new SocketsHttpHandler()
PooledConnectionLifetime = TimeSpan.FromMinutes(2)
.SetHandlerLifetime(Timeout.InfiniteTimeSpan); // Disable rotation, as it is handled by PooledConnectionLifetime
Avoid typed clients in singleton services
When using the named client approach, IHttpClientFactory
is injected into services, and HttpClient
instances are created by calling CreateClient every time an HttpClient
is needed.
However, with the typed client approach, typed clients are transient objects usually injected into services. That may cause a problem because a typed client can be injected into a singleton service.
Important
Typed clients are expected to be short-lived in the same sense as HttpClient
instances created by IHttpClientFactory
(for more information, see HttpClient
lifetime management). As soon as a typed client instance is created, IHttpClientFactory
has no control over it. If a typed client instance is captured in a singleton, it may prevent it from reacting to DNS changes, defeating one of the purposes of IHttpClientFactory
.
If you need to use HttpClient
instances in a singleton service, consider the following options:
Use the named client approach instead, injecting IHttpClientFactory
in the singleton service and recreating HttpClient
instances when necessary.
If you require the typed client approach, use SocketsHttpHandler
with configured PooledConnectionLifetime
as a primary handler. For more information on using SocketsHttpHandler
with IHttpClientFactory
, see the section Using IHttpClientFactory together with SocketsHttpHandler.
Message Handler Scopes in IHttpClientFactory
IHttpClientFactory
creates a separate DI scope per each HttpMessageHandler
instance. These DI scopes are separate from application DI scopes (for example, ASP.NET incoming request scope, or a user-created manual DI scope), so they will not share scoped service instances. Message Handler scopes are tied to handler lifetime and can outlive application scopes, which can lead to, for example, reusing the same HttpMessageHandler
instance with same injected scoped dependencies between several incoming requests.
Users are strongly advised not to cache scope-related information (such as data from HttpContext
) inside HttpMessageHandler
instances and use scoped dependencies with caution to avoid leaking sensitive information.
If you require access to an app DI scope from your message handler, for authentication as an example, you'd encapsulate scope-aware logic in a separate transient DelegatingHandler
, and wrap it around an HttpMessageHandler
instance from the IHttpClientFactory
cache. To access the handler call IHttpMessageHandlerFactory.CreateHandler for any registered named client. In that case, you'd create an HttpClient
instance yourself using the constructed handler.
The following example shows creating an HttpClient
with a scope-aware DelegatingHandler
:
if (scopeAwareHandlerType != null)
if (!typeof(DelegatingHandler).IsAssignableFrom(scopeAwareHandlerType))
throw new ArgumentException($"""
Scope aware HttpHandler {scopeAwareHandlerType.Name} should
be assignable to DelegatingHandler
""");
// Create top-most delegating handler with scoped dependencies
scopeAwareHandler = (DelegatingHandler)_scopeServiceProvider.GetRequiredService(scopeAwareHandlerType); // should be transient
if (scopeAwareHandler.InnerHandler != null)
throw new ArgumentException($"""
Inner handler of a delegating handler {scopeAwareHandlerType.Name} should be null.
Scope aware HttpHandler should be registered as Transient.
""");
// Get or create HttpMessageHandler from HttpClientFactory
HttpMessageHandler handler = _httpMessageHandlerFactory.CreateHandler(name);
if (scopeAwareHandler != null)
scopeAwareHandler.InnerHandler = handler;
handler = scopeAwareHandler;
HttpClient client = new(handler);
A further workaround can follow with an extension method for registering a scope-aware DelegatingHandler
and overriding default IHttpClientFactory
registration by a transient service with access to the current app scope:
public static IHttpClientBuilder AddScopeAwareHttpHandler<THandler>(
this IHttpClientBuilder builder) where THandler : DelegatingHandler
builder.Services.TryAddTransient<THandler>();
if (!builder.Services.Any(sd => sd.ImplementationType == typeof(ScopeAwareHttpClientFactory)))
// Override default IHttpClientFactory registration
builder.Services.AddTransient<IHttpClientFactory, ScopeAwareHttpClientFactory>();
builder.Services.Configure<ScopeAwareHttpClientFactoryOptions>(
builder.Name, options => options.HttpHandlerType = typeof(THandler));
return builder;
For more information, see the full example.
See also
Dependency injection in .NET
Logging in .NET
Configuration in .NET
IHttpClientFactory
IHttpMessageHandlerFactory
HttpClient
Make HTTP requests with the HttpClient
Implement HTTP retry with exponential backoff