मुख्य सामग्रीमा जानुहोस्
~/writing

यो लेख अंग्रेजीमा छ।

.NET

Mastering HttpClient in .NET with Refit

/ ३ मिनेट पढाइ / Ejan Shrestha

Introduction

In the .NET ecosystem, HttpClient is the go-to tool for making HTTP requests to web services. However, despite its simplicity, improper usage can lead to significant issues such as socket exhaustion and poor performance. In this article, we will explore the different ways to implement HttpClient, discuss best practices, and introduce Refit—a powerful library that simplifies API consumption in .NET.

Why HttpClient is Important

HttpClient is a versatile class that provides a straightforward way to send HTTP requests and receive responses from a resource identified by a URI. It's essential in any application that interacts with RESTful APIs, third-party services, or even microservices within a distributed system.

However, as a senior software engineer, it's crucial to understand the implications of how HttpClient is instantiated and managed within an application. Mismanagement can lead to serious performance bottlenecks.

Common Pitfalls with HttpClient

1. Socket Exhaustion

  • Problem: Creating a new HttpClient instance for each request can exhaust the number of available sockets, leading to SocketException.

  • Cause: Each HttpClient instance consumes a socket, which isn't immediately released upon disposal, especially under high load.

  • Solution: Use a singleton HttpClient or leverage IHttpClientFactory.

2. DNS Caching

  • Problem: The default HttpClient implementation caches DNS entries indefinitely, which can cause requests to fail if the underlying IP changes.

  • Solution: IHttpClientFactory handles DNS updates by pooling HttpClientHandler instances.

Different Ways to Implement HttpClient

1. Basic Instantiation

This is the most straightforward approach but should be used cautiously.

using (var client = new HttpClient())
{
    var response = await client.GetAsync("https://api.example.com/data");
    response.EnsureSuccessStatusCode();
    var content = await response.Content.ReadAsStringAsync();
}

Pros:

  • Simple and easy to understand.

Cons:

  • Risk of socket exhaustion if used in a high-traffic environment.

2. Singleton HttpClient

To avoid socket exhaustion, use a singleton HttpClient instance.

public class MyService
{
    private static readonly HttpClient _httpClient = new HttpClient();
 
    public async Task<string> GetDataAsync()
    {
        var response = await _httpClient.GetAsync("https://api.example.com/data");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    }
}

Pros:

  • Prevents socket exhaustion.

Cons:

  • Potential DNS caching issues.

Introduced in .NET Core 2.1, IHttpClientFactory addresses the issues of socket exhaustion and DNS caching by managing HttpClient instances efficiently.

public class MyService
{
    private readonly HttpClient _httpClient;
 
    public MyService(IHttpClientFactory httpClientFactory)
    {
        _httpClient = httpClientFactory.CreateClient("MyClient");
    }
 
    public async Task<string> GetDataAsync()
    {
        var response = await _httpClient.GetAsync("https://api.example.com/data");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    }
}

Pros:

  • Handles DNS updates automatically.

  • Prevents socket exhaustion.

  • Supports named and typed clients for better configuration.

Cons:

  • Slightly more complex to set up compared to a basic instantiation.

4. Typed Clients

Typed clients are a way to create strongly-typed wrappers around HttpClient using IHttpClientFactory.

public class MyApiClient
{
    private readonly HttpClient _httpClient;
 
    public MyApiClient(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }
 
    public async Task<string> GetDataAsync()
    {
        var response = await _httpClient.GetAsync("https://api.example.com/data");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    }
}
 
// Registration in Startup.cs
services.AddHttpClient<MyApiClient>();

Pros:

  • Clean, reusable, and testable code.

  • Centralized configuration.

Cons:

  • Requires additional setup in the DI container.

Advanced API Consumption with Refit

Refit is a type-safe REST client for .NET that simplifies making HTTP requests by automatically generating API client implementations from an interface. It abstracts much of the boilerplate code associated with HttpClient.

1. Getting Started with Refit

To use Refit, first, install the NuGet package:

dotnet add package Refit

2. Defining an API Interface

public interface IMyApi
{
    [Get("/data")]
    Task<string> GetDataAsync();
}

3. Consuming the API

public class MyService
{
    private readonly IMyApi _myApi;
 
    public MyService(IMyApi myApi)
    {
        _myApi = myApi;
    }
 
    public async Task<string> GetDataAsync()
    {
        return await _myApi.GetDataAsync();
    }
}
 
// Registration in Startup.cs
services.AddRefitClient<IMyApi>()
        .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"));

Pros:

  • Clean and concise API consumption.

  • Strongly typed, making it easier to refactor.

  • Built-in support for error handling, JSON serialization, and retries.

Cons:

  • Additional abstraction layer; less control over HttpClient configuration.

Best Practices

  1. Prefer IHttpClientFactory: Always prefer IHttpClientFactory for managing HttpClient instances to avoid common pitfalls.

  2. Use Typed Clients: For complex or reusable HttpClient logic, prefer typed clients for better maintainability.

  3. Leverage Refit for Simplicity: If you're working with multiple APIs and want to minimize boilerplate, Refit is an excellent choice.

  4. Handle Timeouts and Retries: Always configure timeouts and consider implementing retry policies for transient errors.

  5. Dispose HttpClient Correctly: Avoid using using statements with HttpClient unless it's necessary for a short-lived operation. Otherwise, prefer long-lived instances.

  6. Secure Sensitive Data: Use secure mechanisms for handling sensitive data such as API keys or tokens within your HttpClient requests.

Conclusion

HttpClient is a powerful tool in the .NET developer's arsenal, but with great power comes great responsibility. By following best practices such as using IHttpClientFactory and typed clients, you can avoid common pitfalls like socket exhaustion and DNS issues. Additionally, Refit offers a streamlined approach to API consumption, making your code cleaner and more maintainable.

By mastering these techniques, you'll ensure that your .NET applications are robust, efficient, and scalable, meeting the demands of modern software development.


This article provides a comprehensive guide for .NET developers, from basic HttpClient usage to advanced techniques with Refit, ensuring that your applications are both performant and maintainable.

Resume.pdf

PDF डाउनलोड

Ejan Shresthaइजन श्रेष्ठ

Senior Software Engineer, backend and .NET modernisation

Senior software engineer in Kathmandu with 4+ years shipping .NET for insurance and enterprise. I own reliability and modernisation for an insurance platform of 50 .NET projects on Kubernetes: structured logging and tracing across old and new services, the plan off end-of-life .NET to .NET 10, and the fix for socket exhaustion across ~390 call sites.

Selected impact

  • ~390Ending socket exhaustion across the platform. The error class disappeared from production logs, and outbound HTTP now has one place for retries and tracing.
  • 1 IDOne trace from gateway to database. One correlation ID now follows a request end to end.
  • 61%From end-of-life .NET to .NET 10. A sequenced roadmap with known risks; the first services already run on .NET 10.
  • v16A public website on Next.js 16. Shipped to production with a tested rollback path.
  • 4A release standard people follow. Releases are predictable enough to schedule.

Experience

  1. Senior Software Engineer, Himalayan Everest Insurance

    Jan 2025 – present

    Kathmandu, hybrid

    • Own backend reliability and modernisation for an insurance platform of 50 .NET projects, deployed as 28 Kubernetes workloads, across accounting, claims, reinsurance and reporting.
    • Leading the upgrade from .NET Core 2.2, 3.1 and 5 to .NET 10, including the IdentityServer4 replacement.
    • Built structured logging and tracing across every service.
    • Upgraded the public website and CMS to Next.js 16, React 19, Strapi 5 and Node 24.
  2. .NET Developer, EKbana Solutions

    Jul 2022 – Nov 2024

    Lalitpur, on-site

    • Designed backend services for enterprise insurance workflows with ASP.NET Core, PostgreSQL, EF Core and Dapper.
    • Built REST APIs and integrations for customer portals, payments and external partners.
    • Ran background workflows on Hangfire with idempotency, retries and failure recovery.
    • Shipped on Docker, Linux, Nginx/Traefik and Kubernetes; worked on IdentityServer, JWT and RBAC.
  3. Dotnet Developer, NBIT

    Jan 2022 – Jun 2022

    Lalitpur

    • Built and maintained .NET Framework applications for client projects.
  4. Data Specialist, CloudFactory

    Nov 2017 – Jun 2022

    Kathmandu

    • Delivered high-accuracy data work for international clients while completing my degree.