Reputation: 4009
I have a third party api I am using for a number of my Services in .NET Core Web API project.
So, I have something like below:
services.AddHttpClient<IUserService, UserService>(client =>
{
client.BaseAddress = new Uri(Environment.GetEnvironmentVariable("EXTERNAL_SERVICE_BASE_URL") ??
throw new ArgumentNullException("EXTERNAL_SERVICE_BASE_URLcannot be null"));
client.DefaultRequestHeaders.Add("removed", "removed");
}).ConfigurePrimaryHttpMessageHandler(() =>
{
HttpClientHandler clientHandler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
};
clientHandler.ClientCertificates.Add(new X509Certificate2(securitySession.CertificateData.Data, securitySession.CertificateData.Password));
return new HttpClientXRayTracingHandler(clientHandler);
})
.AddPolicyHandler(HttpPolicies.GetRetryPolicy())
.SetHandlerLifetime(TimeSpan.FromSeconds(1));
This is fine and I can access the HttpClient
in UserService
and call my External API fine. However what I am going to need now is more Services in my code - but they use the same EXTERNAL_API - so lets say and AccountService
and a CustomerService
- and i'll end up with this:
services.AddHttpClient<IAccountService, AccountService>(client =>
{
client.BaseAddress = new Uri(Environment.GetEnvironmentVariable("EXTERNAL_SERVICE_BASE_URL") ??
throw new ArgumentNullException("EXTERNAL_SERVICE_BASE_URLcannot be null"));
client.DefaultRequestHeaders.Add("removed", "removed");
}).ConfigurePrimaryHttpMessageHandler(() =>
{
HttpClientHandler clientHandler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
};
clientHandler.ClientCertificates.Add(new X509Certificate2(securitySession.CertificateData.Data, securitySession.CertificateData.Password));
return new HttpClientXRayTracingHandler(clientHandler);
})
.AddPolicyHandler(HttpPolicies.GetRetryPolicy())
.SetHandlerLifetime(TimeSpan.FromSeconds(1));
services.AddHttpClient<ICustomerService, CustomerService>(client =>
{
client.BaseAddress = new Uri(Environment.GetEnvironmentVariable("EXTERNAL_SERVICE_BASE_URL") ??
throw new ArgumentNullException("EXTERNAL_SERVICE_BASE_URLcannot be null"));
client.DefaultRequestHeaders.Add("removed", "removed");
}).ConfigurePrimaryHttpMessageHandler(() =>
{
HttpClientHandler clientHandler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
};
clientHandler.ClientCertificates.Add(new X509Certificate2(securitySession.CertificateData.Data, securitySession.CertificateData.Password));
return new HttpClientXRayTracingHandler(clientHandler);
})
.AddPolicyHandler(HttpPolicies.GetRetryPolicy())
.SetHandlerLifetime(TimeSpan.FromSeconds(1));
The only change being the Service in my App that is using the HttpClient
. Is there a way I can move all the common plumbing of the wire up to a private method that can be called when I am adding the http client to each of my services?
Upvotes: 2
Views: 336
Reputation: 22829
I would suggest to have a single named client
services.AddHttpClient("commonClient", client =>
{
client.BaseAddress = new Uri(Environment.GetEnvironmentVariable("EXTERNAL_SERVICE_BASE_URL") ??
throw new ArgumentNullException("EXTERNAL_SERVICE_BASE_URL cannot be null"));
client.DefaultRequestHeaders.Add("removed", "removed");
}).ConfigurePrimaryHttpMessageHandler(() =>
{
HttpClientHandler clientHandler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
};
clientHandler.ClientCertificates.Add(new X509Certificate2(securitySession.CertificateData.Data, securitySession.CertificateData.Password));
return new HttpClientXRayTracingHandler(clientHandler);
})
.AddPolicyHandler(HttpPolicies.GetRetryPolicy())
.SetHandlerLifetime(TimeSpan.FromSeconds(1));
and 3 typed clients
services.AddHttpClient<IUserService, UserService>();
services.AddHttpClient<IAccountService, AccountService>();
services.AddHttpClient<ICustomerService, CustomerService>();
The usage of these components are a bit different than using just a named or just a typed client
readonly IUserService client;
public XYZController(IHttpClientFactory namedClientFactory, ITypedHttpClientFactory<UserService> typedClientFactory)
{
var namedClient = namedClientFactory.CreateClient("commonClient");
client = typedClientFactory.CreateClient(namedClient);
}
IHttpClientFactory
we retrieve the "commonClient"
UserService
by passing the previously retrieved named clientNOTE: The type parameter of ITypedHttpClientFactory
must be the concrete type not the interface otherwise you would receive an InvalidOperationException
Upvotes: 1
Reputation: 11392
You can create an IServiceCollection
extension method:
public static class ServiceCollectionExtensions
{
// Change securitySession parameter to the appropriate type
public static void AddCustomHttpClient<TClient, TImplementation>(this IServiceCollection services, SecuritySession securitySession)
where TClient : class
where TImplementation : class, TClient
{
services.AddHttpClient<TClient, TImplementation>(client =>
{
client.BaseAddress = new Uri(Environment.GetEnvironmentVariable("EXTERNAL_SERVICE_BASE_URL") ??
throw new ArgumentNullException("EXTERNAL_SERVICE_BASE_URLcannot be null"));
client.DefaultRequestHeaders.Add("removed", "removed");
}).ConfigurePrimaryHttpMessageHandler(() =>
{
HttpClientHandler clientHandler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
};
clientHandler.ClientCertificates.Add(new X509Certificate2(securitySession.CertificateData.Data, securitySession.CertificateData.Password));
return new HttpClientXRayTracingHandler(clientHandler);
})
.AddPolicyHandler(HttpPolicies.GetRetryPolicy())
.SetHandlerLifetime(TimeSpan.FromSeconds(1));
}
}
And use it like this:
services.AddCustomHttpClient<IUserService, UserService>(securitySession);
services.AddCustomHttpClient<IAccountService, AccountService>(securitySession);
services.AddCustomHttpClient<ICustomerService, CustomerService>(securitySession);
Upvotes: 1