If I use _httpClientFactory.CreateClient() in a singleton (no name for the client, I set it up when I use it)
Should I add/specify an empty services.AddHttpClient(); at startup or is that not necessary?
You should only specify empty services.AddHttpClient(); on startup. You could pass it name parameter if you want to configure "specific" HttpClient that you will later call by name (for example add base address or headers). Otherwise IHttpClientFactory will give you not configured one (like calling new HttpClient())
For example:
services.AddHttpClient("MyApiClient").ConfigureHttpClient(client =>
{
client.BaseAddress = new Uri(configuration["MyApiUrl"]);
});
and later calling factory like:
_httpClientFactory.CreateClient("MyApiClient");
will give you HttpClient with configured base address.
The services.AddHttpClient(); in your startup class is used to configure an IHttpClientFactory instance,
which is then used to create and manage named HttpClient instances.
If you are creating an instance of HttpClient directly with _httpClientFactory.CreateClient(), without specifying a named client,
what i suggest is you don't need to add the services.AddHttpClient(); line in the Startup class.
FACT:
This line is necessary only if you plan to use the IHttpClientFactory to create named HttpClient instances.
for More visit: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-7.0
Related
I'd like to add a SocketsHttpHandler to an HttpClient that I am creating via AddHttpClient in Startup.cs. The point of that is to be able to inject instrumentation etc. in the HttpClient via a factory.
This does not give me the option to use the constructor of HttpClient, which is where you'd usually add a SocketsHttpHandler.
Also, I don't see any obvious property etc. I can use to add it later.
...aaaand as is traditional I found the answer five minutes later. Never matters how long you look before, does it now?
How I can change configuration of HttpMessageHandler from Polly retry?
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
{
MaxConnectionsPerServer = 3,
})
I have an application where we communicate with hundreds of HTTPs endpoints. The application is a proxy of sorts.
When testing with polly, I've noticed that if one endpoint, say api.endpoint1.com fails, the calls to api.endpoint2.com and api.endpoint3.com will also be in an open/blocked state.
This makes sense as I've only defined one policy, but what is the recommended approach to handling this scenario so that calls to unrelated endpoints are not blocked due to another having performance issues?
Do I create a collection of Policy's, one for each endpoint or is there a way to supply a context key of sorts(i.e. the hostname) to scope the failures to a given host endpoint?
I've reviewed Polly's docs regarding context keys and it appears these are a way to exchange data back and forth and not what I'm looking for here.
var policy = Policy
.Handle<TimeoutException>()
.CircuitBreaker(1, TimeSpan.FromSeconds(1));
//dynamic, large list of endpoints.
var m = new HttpRequestMessage(HttpMethod.Post, "https://api.endpoint1.com")
{
Content = new StringContent("some JSON data here", Encoding.UTF8,"application/json")
};
policy.Execute(() => HTTPClientWrapper.PostAsync(message));
Yes, your best bet is to create a separate policy per endpoint. This is better than doing it per host because an endpoint may be slow responding for a reason that's specific to that endpoint (e.g., stored procedure is slow).
I've used a Dictionary<string, Policy> with the endpoint URL as the key.
if (!_circuitBreakerPolices.ContainsKey(url))
{
CircuitBreakerPolicy policy = Policy.Handle<Exception>().AdvancedCircuitBreakerAsync(
onBreak: ...
);
_circuitBreakerPolicies.Add(url, policy);
}
await _circuitBreakerPolicies[url].ExecuteAsync(async () => ... );
Here is my alternative solution which does not maintain a collection of policies (either via an IDictionary or via an IConcurrentPolicyRegistry) rather it takes advantage of named typed clients. (Yes you have read correctly named and typed HttpClients)
The named and typed clients
Most probably you have heard (or even used) named or typed clients. But I'm certain that you haven't used named and typed clients. It is a less documented feature of HttpClientFactory + HttpClient combo.
If you look at the different overloads of the AddHttpClient extension method then you can spot this one:
public static IHttpClientBuilder AddHttpClient<TClient,TImplementation>
(this IServiceCollection services, string name, Action<HttpClient> configureClient)
where TClient : class where TImplementation : class, TClient;
It allows us to register a typed client and give a logical name to it. But how can I get the proper instance? That's where the ITypedHttpClientFactory comes into the picture. It allows us to create a typed client from a named client. Wait what??? I hope you will understand this sentence at the end of this post. :)
The typed client
For the sake of simplicity let me use this typed client as an example:
public interface IResilientClient
{
Task GetAsync();
}
public class ResilientClient: IResilientClient
{
private readonly HttpClient client;
public ResilientClient(HttpClient client)
{
this.client = client;
}
public Task GetAsync()
{
//TODO: implement it properly
return Task.CompletedTask;
}
}
The named and typed clients registration
Let suppose you have a list of downstream system urls (urls). Then you can register multiple typed client instances with different unique names and base urls
foreach (string url in urls)
{
builder.Services
.AddHttpClient<IResilientClient, ResilientClient>(url,
client => client.BaseAddress = new Uri(url))
.AddPolicyHandler(GetCircuitBreakerPolicy());
}
Here I have used the url as the unique name
So, we can get the appropriate instance based on the downstream url
The policy definition
private IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
=> Policy<HttpResponseMessage>
.Handle<TimeoutException>()
.CircuitBreakerAsync(1, TimeSpan.FromSeconds(1));
I have modified the policy to support async: .CircuitBreakerAsync
I've also amended it to be suitable with the AddPolicyHandler: Policy<HttpResponseMessage>
It is defined as a function so each registered named typed client will have a different Circuit Breaker instance
The usage
This is be a bit clumsy, but I think it is okay. So, wherever you want to use one of the named typed clients you have to inject two interfaces:
IHttpClientFactory: To be able to create a named HttpClient
ITypedHttpClientFactory<ResilientClient>: To be able to create a typed client from the named HttpClient
public XYZService(
IHttpClientFactory namedClientFactory,
ITypedHttpClientFactory<ResilientClient> namedTypedClientFactory)
{
var namedClient = namedClientFactory.CreateClient(xyzUrl);
var namedTypedClient = namedTypedClientFactory.CreateClient(namedClient);
}
Please note that you have to use ResilientClient concrete class as the type parameter not the interface IResilientClient
If you would use the interface then you would receive the following runtime error:
InvalidOperationException: A suitable constructor for type 'IResilientClient' could not be located. Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments. Also ensure no extraneous arguments are provided.
Summary
With the named and typed client feature of AddHttpClient we can register multiple instances of the same typed client
With the IHttpClientFactory we can retrieve a registered named client which has the proper BaseAddress and decorated with a Circuit Breaker
With the ITypedHttpClientFactory we can convert the named client into a typed client to be able to hide low-level API usage
Related sample application's github repository
I am writing a piece of middleware (maybe I want a scoped service??), I guess my plan is to have some kind of multi-tenant scenario.
If for example, I have 2 domains that respond on this service:
www.domain1.com
www.domain2.com
I want to capture the request when it starts, look at the host name that is being used and then set some other object to be available through Dependency Injection for everything further up the pipeline.
It seems that middleware should be the right way to achieve this, but not sure how to do the final step.
My options seem to be:
Middleware
Register Singleton service to access database
Register early to be the first item of middleware to capture the request.
Analyse Request Object and build custom configuration object
Add custom configuration as a scoped object to the DI container for use by other services
Service
Register Singleton service to access database
Register Singleton service for IHttpContextAccessor
Register Scoped? Service - to do equivalent of middleware
Analyse the request object and build custom configuration object
Register custom object as new scoped object in the DI container
My assumption is that the Service is able to register the custom scoped object as it is still within the ConfigureServices method of the startup.cs
However, with middleware it is initialised through the Configure method by which point the DI container has already been built?
You can use the factory-overload of AddScoped for the service you want to be different per tenant/request. Here's an example:
services.AddScoped<IServiceForTenant>(sp =>
{
var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
var serviceForTenant = new ServiceForTenant();
// TODO: Use httpContextAcccessor.HttpContext to configure serviceForTenant.
return serviceForTenant;
});
For each request that comes in to your ASP.NET Core application, the code above will run when you first request IServiceForTenant in e.g. a controller. At this point, your code can read from IHttpContextAccessor.HttpContext and make whatever decisions it needs in order to create the implementation instance for IServiceForTenant. This same instance will then be used for the rest of the request (i.e. further up the pipeline).
The argument passed into AddScoped is Func<IServiceProvider, T>. All you need to provide here is a delegate of some kind, which could be done in one of many ways. Here's some examples:
You could just wrap the call into its own extension method, like this:
public static void AddServiceForTenant(this IServiceCollection services)
{
services.AddScoped<IServiceForTenant>(sp =>
{
// ...
});
}
In ConfigureServices:
services.AddServiceForTenant();
Use a class with a static method:
public static class ServiceForTenantFactory
{
public static ITenantForService Create(IServiceProvider sp)
{
// ...
}
}
In ConfigureServices:
services.AddScoped(ServiceForTenantFactory.Create);
Use a class with an instance method:
public class ServiceForTenantFactory
{
public ITenantForService Create(HttpContext httpContext)
{
// ...
}
}
In ConfigureServices:
services.AddScoped(sp =>
{
var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
var serviceForTenantFactory = new ServiceForTenantFactory(); // Or use DI.
return serviceForTenantFactory.Create(httpContextAccessor.HttpContext);
});
This last option is the most flexible, as you could even resolve ServiceForTenantFactory itself from DI and it can have its own dependencies, etc. Note also that Create here takes the HttpContext directly (as an example).
As I've already said, there are yet more options than the three of shown, but this should be a good base to work with.
I have an application (IJobInit) that uses a list from JSON settings to create multiple instances of a class (IJob). This class does some work using two other dependencies, IInputClient and IOutputClient. It uses M.Extensions.DependencyInjection to create a container which is handed off to AutoFac to create an IContainer.
IJobInit(IContainer container)
I would like IInputClient to be configured different for each instance of IJob. Speficially, I'd like to pass in a secret for it to use. The result would be:
IInputClient(HttpClient client)
where HttpClient is configured using ConfigureHttpClient such that IJob does not know that it is pre-authenticated. This would also be suitable:
IInputClient(ISecretProvider secretsProvider, string secretName)
The end result is three instances of IJob with IInputClient configured differently.
IJob(IInputClient inputClient1, IOutputClient outputClient)
IJob(IInputClient inputClient2, IOutputClient outputClient)
IJob(IInputClient inputClient3, IOutputClient outputClient)
How do I achieve this? I was looking at Autofac scopes but those controlwhen an instance is created without any control over its configuration (unless I missed it).
A colleague suggested that I could host each instance of IJob in its own process with its own configuration which is possible but I'm trying to host all the jobs in a single Azure Function and use the list in config to create the inner jobs.
Thanks!
I'm not totally happy with this solution but it works for now.
private async Task<IInputClient> GetClientAsync(string secretId)
{
HttpClient httpClient = this.httpClientFactory.CreateClient();
string secret = await this.secretsProvider.GetSecretAsync(secretId);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Concat(":", secret))));
return this.scope.Resolve<IInputClient>(new TypedParameter(typeof(HttpClient), httpClient));
}
I'm trying to use the new IHttpClientFactory with an ASP.net Core 2.1 WEB API application.
public void ConfigureServices(IServiceCollection services)
{
// other services configuration
services.AddHttpClient();
}
In my ConfigureServices method, I don't see a way to add the IHttpClientFactoryand configure it to use WinHttpHandler.
The AddHttpClient methods that return a IHttpClientBuilder gives you access to methods that configure the HttpMessageHandler but those have to be derived from DelegatingHandler but WinHttpHandler does not derive from DelegatingHandler.
See no way to tell HttpClient to use WinHttpHandler when being constructed.
Figured it out.
Thanks to tip given by #Nkosi in the comments!
I solved this by using a named HttpClient when registering the HttpClient service and then configuring the message handler to use WinHttpHandler as the PrimaryHandler
services.AddHttpClient<HttpClient>("WinHttp")
.ConfigureHttpMessageHandlerBuilder(c =>
{
c.PrimaryHandler = new WinHttpHandler() { WindowsProxyUsePolicy = WindowsProxyUsePolicy.UseWinInetProxy };
});
Then when using the IHttpClientFactory, specify the name you gave when registering it.
var httpClient = this._httpClientFactory.CreateClient("WinHttp");
Your HttpClient will now use WinHttpHandler!
NOTE
To use WinHttpHandler you must add nuget package System.Net.Http.WinHttpHandler