# HttpClient Best Practices

# Using HttpClient Wrong Can Break Your App

Let’s talk about something that might seem simple but can cause BIG problems if you’re not careful: **HTTP clients**. These are the tools your app uses to talk to other services on the internet. But if you use them the wrong way, your app can crash.

---

## The Wrong Way: Creating a New HttpClient Every Time ❌

Imagine you’re building an app that needs to call an API. You think, “I’ll just create a new `HttpClient` every time I need to make a request. Easy, right?” So you write something like this:

```csharp
public async Task<User> GetUserAsync(string username)
{
    using var client = new HttpClient(); // Danger! 🚨
    client.BaseAddress = new Uri("https://api.coolservice.com");
    client.DefaultRequestHeaders.Add("Authorization", "Bearer my-token");

    var user = await client.GetFromJsonAsync<User>($"users/{username}");
    return user;
}
```

Looks simple, right? But here’s the problem: **HttpClient is not meant to be used this way!**

### Why This Is Bad

1. **Port Exhaustion**: Every time you create a new `HttpClient`, it opens a new connection. If you do this too much, your app can run out of available ports. When this happens, your app crashes.
    
2. **Wasted Resources**: Creating and disposing of `HttpClient` over and over is like buying a new phone every time you want to make a call. It’s wasteful and inefficient.
    

## The Big Problem with Disposing Handlers

Here’s where things get tricky. When you dispose of a `HttpClient`, it also disposes of its `HttpMessageHandler` by default. This might seem fine, but remember: **the handler manages the underlying connections.** If you dispose of it, those connections are closed, and new ones have to be created the next time you make a request.

This is why creating and disposing of `HttpClient` instances over and over can lead to **port exhaustion** and **DNS issues**. It’s like constantly tearing down and rebuilding a bridge instead of reusing it.

`HttpClientFactory` is smart. When it creates a `HttpClient`, it sets `disposeHandler` to `false`. This means the handler isn’t disposed of when the `HttpClient` is disposed. Instead, the factory manages the handler’s lifecycle, reusing it when possible and disposing of it only when it’s no longer need.

explore source code : [HttpClientLibrary](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs)

## TL;DR: The Key Takeaways

1. **HttpClient’s constructors** control whether the handler is disposed of.
    
2. **Disposing handlers** closes connections, which can lead to port exhaustion.
    
3. **HttpClientFactory** sets `disposeHandler` to `false`, reusing handlers and connections.
    
4. **Reusing handlers** is the secret sauce to keeping your app fast and reliable.
    

---

## The Right Way: Use HttpClientFactory ✅

Instead of creating `HttpClient` manually, you should use **HttpClientFactory**. Think of it as a smart manager that handles all the connections for you. It reuses connections, avoids port exhaustion, and makes your life easier.

```csharp
public class CoolService
{
    private readonly IHttpClientFactory _httpClientFactory;

    public CoolService(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public async Task<User> GetUserAsync(string username)
    {
        var client = _httpClientFactory.CreateClient(); //No more "using"
        client.BaseAddress = new Uri("https://api.coolservice.com");
        client.DefaultRequestHeaders.Add("Authorization", "Bearer my-token");

        var user = await client.GetFromJsonAsync<User>($"users/{username}");
        return user;
    }
}
```

### Why This Is the best way:

1. **Reuses Connections**: HttpClientFactory manages a pool of connections, so you don’t waste resources.
    
2. **Short-Lived Clients**: The `HttpClient` instances are meant to be used quickly and then discarded. The factory handles the cleanup for you.
    
3. **Handles DNS Changes**: If the API’s IP address changes (common in cloud environments), HttpClientFactory adapts automatically. No more broken connections!
    

---

## Level Up: Named and Typed Clients 🚀

Want to make your code even cleaner? Use **named clients** or **typed clients**. These let you pre-configure your `HttpClient` so you don’t have to repeat the same setup code every time.

### Named Clients

```csharp
services.AddHttpClient("CoolService", client =>
{
    client.BaseAddress = new Uri("https://api.coolservice.com");
    client.DefaultRequestHeaders.Add("Authorization", "Bearer my-token");
});
```

Then, use it in your service:

```bash
public async Task<User> GetUserAsync(string username)
{
    var client = _httpClientFactory.CreateClient("CoolService"); //pass in name of the client
    var user = await client.GetFromJsonAsync<User>($"users/{username}");
    return user;
}
```

### Typed Clients

Even better, you can create a **typed client**. This is like having a custom-made `HttpClient` for your service.

First, define your typed client:

```bash
public class CoolService
{
    private readonly HttpClient _client;

    public CoolService(HttpClient client)
    {
        _client = client;
    }

    public async Task<User> GetUserAsync(string username)
    {
        return await _client.GetFromJsonAsync<User>($"users/{username}");
    }
}
```

Then, configure it in `Startup.cs`:

```csharp
services.AddHttpClient<CoolService>(client =>
{
    client.BaseAddress = new Uri("https://api.coolservice.com");
    client.DefaultRequestHeaders.Add("Authorization", "Bearer my-token");
});
```

Now, you can inject `CoolService` directly, and the `HttpClient` will be ready to go. No more repetitive setup!

---

## Watch Out: Typed Clients in Singleton Services ⚠️

Here’s a pro tip: **Don’t use typed clients in singleton services**. Why? Because typed clients are meant to be short-lived, and if they’re stuck in a singleton, they won’t react to DNS changes. It’s like using an old map forever, even though the city has changed. 🗺️

If you use a typed client in a singleton, configure the `PooledConnectionLifetime` to refresh connections periodically:

```csharp
services.AddHttpClient<CoolService>(client =>
{
    client.BaseAddress = new Uri("https://api.coolservice.com");
    client.DefaultRequestHeaders.Add("Authorization", "Bearer my-token");
})
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(15) // Refresh connections
})
.SetHandlerLifetime(Timeout.InfiniteTimeSpan);
```

---

---

## TL;DR: Why HttpClientFactory Wins

1. **No Port Exhaustion**: Reuses connections instead of creating new ones.
    
2. **Handles DNS Changes**: Keeps your app up-to-date with changing IP addresses.
    
3. **Cleaner Code**: Named and typed clients make your code easier to read and maintain.
    
4. **No Disposal Drama**: Let the factory handle the lifecycle of `HttpClient`.
    

So, next time you’re tempted to create a new `HttpClient`, remember: **HttpClientFactory is your friend.** Use it, and keep your app running smoothly.
