Click here to Skip to main content
15,886,199 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I am writing a library which user can reuse to connect with multiple APIs.
Requirement is to create multiple Typed client using IHttpClientFactory based on configuration in appsettings.json. I am reading Typed client from config so unable to create concrete different class for each config API as shown at MSDN.

Make HTTP requests using IHttpClientFactory in ASP.NET Core | Microsoft Docs[^]

I am trying to reuse single Typed class to create multiple Typed Client and want to resolve them.

Challenge is, I am unable to resolve different Typed client using Microsoft Dependency Injection.

What I have tried:

I tried below approach-

I am having a Typed client class which is having below structure-
C#
public interface IHttpClientFactoryService
{       
   Task<T> GetAsync<T>(string url);  
}    
    
public class HttpTypedClient : IHttpClientFactoryService
{
    HttpClient _httpClient;
    private static IAppSettings _configuration;


    public HttpTypedClient(HttpClient httpClient, String name)
    {
        this._httpClient = httpClient;
        this.Name = name;
    }

    public void LoadConfig(string Uuid)
    {
        _configuration = AppSettings.ApplicationConfiguration;
        ClientConfigurations clientConfiguration = _configuration.ClientConfigurations.Where(x => x.ConfigurationUId.ToString() == Uuid).FirstOrDefault();
        _httpClient.BaseAddress = new Uri(clientConfiguration.BaseAddress);        
    }

    public async Task<T> GetAsync<T>(string url)
    {            
        var request = new HttpRequestMessage(HttpMethod.Get, url);

        try
        {
            using (HttpResponseMessage response = await _httpClient.SendAsync(request))
            {
                using (var stream = await response.Content.ReadAsStreamAsync())
                {
                    if (stream.Length > 0)
                    {
                        if (!response.IsSuccessStatusCode)
                        {
                            // inspect the status code
                            if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
                            {
                                Console.WriteLine("The requested movie cannot be found.");
                                // return;
                            }
                        }
                        response.EnsureSuccessStatusCode();
                        //data = stream.ReadAndDeserializeFromJson<T>();
                        if (stream == null)
                        {
                            throw new ArgumentNullException(nameof(stream));
                        }

                        if (!stream.CanWrite)
                        {
                            throw new NotSupportedException("Can't write to this stream.");
                        }
                        var result = await JsonSerializer.DeserializeAsync<T>(stream, _serializeOptions);

                        return result;
                    }
                }
            }
        }
        catch (Exception ex)
        {
            //  throw;
        }

        Object o = new Object();
        return (T)o;
    }

One class HttpClientstartup class for registering Typed client.
C#
public static class HttpClientstartup
{
    public static void ConfigureHttpClient(IServiceCollection services,string Uuid)
    {
        services.AddHttpClient();
        services.AddHttpClient<IHttpClientFactoryService, HttpTypedClient>();          
    }       
}

Flow for registering and resolving the Typed client is

Program class code in Console Client-
C#
static async Task Main(string[] args)
{
    using IHost host = CreateHostBuilder(args).Build();
    _serviceProvider = host.Services;

    //Add dependency of Typed client
    HttpClientStartup.ConfigureHttpClient(_iServiceCollection);
    var provider = _iServiceCollection.BuildServiceProvider();

    var typedService1 = provider.GetService<IHttpClientFactoryService>();
    typedService1.LoadConfig("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4");

        var resultList1 = await typedService1.GetAsync<List<Movie>>("api/movies"); 

    var typedService2 = provider.GetService<IHttpClientFactoryService>();
    typedService2.LoadConfig("9472D0C4-8252-43E2-B09B-BAB3E874F8A5");            
    var movieList2 = await typedService2.GetAsync<List<Movie>>("api/books");          

     Console.ReadKey();
    await host.RunAsync();
}
Posted
Updated 7-Dec-21 23:24pm
v2

1 solution

With the IHttpClientFactory, you can either have a standard HttpClient keyed by name, or a typed client keyed by type. There is no support for registering different versions of the same typed client.

How about something like this?
C#
public interface ITypedClient
{
    Task<T> GetAsync<T>(string url);
}

public class TypedClient : ITypedClient
{
    private readonly HttpClient _client;
    
    public TypedClient(HttpClient client, string uuid)
    {
        _client = client ?? throw new ArgumentNullException(nameof(client));
        Uuid = uuid;
    }
    
    public string Uuid { get; }
    
    public async Task<T> GetAsync<T>(string url)
    {
        ...
    }
}
C#
public interface ITypedClientFactory
{
    ITypedClient CreateClient(string uuid);
}

public class TypedClientFactory
{
    private readonly IHttpClientFactory _clientFactory;
    
    public TypedClientFactory(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory));
    }
    
    public ITypedClient CreateClient(string uuid)
    {
        var client = _clientFactory.CreateClient(uuid);
        return new TypedClient(client, uuid);
    }
}
C#
public static class HttpClientStartup
{
    public static void ConfigureHttpClient(IServiceCollection services, IAppSettings configuration)
    {
        services.AddSingleton<ITypedClientFactory, TypedClientFactory>();
        
        foreach (ClientConfigurations clientConfiguration in configuration.ClientConfigurations)
        {
            string uuid = clientConfiguration.ConfigurationId.ToString();
            services.AddHttpClient(uuid, client =>
            {
                client.BaseAddress = new Uri(clientConfiguration.BaseAddress);
            });
        }
    }       
}
C#
var serviceCollection = new ServiceCollection();
HttpClientStartup.ConfigureHttpClient(serviceCollection, AppSettings.ApplicationConfiguration);
var provider = serviceCollection.BuildServiceProvider();

var factory = provider.GetRequiredService<ITypedClientFactory>();

var typedService1 = factory.CreateClient("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4");
var resultList1 = await typedService1.GetAsync<List<Movie>>("api/movies");

var typedService2 = factory.CreateClient("9472D0C4-8252-43E2-B09B-BAB3E874F8A5");
var movieList2 = await typedService2.GetAsync<List<Movie>>("api/books");
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900