J'ai créé une classe ViewComponent
qui appelle un REST API
en utilisant HttpClient
, voici le 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);
}
}
}
Je reçois cette erreur:
InvalidOperationException: impossible de résoudre le service pour le type 'System.Net.Http.HttpClient' lors de l'activation de MyApp.ViewComponents.ProductsViewComponent '
J'ai injecté la HttpClient
dans la méthode ConfigureService
disponible dans Startup
de cette façon:
services.AddHttpClient<FixturesViewComponent>(options =>
{
options.BaseAddress = new Uri("http://80.350.485.118/api/v2");
});
METTRE À JOUR:
J'ai enregistré le ProductsViewComponent
aussi, même erreur.
TLDR;ViewComponent
s ne prend pas en charge clients typés prêts à l'emploi. Pour résoudre ce problème, ajoutez un appel à AddViewComponentsAsServices()
à la fin de l'appel à services.AddMvc(...)
.
Après un assez long chat qui avait été capable de reproduire votre problème, nous avons initialement déterminé que le problème observé était spécifique à ViewComponent
s. Même avec un appel à IServiceCollection.AddHttpClient<SomeViewComponent>()
, le passage d'une instance de HttpClient
au constructeur SomeViewComponent
s vient de refuser de fonctionner.
Cependant, siéger dans une nouvelle classe (SomeService
) entreSomeComponent
et HttpClient
fonctionne comme prévu. C’est ce que les documents appellent un client typé . Le code ressemble un peu à ceci:
// 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)
{
// ...
}
}
Comme je l'ai déjà indiqué, cette approche fonctionne: le système ASP.NET Core DI est très heureux de créer l'instance de SomeService
et son instance typée HttpClient
.
Pour reformuler le problème d'origine, prenons l'exemple de code suivant:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<SomeViewComponent>();
// ...
}
public class SomeViewComponent
{
public SomeViewComponent(HttpClient httpClient)
{
// ...
}
}
Dans ce cas, le système ASP.NET Core DI refuse de créer une instance de SomeViewComponent
en raison de l'impossibilité de résoudre HttpClient
. Il s’avère que cela n’est pas spécifique juste à ViewComponent
s: cela s’applique également à Controller
s et TagHelper
s (merci à Chris Pratt pour sa confirmation de TagHelper
s).
Fait intéressant, ce qui suit fonctionne également:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<SomeViewComponent>();
// ...
}
public class SomeViewComponent
{
public SomeViewComponent(IHttpClientFactory httpClientFactory)
{
var httpClient = httpClientFactory.CreateClient("SomeViewComponent")
// ...
}
}
Dans cet exemple, nous tirons parti du fait que l'appel à AddHttpClient<SomeViewComponent>
a enregistré un client nommé pour nous.
Afin de pouvoir injecter HttpClient
directement dans une ViewComponent
, nous pouvons ajouter un appel à AddViewComponentsAsServices
lorsque nous enregistrons MVC avec DI:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(...)
.AddViewComponentsAsServices();
// ...
}
AddControllersAsServices
et AddTagHelpersAsServices
peuvent également être appelés pour ajouter le même support pour Controller
s et TagHelpers
respectivement.
Si nous examinons la documentation de plus près, il est clair qu’aucun des exemples n’injecte une variable HttpClient
dans Controller
s et al - il n’est tout simplement pas fait mention de cette approche.
Malheureusement, je ne connais pas suffisamment le système ASP.NET Core DI pour pouvoir expliquer exactement pourquoi cela fonctionne comme cela: Les informations que j'ai fournies ci-dessus expliquent simplement le quoi avec une solution. Chris Pratt a ouvert un numéro dans Github pour que les documents soient mis à jour afin de développer cette fonctionnalité.
Il semble que vous ayez mélangé deux composants de vue. Vous enregistrez la FixturesViewComponent
en tant que "client HTTP nommé" et pourtant vous essayez d'injecter une instance HttpClient
dans la ProductsViewComponent
.
Changer l'enregistrement de HttpClient en ProductsViewComponent
devrait aider:
services.AddHttpClient<ProductsViewComponent>(options =>
{
options.BaseAddress = new Uri("http://80.350.485.118/api/v2");
});
J'ai eu un problème similaire - le problème était en double enregistrement:
services.AddHttpClient<Service>();
services.AddSingleton<Service>(); // fixed by removing this line