2

Introduction

I am developing SDK (that is just a wrapper for an REST API). This SDK is internally using the HttpClient to make requests for the API. The SDK targets .NET Standard 2.1. Here is simplified code.

public class SomeSdk 
{
    private readonly HttpClient _httpClient;

    public SomeSdk(<some-settings>) 
    {
        _httpClient = new HttpClient(<some-settings>);
    }

    public async Task<string> GetSomethingFromApiAsync() 
    {
        return await _httpClient.GetStringAsync(...);
    }
}

Issue

The SDK is being used by other developers in ASP.NET Core projects. I am aware of the troubles with HttpClient and I know that MS DOCS recommand to inject HttpClientFactory to avoid SocketException and to reflect DNS changes.

First solution that comes to mind is to let the user pass the HttpClient (that is from HttpClientFactory) in the SDK constructor or other methods. Like this.

public async Task<string> GetSomethingFromApiAsync(HttpClient clientFromFactory) 
{
    return await clientFromFactory.GetStringAsync(...);
}

However there is problem that the SDK configures the HttpClient before it uses it (Proxy, Timeout, etc.) Therefore it's initialization should be left in the SDK constructor.

Question

My current solution is to inject SDK as singleton in the ASP.NET Core projects and accept the DNS reflection issues. I would still like to ask if there is anyone who would recommand me a better approach for this problem.

Thank you very much, Adam.

1

2 Answers 2

1

The better way to instantiate HttpClient is using DI.
So, assuming you have in your SDK project some class like that

public interface ISomeSdk
{
    Task<string> GetSomethingFromApiAsync();
}

public class SomeSdk: ISomeSdk
{
    private readonly HttpClient _httpClient;

    public SomeSdk(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<string> GetSomethingFromApiAsync()
    {
        return await _httpClient.GetStringAsync("");
    }
}

you can easily add an extension method to register it through DI (here, in SDK project)

public static class HttpClientExtensions
{
    public static void RegisterSdk(this IServiceCollection services)
    {
        services.AddHttpClient<ISomeSdk, SomeSdk>(client =>
        {
            client.Timeout = TimeSpan.FromSeconds(60);
            client.BaseAddress = new Uri("https://google.com");
        });
    }
}

after that in the startup project this class can be added into DI registration

public void ConfigureServices(IServiceCollection services)
{
    services.RegisterSdk();
    //...

What allows to use it whenever it's needed

public class SomeController : ControllerBase
{
    private readonly ISomeSdk _someSdk;

    public SomeController(ISomeSdk someSdk)
    {
        _someSdk = someSdk;
    }
Sign up to request clarification or add additional context in comments.

1 Comment

I am using the same approach in one of my libraries but there is an issue with the extensibility of the above extension method. Imagine that the user wants to use a proxy with the HTTP client. They have to add a new typed HTTP client to DI. How can the extension method, say RegisterSdk be made DI and consumer-friendly in such a case? The new typed HTTP client will be inherited from the existing one, say ISomeSdk (since it's a wrapper and contains the Get, Post etc. methods). The consumer has to do everything inside the ext method manually in such a case.
0

The better way is to use HttpClientFactory to create HttpClient instances.

HttpClientFactory can create and manage new HttpClient instances but also, when creating new HttpClient instances, it doesn’t recreate a new message handler but it takes one from a pool. Then, it uses that message handler to send the requests to the API. The default lifetime of a handler is set to two minutes, and during that time, any request for a new HttpClient can reuse an existing message handler and the connection as well. This means we don’t have to create a new message handler for every request and also we don’t have to open a new connection, thus preventing the socket exhausting issue.

With HttpClientHandler, we can centralize our HttpClient’s configuration. If you repeat the same configuration in each service class with HttpClientHandler, you can prevent that.

The examples how to use it you can find here https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-5.0

1 Comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.