Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to resolve service for type 'System.Net.Http.HttpClient'

I created a ViewComponent class which call a REST API using the HttpClient, this is the code:

public class ProductsViewComponent : ViewComponent
{
    private readonly HttpClient _client;

    public ProductsViewComponent(HttpClient client)
    {
        _client = client ?? throw new ArgumentNullException(nameof(client));
    }

    public async Task<IViewComponentResult> InvokeAsync(string date)
    {
        using(var response = await _client.GetAsync($"/product/get_products/{date}"))
        {
            response.EnsureSuccessStatusCode();
            var products = await response.Content.ReadAsAsync<List<Products>>();
            return View(products);
        }
    }
}

I get this error:

InvalidOperationException: Unable to resolve service for type 'System.Net.Http.HttpClient' while attempting to activate MyApp.ViewComponents.ProductsViewComponent'

I injected the HttpClient in the ConfigureService method available in Startup in this way:

 services.AddHttpClient<FixturesViewComponent>(options =>
 {
    options.BaseAddress = new Uri("http://80.350.485.118/api/v2");
 });

UPDATE:

I registered the ProductsViewComponent too, same error.

like image 400
Charanoglu Avatar asked Sep 05 '18 13:09

Charanoglu


4 Answers

I had a similar problem - the problem was in double registration:

services.AddHttpClient<Service>();
services.AddSingleton<Service>();  // fixed by removing this line

Similar examples [just adding to clarify that it's not specific to AddSingleton, nor related to the order.]

services.AddScoped<IService, Service>();  // fixed by removing this line
services.AddHttpClient<IService, Service>();
like image 197
Petr Mašlaň Avatar answered Oct 21 '22 16:10

Petr Mašlaň


TLDR; ViewComponents do not support typed clients out of the box. To resolve this, add a call to AddViewComponentsAsServices() onto the end of the call to services.AddMvc(...).


After a pretty long chat that ran off the back of being able to reproduce your issue, we determined initially that the problem being observed is specific to ViewComponents. Even with a call to IServiceCollection.AddHttpClient<SomeViewComponent>(), passing an instance of HttpClient into SomeViewComponents constructor just refused to work.

However, sitting a new class (SomeService) between SomeComponent and HttpClient works as expected. This is what the docs refer to as a typed client. The code looks a bit like this:

// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient<SomeService>();
    // ...
}

// SomeService.cs
public class SomeService
{
    public SomeService(HttpClient httpClient)
    {
        // ...
    }
}

// SomeViewComponent.cs
public class SomeViewComponent
{
    public SomeViewComponent(SomeService someService)
    {
        // ...
    }
}

As I've already stated, this approach works - the ASP.NET Core DI system is very happy to create the instance of SomeService and its typed HttpClient instance.

To restate the original problem, take the following example code:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient<SomeViewComponent>();
    // ...
}

public class SomeViewComponent
{
    public SomeViewComponent(HttpClient httpClient)
    {
        // ...
    }
}

In this case, the ASP.NET Core DI system refuses to create an instance of SomeViewComponent due to not being able to resolve HttpClient. It turns out that this is not specific just to ViewComponents: it also applies to Controllers and TagHelpers (thanks to Chris Pratt for confirming for TagHelpers).

Interestingly, the following also works:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient<SomeViewComponent>();
    // ...
}

public class SomeViewComponent
{
    public SomeViewComponent(IHttpClientFactory httpClientFactory)
    {
        var httpClient = httpClientFactory.CreateClient("SomeViewComponent")
        // ...
    }
}

In this example, we're taking advantage of the fact that the call to AddHttpClient<SomeViewComponent> registered a named client for us.

In order to be able to inject HttpClient directly into a ViewComponent, we can add a call to AddViewComponentsAsServices when we register MVC with DI:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(...)
        .AddViewComponentsAsServices();
    // ...
}

AddControllersAsServices and AddTagHelpersAsServices can also be called to add the same support for Controllers and TagHelpers respectively.

If we look at the docs more closely, it's clear that none of the examples there inject a HttpClient into Controllers et al - there's simply no mention of this approach at all.

Unfortunately, I don't know enough about the ASP.NET Core DI system in order to be able to explain exactly why this works the way it does: The information I've provided above simply explains the what along with a solution. Chris Pratt has opened an issue in Github for the docs to be updated to expand upon this.

like image 33
Kirk Larkin Avatar answered Oct 21 '22 17:10

Kirk Larkin


I was getting a similar error in my Azure Function Version 2. As per this document, we should be able to add the IHttpClientFactory as a dependency. After adding this DI in my Azure Function, I was getting the error mentioned below.

Microsoft.Extensions.DependencyInjection.Abstractions: Unable to resolve service for type 'System.Net.Http.IHttpClientFactory' while attempting to activate 'OServiceBus.Adapter.FetchDataFromSubscription1'

The issue was that I had not override the Configure function to add the HttpClient as a registered dependency. So I just created a class called Statup in the root directory of my Azure Function as follows.

using Microsoft.Azure.Functions.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(ServiceBus.Adapter.Startup))]
namespace ServiceBus.Adapter {
    public class Startup: FunctionsStartup {
        public override void Configure(IFunctionsHostBuilder builder) {
            builder.Services.AddHttpClient();
        }
    }
}

After adding this, my function started working properly. Hope it helps.

like image 4
Sibeesh Venu Avatar answered Oct 21 '22 18:10

Sibeesh Venu


I had a similar error message trying to inject a wrapper for an external REST service to my controller as an interface. I needed to change the following in ConfigureServices:

services.AddHttpClient<IMyServiceWrapper>("MyServiceWrapper", client =>
{
   client.BaseAddress = new Uri("http://some_service/api");
}

to

services.AddHttpClient<IMyServiceWrapper, MyServiceWrapper>("MyServiceWrapper", client =>
{
   client.BaseAddress = new Uri("http://some_service/api");
}

in order to be able to use the interface in the constructor of my controller:

public MyController(IMyServiceWrapper myService)
{
  _myService = myService;
}

Useful for testing myController using a mock service.

like image 3
Martin Dekker Avatar answered Oct 21 '22 17:10

Martin Dekker