Charanoglu
Charanoglu

Reputation: 1339

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.

Upvotes: 45

Views: 112701

Answers (8)

Yusuf &#220;nl&#252;
Yusuf &#220;nl&#252;

Reputation: 23

if you are up version .net 6.0 add below code in to program.cs

builder.Services.AddHttpClient();

Upvotes: 0

Enrico
Enrico

Reputation: 3471

For me the fix was to add:

services.AddHttpClient(); 

Upvotes: 4

Petr Mašlaň
Petr Mašlaň

Reputation: 1070

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>();

Upvotes: 97

aiodintsov
aiodintsov

Reputation: 2605

Maybe it will help, but in my situation this worked:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService,MyService>(); // my usual DI injection of a service that can be mocked
    services.AddHttpClient<IMyService,MyService>(client => {
        client.BaseAddress = new Uri("https://myservice.com/api");
    }); // notice that I use IMyService for the reference of the registration AND implementation to where it will be injected.
}

public class MyService
{
    public MyService(HttpClient client)
    {
        // client.BaseAddress is properly set here
    }
}

public class MyController : Controller
{
    public MyController(IMyService service) // used by the interface
    {}
}

I've tried services.AddHttpClient<IMyService>() as well, which would not resolve due to lack of it's constructor. Also tried services.AddHttpClient<MyService>() as above, but it would not resolve the configured instance, as described above.

So the important part is that class that is used to reference the resolved type needs to be used. So this also works:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<MyService>(); // registering the type itself, not via interface
    services.AddHttpClient<MyService>(client => {
        client.BaseAddress = new Uri("https://myservice.com/api");
    }); // it's ok here, since it will be resolved by it's own type name
}

public class MyService
{
    public MyService(HttpClient client)
    {
        // client.BaseAddress is properly set here
    }
}

public class MyController : Controller
{
    public MyController(MyService service) // used by the type directly
    {}
}

It kind of makes sense, but documentation and examples could be better.

Upvotes: 1

Martin Dekker
Martin Dekker

Reputation: 230

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.

Upvotes: 4

Sibeesh Venu
Sibeesh Venu

Reputation: 21779

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.

Upvotes: 6

Kirk Larkin
Kirk Larkin

Reputation: 93233

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.

Upvotes: 17

Henk Mollema
Henk Mollema

Reputation: 46621

It seems that you've got two view components mixed up. You're registering the FixturesViewComponent as a "named HTTP client" yet you attempt to inject an HttpClient instance in the ProductsViewComponent.

Changing the HttpClient registration to ProductsViewComponent should help:

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

Upvotes: 2

Related Questions