Home > Software design >  What is the correct approach to pass HttpMessageHandler in HttpClient?
What is the correct approach to pass HttpMessageHandler in HttpClient?

Time:01-25

I want to pass HttpMessageHandler into my HttpClient. The main reason is to pass the proxy (IWebProxy).

Ideally, HttpClient is defined in constructor like this (Using DI):

public class MyApiClient : IMyApiClient
{
    private readonly HttpClient _httpClient;

    public MyApiClient(HttpClient httpClient)
    {
        // I am not sure how to pass `handler` here.
        _httpClient = httpClient;
    }
}

The way I am adding proxy into HttpClient is as following (Not using DI):

public class MyApiClient : IMyApiClient {
  private readonly HttpClient _httpClient;

  public MyApiClient() {
    var proxy = new WebProxy {
      Address = new Uri("http://test.com:1234"),
      BypassProxyOnLocal = false,
      UseDefaultCredentials = false,
    };

    // Create a client handler that uses the proxy
    var httpClientHandler = new HttpClientHandler {
      Proxy = proxy,
    };

    // Disable SSL verification
    httpClientHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
    
    _httpClient = new HttpClient(handler: httpClientHandler, disposeHandler: true);
  }
}

Questions:

  1. Is my approach correct to pass proxy?
  2. Is it possible to pass proxy in HttpClient using DI (first approach)?

CodePudding user response:

Manually handling either HttpClient or the underlying HttpMessageHandler is considered a bad practice in general due to the way the low level socket resources are managed: in most cases, it would either lead to socket starvation due to leaks, or it could lead to unusable failed connections.

Instead, you should rely on IHttpClientFactory and related abstractions. First, I'd strongly recommend reading the guidelines from Microsoft themselves here:

The simplest possible way to configure a single handler is to use the AddHttpClient overload. In your case, it would look something like the following:

services
    .AddHttpClient<IMyApiClient, MyApiClient>((provider, client) => 
    {
        // Confiure one-off settings of the client here. 
        // There is an overload which doesn't pass the 'provider', in case
        // you don't need DI during the configuration.
        //
        // For example (using recommended 'IOptions' approach):

        var settings = provider.GetRequiredService<IOptions<MyApiSettings>>();
        client.BaseAddress = settings.Value.ApiRoot;
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        // Add whatever logic to create the handler here
        // Similar to the other method, this also has DI-enabled overloads.
        var proxy = new WebProxy 
        {
            Address = new Uri("http://test.com:1234"),
            BypassProxyOnLocal = false,
            UseDefaultCredentials = false,
        };

        // Create a client handler that uses the proxy
        var httpClientHandler = new HttpClientHandler { Proxy = proxy };

        // Disable SSL verification
        httpClientHandler.ServerCertificateCustomValidationCallback =
            HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
        
        return httpClientHandler;
    });

This will automatically link the configured HttpClient instance with your MyApiClient in the container, so that you can then directly inject HttpClient in the MyApiClient constructor like in your first example:

public class MyApiClient : IMyApiClient
{
    private readonly HttpClient _httpClient;

    public MyApiClient(HttpClient httpClient)
    {
        // It will work now ;)
        _httpClient = httpClient;
    }
}
  • Related