简体   繁体   中英

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.

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

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

TLDR; ViewComponent s 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 ViewComponent s. Even with a call to IServiceCollection.AddHttpClient<SomeViewComponent>() , passing an instance of HttpClient into SomeViewComponent s 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 ViewComponent s: it also applies to Controller s and TagHelper s (thanks to Chris Pratt for confirming for TagHelper s).

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 Controller s and TagHelpers respectively.

If we look at the docs more closely, it's clear that none of the examples there inject a HttpClient into Controller s 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.

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.

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");
});

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM