DEV Community

Mo
Mo

Posted on

Robust use of HTTP Client

In this blog post, I will show you how to use HttpClient and DelegatingHandler in ASP.NET Core to create custom HTTP clients that can handle cross-cutting concerns such as logging, authentication, caching, etc. These classes are part of the System.Net.Http namespace and provide a flexible and extensible way to interact with HTTP services.

HttpClient is a class that provides a high-level abstraction for sending HTTP requests and receiving HTTP responses. It supports asynchronous operations, cancellation tokens, headers, content, etc. You can use HttpClient to consume any HTTP service, such as REST APIs, SOAP services, etc.

DelegatingHandler is an abstract class that represents a handler for HTTP requests and responses. It can act as a middleware in the HTTP pipeline, intercepting and modifying the requests and responses before they reach the final destination. You can create your own subclasses of DelegatingHandler to implement specific logic for your HTTP clients.

IHttpClientFactory is an interface that defines a factory for creating HttpClient instances. It also manages the lifetime and pooling of the underlying HttpMessageHandler instances, which are used by HttpClient to send HTTP requests. You can register your own implementations of IHttpClientFactory in the dependency injection container of ASP.NET Core and use them to create HttpClient instances with different configurations and behaviours.

In ASP.NET Core, you can use the AddHttpClient extension method to register your IHttpClientFactory implementations and configure your HttpClient instances. You can also use the AddHttpMessageHandler extension method to add your DelegatingHandler instances to the HTTP pipeline of your HttpClient instances. This way, you can create custom HTTP clients that can handle various cross-cutting concerns in a consistent and reusable way.

For example, I have created a typed clients for GitHub client:

/// <summary>
/// Typed clients using of IHttpClientFactory for GithubClient
/// </summary>
public sealed class GithubClient : IDisposable
{
    private readonly HttpClient _httpClient;

    public GithubClient(HttpClient httpClient) => _httpClient = httpClient;

    public async Task<string> GetHomePageAsync() => await _httpClient.GetStringAsync("https://github.com");

    public void Dispose() => _httpClient?.Dispose();
}
Enter fullscreen mode Exit fullscreen mode

and for logging and exception handling I have created two more classes that inherit from DelegatingHandler:

public sealed class LoggingHandler : DelegatingHandler
{
    private readonly ILogger<LoggingHandler> _logger;

    public LoggingHandler(ILogger<LoggingHandler> logger) => _logger = logger;

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        _logger.LogInformation("Sending request!");

        var response = base.SendAsync(request, cancellationToken);

        _logger.LogInformation("Request was successful!");

        return response;
    }
}
Enter fullscreen mode Exit fullscreen mode
public sealed class HttpClientExceptionHandler : DelegatingHandler
{
    private readonly ILogger<HttpClientExceptionHandler> _logger;

    public HttpClientExceptionHandler(ILogger<HttpClientExceptionHandler> logger) => _logger = logger;

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        try
        {
            return await base.SendAsync(request, cancellationToken);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, ex?.Message);

            throw;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

and registered them in the Program.cs file:

builder.Services.AddScoped<LoggingHandler>();
builder.Services.AddScoped<HttpClientExceptionHandler>();

builder.Services.AddHttpClient<GithubClient>()
    .AddHttpMessageHandler<LoggingHandler>()
    .AddHttpMessageHandler<HttpClientExceptionHandler>();
Enter fullscreen mode Exit fullscreen mode

The main advantage of this approach is that instead of putting all logging and exception handling codes into one file (GithubClient) I have separated them into different files for each responsibility and just used .AddHttpMessageHandler to use them for intercepting.

Oldest comments (0)