我创建了一个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
,同样的错误。
答案 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> SomeService
和SomeComponent
之间坐在新班(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
的正义:它也适用于Controller
和TagHelper
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
来分别为AddTagHelpersAsServices
和Controller
添加相同的支持。
如果我们仔细研究一下文档,很明显那里没有一个示例将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
{}
}
这有点道理,但文档和示例可能会更好。