Context
In microservice architectures or when integrating external services, incorrect use of HttpClient is one of the most frequent errors in .NET projects. Issues like lack of connection reuse, absence of exception handling and duplicated configuration are patterns that appear repeatedly and have real production consequences.
The Problem
The starting point is a PersonService that instantiates HttpClient directly in each method: without lifecycle management, without exception handling, without centralized configuration. Each call creates a new instance, which leads to socket exhaustion and performance problems that are hard to diagnose.
public async Task<ExternalResponse<GetPersonResponse>> GetName(string name)
{
HttpClient client = new();
client.BaseAddress = new Uri("https://api.genderize.io");
var response = await client.GetAsync($"/?name={name}");
// no exception handling, no reuse
}
The Solution
The repository documents the evolution from that problematic code to a production-ready implementation, in three stages:
IHttpClientFactory with named client — centralizes configuration (base URL, timeout) in the dependency injection registration and automatically manages instance lifecycle. The service receives the factory through injection and creates clients with CreateClient().
Custom logging with HttpLoggerHandler — a custom HttpMessageHandler replaces ASP.NET’s four standard logs with two structured entries that capture method, URI, headers and body of both request and response, facilitating operational diagnostics.
Resilience with Polly — a retry policy is added (2 attempts with a 1-second interval) chained to the HttpClient via AddPolicyHandler. The logging handler and the resilience policy are composed in the same configuration:
services.AddHttpClient(HttpConstants.ClientName, client =>
{
client.BaseAddress = new Uri(externalUrl);
client.Timeout = TimeSpan.FromSeconds(15);
})
.AddHttpMessageHandler<HttpLoggerHandler>()
.AddPolicyHandler((IAsyncPolicy<HttpResponseMessage>)retryPolicy);
Technologies
- .NET / ASP.NET Core — base framework and service configuration
- C# — implementation with primary constructors and using blocks
- IHttpClientFactory — HttpClient lifecycle management
- Polly — resilience policies: retry with backoff
- HttpMessageHandler — message pipeline for custom logging
- Docker — containerization of the example