Customized HTTP Clients in .NET Core with HttpClientFactory
In the realm of modern .NET Core applications, the efficient management of HTTP clients is pivotal for achieving optimal performance and reliability. Enter HttpClientFactory
, a feature that simplifies this process, offering a flexible and structured approach. This article will delve into the intricacies of constructing robust HTTP clients in .NET Core using HttpClientFactory
along with custom configurations.
Setting Up HttpClientFactory
To kickstart the process, harness the power of HttpClientFactory
by adding it to your application's services. Within your startup code, include the following line:
services.AddHttpClients();
This lays the foundation for centralized configuration and management of HTTP clients.
Extending Services with Custom HttpClient Configuration
Tailoring HTTP client configurations to meet specific requirements is achieved through a custom extension method. In the example below, the AddHttpClients
extension method is introduced:
public static class HttpClientExtensions
{
public static void AddHttpClients(this IServiceCollection services)
{
services
.AddHttpClient("ApiClient", (provider, client) => {
var settings = provider.GetRequiredService<ServiceSettings>();
client.BaseAddress = new Uri(settings.BaseUrl);
client.DefaultRequestHeaders.Add("Accept", MediaTypeNames.Application.Json);
client.Timeout = TimeSpan.FromMilliseconds(settings.HttpTimeoutInMilliseconds);
})
.SetHandlerLifetime(System.Threading.Timeout.InfiniteTimeSpan)
.ConfigurePrimaryHttpMessageHandler(provider => {
var settings = provider.GetRequiredService<ServiceSettings>();
return HttpClientHandlerHelper.ConfigureHttpClientHandler(settings.CertificateThumbprintOrSubjectName);
})
}
}
This extension method facilitates fine-tuned configuration of HTTP clients based on the specific needs of your application.
Our settings class and appsettings accordingly:
public class ServiceSettings : ISettings
{
public string BaseUrl { get; set; }
public string CertificateThumbprintOrSubjectName { get; set; }
public int HttpTimeoutInMilliseconds { get; set; }
}
"ServiceSettings": {
"BaseUrl": "https://serviceUrl:9191/", //change accordingly with your url
"CertificateThumbprintOrSubjectName": "testCertificate", //change accordingly if using any certificate
"HttpTimeoutInMilliseconds": 1000
},
HttpClientHandler Configuration
A critical component in customizing the behavior of HTTP clients is the HttpClientHandler
. The HttpClientHandlerHelper
class provides the ConfigureHttpClientHandler
method, which handles certificates, SSL protocols, and decompression:
public static class HttpClientHandlerHelper
{
public static HttpClientHandler CConfigureHttpClientHandler(string certificateThumbprintOrSubjectName)
{
try
{
var certificate = GetCertificateFromCertStore(certificateThumbprintOrSubjectName);
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = SslProtocols.Tls12;
handler.ServerCertificateCustomValidationCallback = (_, _, _, _) => true;
handler.AutomaticDecompression = System.Net.DecompressionMethods.GZip;
if (certificate != null)
{
handler.ClientCertificates.Add(certificate);
}
else
{
Log.Warning(
"Certificate could not be found in the Windows certificate store. Certificate Identifier: {CertificateIdentifier}",
certificateThumbprintOrSubjectName);
}
return handler;
}
catch (Exception ex)
{
throw new Exception(ex);
}
}
private static X509Certificate2 GetCertificateFromCertStore(string certificateThumbprintOrSubjectName)
{
var searchValue = certificateThumbprintOrSubjectName;
if (string.IsNullOrWhiteSpace(searchValue))
return null;
using var certStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadOnly);
return certStore.FindCertificate(X509FindType.FindByThumbprint, searchValue) ??
certStore.FindCertificate(X509FindType.FindBySubjectName, searchValue);
}
private static X509Certificate2 FindCertificate(this X509Store certStore, X509FindType findType, string searchValue)
{
var certificate = certStore.Certificates
.Find(findType, searchValue, true)
.FirstOrDefault(c => c.HasPrivateKey);
if (certificate != null)
{
Log.Information(
"Certificate loaded by using windows certificate store. Certificate Identifier: {CertificateIdentifier}",
searchValue);
}
return certificate;
}
}
Conclusion
By leveraging HttpClientFactory
and custom configurations, .NET Core developers can build resilient and efficient HTTP clients. This approach simplifies client management and enhances the flexibility of handling various scenarios. As you integrate these practices into your projects, keep in mind the importance of fine-tuning configurations to match your application's unique requirements.