org.sonar.plugins.csharp.S6962.html Maven / Gradle / Ivy
In frequently used code paths, such as controller actions, you should avoid using the HttpClient directly and opt for one of the IHttpClientFactory-based mechanisms instead. This
way, you avoid wasting resources and creating performance overhead.
Why is this an issue?
If a code path that creates and disposes of HttpClient objects is frequently used, then the following issues can occur:
- Under heavy load, there’s the risk of running out of available
sockets, leading to SocketException errors. This
is because each HttpClient instance uses a separate network connection, and there’s a limit to the number of connections that can be opened
simultaneously. Note that even after you dispose of an HttpClient its sockets are not immediately freed up.
- Each HttpClient has its own set of resources (like headers, base address, timeout, etc.) that must be managed. Creating a new HttpClient for
every request means these resources are not being reused, leading to resource waste.
- You introduce a significant performance overhead when creating a new HttpClient for every HTTP request.
How to fix it
The IHttpClientFactory
was introduced in
ASP.NET Core 2.1 to solve these problems. It handles pooling HTTP connections to optimize performance and reliability.
There are several ways that you can use
IHttpClientFactory in your application:
Alternatively, you may cache the HttpClient in a singleton or a static field. You should be aware that by default, the HttpClient doesn’t respect
the DNS’s Time To Live (TTL) settings. If the IP address associated with a domain name changes, HttpClient might still use the old, cached IP address,
leading to failed requests.
Code examples
Noncompliant code example
[ApiController]
[Route("controller")]
public class FooController : Controller
{
[HttpGet]
public async Task<string> Foo()
{
using var client = new HttpClient(); // Noncompliant
return await client.GetStringAsync(_url);
}
}
Compliant solution
// File: Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
// ...
}
}
[ApiController]
[Route("controller")]
public class FooController : Controller
{
private readonly IHttpClientFactory _clientFactory;
public FooController(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
[HttpGet]
public async Task<string> Foo()
{
using var client = _clientFactory.CreateClient(); // Compliant (Basic usage)
return await client.GetStringAsync(_url);
}
}
Resources
Documentation
- {rule:csharpsquid:S6420} - Client instances should not be recreated on each Azure Function invocation
- Microsoft Learn - IHttpClientFactory Interface
- Microsoft Learn - HttpClient Class
- Microsoft Learn - IHttpClientFactory with .NET
- Microsoft Learn - Use IHttpClientFactory to implement resilient HTTP requests
- Microsoft Learn - Make HTTP requests using
IHttpClientFactory in ASP.NET Core
- Microsoft Learn - Guidelines for using
HttpClient