【问题标题】:Unable to resolve service for type 'System.Net.Http.HttpClient'无法解析“System.Net.Http.HttpClient”类型的服务
【发布时间】:2019-02-10 16:49:33
【问题描述】:

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

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注入到Startup中可用的ConfigureService方法中:

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

更新:

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

【问题讨论】:

  • 您是如何在您的 IoC 容器中注册 HttpClient 的?
  • ProductsViewComponent != FixturesViewComponent.
  • @KirkLarkin 我同时注册了ViewComponent,但我仍然遇到同样的错误
  • 所有这些代码都很好。在Startup.cs 中添加服务后是否重建?除此之外,这可能是一个愚蠢的问题,但是您确定将其添加到正确的项目中吗?你确定你真的保存了文件吗?不知道还能去哪里,因为否则它会起作用。这些都不是奇怪或不正常的。
  • @KirkLarkin 我在这里创建了一个聊天室:chat.stackoverflow.com/rooms/179489/viewcomponent-injection cmets 太长了

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


【解决方案1】:

我遇到了类似的问题 - 问题在于双重注册:

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

类似的例子[只是为了澄清它不是特定于 AddSingleton 的,也与订单无关。]

services.AddScoped<IService, Service>();  // fixed by removing this line
services.AddHttpClient<IService, Service>();

【讨论】:

  • 也解决了我的问题。任何人都知道这是否在任何地方的文档中?
  • 如果 AddHttpClient 将为我注册服务,它将注册的生命周期是什么(单例、作用域、瞬态)?例如,有没有办法可以确保它是单例的?
  • @Alejo 通过services.AddHttpClient&lt;Service&gt;();docs.microsoft.com/en-us/aspnet/core/fundamentals/…添加服务将注册为Transient
【解决方案2】:

TLDR; ViewComponents 不支持 typed clients 开箱即用。要解决此问题,请在对services.AddMvc(...) 的调用末尾添加对AddViewComponentsAsServices() 的调用。


在很长一段时间 chat 无法重现您的问题之后,我们最初确定所观察到的问题特定于 ViewComponents。即使调用了IServiceCollection.AddHttpClient&lt;SomeViewComponent&gt;(),将HttpClient 的实例传递给SomeViewComponents 构造函数也只是拒绝工作。

但是,在SomeComponentHttpClient 之间安排一个新班级 (SomeService) 可以正常工作。这就是文档所指的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 系统由于无法解析HttpClient 而拒绝创建SomeViewComponent 的实例。事实证明,这并不是ViewComponents 特有的只是:它也适用于Controllers 和TagHelpers(感谢 Chris Pratt 对TagHelpers 的确认)。

有趣的是,以下方法也有效:

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

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

在本例中,我们利用了对AddHttpClient&lt;SomeViewComponent&gt; 的调用为我们注册了named client 的事实。

为了能够将HttpClient直接注入ViewComponent,我们可以在向DI注册MVC时添加对AddViewComponentsAsServices的调用:

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

也可以调用AddControllersAsServicesAddTagHelpersAsServices分别添加对Controllers和TagHelpers的相同支持。

如果我们更仔细地查看文档,很明显没有一个示例将HttpClient 注入Controllers 等 - 根本没有提到这种方法。

不幸的是,我对 ASP.NET Core DI 系统的了解不够,无法准确解释为什么它的工作方式:我在上面提供的信息只是解释 what 以及解决方案。 Chris Pratt 在 Github 中打开了一个 issue,以便更新文档以扩展此内容。

【讨论】:

    【解决方案3】:

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

    Microsoft.Extensions.DependencyInjection.Abstractions:无法 解析“System.Net.Http.IHttpClientFactory”类型的服务,而 试图激活 'OServiceBus.Adapter.FetchDataFromSubscription1'

    问题是我没有重写 Configure 函数来添加 HttpClient 作为注册的依赖项。所以我只是在我的 Azure Function 的根目录中创建了一个名为 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】:

      我尝试将外部 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】:

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

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

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

      【讨论】:

        【解决方案6】:

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

        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&lt;IMyService&gt;(),但由于缺少它的构造函数而无法解决。 还尝试了services.AddHttpClient&lt;MyService&gt;(),如上所述,但它无法解析配置的实例。

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-08-16
          • 2022-01-10
          • 2020-03-13
          • 2019-04-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多