无法解析类型为'System.Net.Http.HttpClient'的服务

时间:2018-09-05 13:53:03

标签: c# asp.net asp.net-core

我创建了一个ViewComponent类,它使用REST API调用HttpClient,这是代码:

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

我收到此错误:

  

InvalidOperationException:尝试激活MyApp.ViewComponents.ProductsViewComponent'时无法解析类型为'System.Net.Http.HttpClient'的服务

我以这种方式通过HttpClient中可用的ConfigureService方法注入了Startup

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

更新:

我也注册了ProductsViewComponent,同样的错误。

6 个答案:

答案 0 :(得分:6)

我有一个类似的问题-问题在于双重注册:

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

答案 1 :(得分:2)

TLDR; ViewComponent个不支持typed clients。要解决此问题,请将对AddViewComponentsAsServices()的调用添加到对services.AddMvc(...)的调用的末尾。


经过漫长的chat后,才能够重现您的问题,我们最初确定观察到的问题特定于ViewComponent个问题。即使调用了IServiceCollection.AddHttpClient<SomeViewComponent>(),也将HttpClient的实例传递到SomeViewComponent的构造函数中只是拒绝工作。

但是,在{em> SomeServiceSomeComponent之间坐在新班(HttpClient)上可以正常工作。这就是文档所称的typed client。代码看起来像这样:

// 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)
    {
        // ...
    }
}

正如我已经说过的,这种方法行之有效-ASP.NET Core DI系统非常高兴创建SomeService实例及其类型化的HttpClient实例。

要重述原始问题,请使用以下示例代码:

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

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

在这种情况下,ASP.NET Core DI系统由于无法解析SomeViewComponent而拒绝创建HttpClient的实例。事实证明,这不是ViewComponent正义:它也适用于ControllerTagHelper s(感谢Chris Pratt确认了{ {1}} s。

有趣的是,以下内容也适用:

TagHelper

在此示例中,我们利用了对public void ConfigureServices(IServiceCollection services) { services.AddHttpClient<SomeViewComponent>(); // ... } public class SomeViewComponent { public SomeViewComponent(IHttpClientFactory httpClientFactory) { var httpClient = httpClientFactory.CreateClient("SomeViewComponent") // ... } } 的调用为我们注册named client的事实。

为了能够将AddHttpClient<SomeViewComponent>直接注入到HttpClient中,我们可以在用DI注册MVC时向ViewComponent添加一个调用:

AddViewComponentsAsServices
也可以调用

public void ConfigureServices(IServiceCollection services) { services.AddMvc(...) .AddViewComponentsAsServices(); // ... } AddControllersAsServices来分别为AddTagHelpersAsServicesController添加相同的支持。

如果我们仔细研究一下文档,很明显那里没有一个示例将TagHelpers注入HttpClient s等中-根本没有提到这种方法。

不幸的是,我对ASP.NET Core DI系统了解不足,无法准确地解释为什么的工作方式:我上面提供的信息只是说明内容以及解决方案。克里斯·普拉特(Chris Pratt)在Github中打开了issue,以便对文档进行更新以扩展此功能。

答案 2 :(得分:1)

似乎您混合了两个视图组件。您正在将FixturesViewComponent注册为“命名HTTP客户端”,但是尝试将HttpClient实例注入ProductsViewComponent中。

将HttpClient注册更改为ProductsViewComponent应该会有所帮助:

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

答案 3 :(得分:1)

我在Azure Function版本2中遇到类似的错误。根据this document,我们应该能够将IHttpClientFactory添加为依赖项。在我的Azure函数中添加此DI之后,出现了下面提到的错误。

  

Microsoft.Extensions.DependencyInjection.Abstractions:无法执行   解决类型为'System.Net.Http.IHttpClientFactory'的服务   试图激活   'OServiceBus.Adapter.FetchDataFromSubscription1'

问题是我没有重写Configure函数来将HttpClient添加为已注册的依赖项。因此,我仅在Azure函数的根目录中创建了一个名为Statup的类,如下所示。

使用Microsoft.Azure.Functions.Extensions.DependencyInjection; 使用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();
        }
    }
}

添加此内容后,我的功能开始正常运行。希望对您有所帮助。

答案 4 :(得分:0)

我有一个类似的错误消息,试图将外部REST服务的包装程序作为接口插入控制器。我需要在ConfigureServices中更改以下内容:

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

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

为了能够在我的控制器的构造函数中使用该接口:

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

用于通过模拟服务测试myController。

答案 5 :(得分:0)

也许它会有所帮助,但在我的情况下这是有效的:

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
    {}
}

我也试过 services.AddHttpClient<IMyService>() ,由于缺少它的构造函数而无法解决。 还尝试了 services.AddHttpClient<MyService>() 如上所述,但它无法解析配置的实例,如上所述。

所以重要的部分是需要使用用于引用解析类型的类。所以这也有效:

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
    {}
}

这有点道理,但文档和示例可能会更好。