Past few years, we are using Httpclient but are we using it properly?
Recently, I came across the code of a senior developer from the other team. The developer was trying to communicate with the microservices by using HttpClient and has implemented his own logic of retry.
Implementing the own logic for retry is not wrong but understanding what overhead it could bring is more important.
The logic that he has written has some major drawbacks
- Usage of HttpClient
- Usage of retry using loops
In this article, we’ll be covering only about drawbacks of HttpClient and how to fix the issues related to it.
Please find the source code here.
Usage of HttpClient
HttpClient is used to send the HTTP request or receive the HTTP response from the desired endpoint specified.
Internally, HttpClient uses HttpMessageInvoker class. HttpMessageInvoker class allows the applications to call the SendAsync method(send the HTTP request as an async operation). However, HttpMessageInvoker uses an IDisposable interface to release unmanaged resources.
//
// Summary:
// Provides a class for sending HTTP requests and receiving HTTP responses from
// a resource identified by a URI.
public class HttpClient : HttpMessageInvoker
{
//
// Summary:
// Gets or sets the base address of Uniform Resource Identifier (URI) of the Internet
// resource used when sending requests.
//
// Returns:
// The base address of Uniform Resource Identifier (URI) of the Internet resource
// used when sending requests.
public Uri? BaseAddress
{
[System.Runtime.CompilerServices.NullableContext(2)]
get
{
throw null;
}
[System.Runtime.CompilerServices.NullableContext(2)]
set
{
}
}
//
// Summary:
// Gets or sets the global Http proxy.
//
// Returns:
// A proxy used by every call that instantiates a System.Net.HttpWebRequest.
//
// Exceptions:
// T:System.ArgumentNullException:
// The value passed cannot be null.
public static IWebProxy DefaultProxy
{
get
{
throw null;
}
set
{
}
}
//
// Summary:
// A specialty class that allows applications to call the System.Net.Http.HttpMessageInvoker.SendAsync(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken)
// method on an HTTP handler chain.
public class HttpMessageInvoker : IDisposable
{
//
// Summary:
// Initializes an instance of a System.Net.Http.HttpMessageInvoker class with a
// specific System.Net.Http.HttpMessageHandler.
//
// Parameters:
// handler:
// The System.Net.Http.HttpMessageHandler responsible for processing the HTTP response
// messages.
public HttpMessageInvoker(HttpMessageHandler handler)
{
}
//
// Summary:
// Initializes an instance of a System.Net.Http.HttpMessageInvoker class with a
// specific System.Net.Http.HttpMessageHandler.
//
// Parameters:
// handler:
// The System.Net.Http.HttpMessageHandler responsible for processing the HTTP response
// messages.
//
// disposeHandler:
// true if the inner handler should be disposed of by Dispose(), false if you intend
// to reuse the inner handler.
public HttpMessageInvoker(HttpMessageHandler handler, bool disposeHandler)
{
}
//
// Summary:
// Releases the unmanaged resources and disposes of the managed resources used by
// the System.Net.Http.HttpMessageInvoker.
public void Dispose()
{
}
//
// Summary:
// Releases the unmanaged resources used by the System.Net.Http.HttpMessageInvoker
// and optionally disposes of the managed resources.
//
// Parameters:
// disposing:
// true to release both managed and unmanaged resources; false to releases only
// unmanaged resources.
protected virtual void Dispose(bool disposing)
{
}
//
// Summary:
// Sends an HTTP request with the specified request and cancellation token.
//
// Parameters:
// request:
// The HTTP request message to send.
//
// cancellationToken:
// The cancellation token to cancel operation.
//
// Returns:
// The HTTP response message.
//
// Exceptions:
// T:System.ArgumentNullException:
// The request was null.
//
// T:System.NotSupportedException:
// For HTTP/2 and higher or when requesting version upgrade is enabled by System.Net.Http.HttpVersionPolicy.RequestVersionOrHigher.
// -or- If using custom class derived from System.Net.Http.HttpContent not overriding
// System.Net.Http.HttpContent.SerializeToStream(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken)
// method. -or- If using custom System.Net.Http.HttpMessageHandler not overriding
// System.Net.Http.HttpMessageHandler.Send(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken)
// method.
[UnsupportedOSPlatform("browser")]
public virtual HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
{
throw null;
}
//
// Summary:
// Send an HTTP request as an asynchronous operation.
//
// Parameters:
// request:
// The HTTP request message to send.
//
// cancellationToken:
// The cancellation token to cancel operation.
//
// Returns:
// The task object representing the asynchronous operation.
//
// Exceptions:
// T:System.ArgumentNullException:
// The request was null.
public virtual Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
throw null;
}
}
Current Implementation
I’m using the default Web API template for both frontend and backend applications.
Backend
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}




Mahesh ChandPosted Nov 4, 2022, 5:29 PM
Welcome to the community, Anup. Nice detailed article. Best