Using HttpClientFactory in .NET Core for Resilient HTTP Calls
Learn how to use .NET Core's HttpClientFactory to create reusable, resilient HTTP clients with named and typed configurations, add Polly policies, and avoid common pitfalls like socket exhaustion and DNS caching issues.
26 Dec 2025, 23:30 UTC

Why HttpClientFactory Matters
In ASP.NET Core, creating HttpClient instances directly inside request handlers can lead to socket exhaustion and stale DNS entries. HttpClientFactory centralises creation, reuses HttpMessageHandler instances, and integrates Polly for retry, circuit‑breaker, and timeout policies. The result is a more reliable, testable, and resource‑efficient HTTP client layer.
Setting Up the Factory
Register the factory once in Program.cs or Startup.cs:
builder.Services.AddHttpClient();
This adds the IHttpClientFactory service and the default handler pool. From here you can create named or typed clients.
Named Clients
Named clients let you configure base addresses, headers, and handlers per name. For example, a weather API client:
builder.Services.AddHttpClient("Weather", client =>
{
client.BaseAddress = new Uri("https://api.weather.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
});
Inject the factory where needed:
public class WeatherService
{
private readonly HttpClient _client;
public WeatherService(IHttpClientFactory factory)
{
_client = factory.CreateClient("Weather");
}
public async Task GetForecastAsync(string location)
{
var response = await _client.GetAsync($"/forecast/{location}");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
Typed Clients
Typed clients expose a domain‑specific API and hide HttpClient details. Register a typed client by passing the implementation type:
builder.Services.AddHttpClient();
Implementation example:
public class WeatherClient
{
private readonly HttpClient _client;
public WeatherClient(HttpClient client)
{
_client = client; // injected by the factory
}
public async Task GetAsync(string location)
{
var response = await _client.GetAsync($"/forecast/{location}");
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize(json);
}
}
Adding Polly Policies
Polly integrates seamlessly. Attach a retry policy that backs off exponentially:
builder.Services.AddHttpClient("Weather")
.AddPolicyHandler(
Policy
.HandleTransientHttpError()
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
);
For circuit‑breaker:
builder.Services.AddHttpClient("Weather")
.AddPolicyHandler(
Policy
.HandleTransientHttpError()
.CircuitBreakerAsync(2, TimeSpan.FromMinutes(1))
);
Verifying Correctness
- Handler Reuse: Resolve two named clients and compare
GetHashCode()of theirHttpMessageHandler:var client1 = factory.CreateClient("Weather"); var client2 = factory.CreateClient("Weather"); Console.WriteLine(client1.GetHashCode() == client2.GetHashCode()); // true - Base Address: Send a request and inspect the
RequestUriin logs to confirm the configured base address. - Retry Count: Configure a mock endpoint that fails the first two times. Verify that the retry policy logs three attempts.
- DNS Update: Point a hostname to a new IP, trigger a request, and confirm that the factory picks up the new address without restarting.
- Socket Count: Run a high‑volume test and monitor
netstatorProcess Explorer. A factory‑managed client keeps the number of open sockets bounded.
Common Mistakes & Limits
- Direct HttpClient Instantiation: Creating
new HttpClient()in a loop exhausts sockets. Always use the factory. - Duplicate Names: Registering the same named client twice overwrites the first. Keep names unique.
- Excessive Retries: More than 5 retries can hide real problems and add latency. Keep retry counts reasonable.
- SSL/TLS Mismatch: If the target server requires a specific protocol, configure
SslProtocolson the handler or useConfigurePrimaryHttpMessageHandler. - Static HttpClient: Storing a client in a static field bypasses the factory’s lifecycle. Use factory‑created instances only.
- Exposing Handlers: Do not expose
HttpMessageHandleroutside the factory; it should remain internal.
When HttpClientFactory Is Not Enough
For very high‑performance scenarios, consider using SocketsHttpHandler directly with custom pooling. However, for most web applications, the factory’s built‑in pooling and Polly integration provide a robust foundation.
Takeaway
Register HttpClientFactory once, then create named or typed clients with optional Polly policies. Verify handler reuse, base address, and resiliency. Avoid common pitfalls to keep your HTTP layer efficient and reliable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.