web-dev-qa-db-fra.com

Impossible de résoudre le service pour le type 'System.Net.Http.HttpClient'

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.

3
Charanoglu

TLDR;ViewComponents 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 à ViewComponents. Même avec un appel à IServiceCollection.AddHttpClient<SomeViewComponent>(), le passage d'une instance de HttpClient au constructeur SomeViewComponents 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 à ViewComponents: cela s’applique également à Controllers et TagHelpers (merci à Chris Pratt pour sa confirmation de TagHelpers).

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 Controllers 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 Controllers 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é.

2
Kirk Larkin

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

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
0
Petr Mašlaň