I am trying the most simple IHttpClientFactory
use case
public interface IWeatherForecast
{
Task<string> Get();
}
class WeatherForecast : IWeatherForecast
{
private readonly HttpClient httpClient;
public WeatherForecast (IHttpClientFactory httpClientFactory)
{
httpClient = httpClientFactory.CreateClient();
}
public async Task<string> Get()
{
var resp = await httpClient.GetAsync("https://testwebapiforuseinsamples.azurewebsites.net/weatherforecast");
return JsonConvert.SerializeObject(resp.Content.ReadAsStringAsync());
}
}
and then instantiate it
static async Task Main(string[] args)
{
var container = new ServiceCollection();
container.AddHttpClient<IWeatherForecast, WeatherForecast>();
var serviceProvider = container.BuildServiceProvider();
var resp = await serviceProvider.GetRequiredService<WeatherForecast>().Get();
}
However when I run it, it throws
System.InvalidOperationException: 'A suitable constructor for type 'HttpClientFactoryExample.WeatherForecast' could not be located. Ensure the type is concrete ...
Can someone point it what is wrong with this code. I was expecting that after adding WeatherForecast
service to DI, I will be able to get an initialized instance of it from the container.
CodePudding user response:
When you register type in Service collection as HTTP client container.AddHttpClient<IWeatherForecast, WeatherForecast>()
, it MUST contains HttpClient
in constructor. In your case it should be:
public WeatherForecast(HttpClient httpClient)
{
this.httpClient = httpClient;
}
Another option is to register separately the HttpClientFactory
and your WeatherForecast
service:
container.AddHttpClient();
container.AddTransient<IWeatherForecast, WeatherForecast>();
and then use the service with HttpClientFactory:
private readonly HttpClient httpClient;
public WeatherForecast(IHttpClientFactory factory)
{
this.httpClient = factory.CreateClient();
}
CodePudding user response:
When using the type client registration that takes an abstraction like you did here
//...
container.AddHttpClient<IWeatherForecast, WeatherForecast>();
//...
it registers the abstraction as the client type, not the implementation.
Adds the
IHttpClientFactory
and related services to theIServiceCollection
and configures a binding between theTClient
type and a namedHttpClient
. The client name will be set to the type name ofTClient
.
Reference IHttpClientBuilder AddHttpClient<TClient,TImplementation>
Because you try to resolve the implementation WeatherForecast
//...
var resp = await serviceProvider.GetRequiredService<WeatherForecast>().Get();
//...
it will fail because
System.InvalidOperationException: No service for type 'WeatherForecast' has been registered.
What you should have actually resolved was the abstraction IWeatherForecast
//...
var resp = await serviceProvider.GetRequiredService<IWeatherForecast>().Get();
//...
which the container knows is mapped to the WeatherForecast
implementation and will then initialize it along with its dependencies.
With that fixed, you also need to update the service to explicitly depend on HttpClient
since this is a typed client registration
class WeatherForecast : IWeatherForecast {
private readonly HttpClient httpClient;
public WeatherForecast (HttpClient httpClient) {
this.httpClient = httpClient;
}
public async Task<string> Get() {
var resp = await httpClient.GetAsync("https://testwebapiforuseinsamples.azurewebsites.net/weatherforecast");
return JsonConvert.SerializeObject(resp.Content.ReadAsStringAsync());
}
}
Reference Use IHttpClientFactory to implement resilient HTTP requests