๐ HttpClient Patterns in ASP.NET: Your Personal Delivery Service
Imagine youโre running a busy restaurant. You need to order ingredients from different suppliers every day. Would you hire a new delivery driver for EVERY single order? That would be chaos! Instead, smart restaurants have a delivery team that knows all the suppliers, reuses their vehicles, and handles everything efficiently.
Thatโs exactly what HttpClient Patterns do in ASP.NET! Letโs explore this together.
๐ฏ What Youโll Learn
- HttpClientFactory โ The delivery team manager
- Named HttpClients โ Specialized drivers for different suppliers
- Typed HttpClients โ Expert drivers with deep knowledge
๐ฆ The Problem: Why We Need a Better Way
The Old Way (Donโt Do This!)
// โ BAD: Creating new HttpClient each time
using (var client = new HttpClient())
{
var result = await client
.GetAsync("https://api.weather.com");
}
Why is this bad?
Think of it like this: Every time you need milk, you:
- Buy a new car ๐
- Drive to the store
- Get milk
- Throw the car away! ๐๏ธ
This wastes resources and can even break your app when too many connections pile up!
๐ญ HttpClientFactory: The Delivery Team Manager
What is HttpClientFactory?
HttpClientFactory is like hiring a smart delivery manager who:
- โป๏ธ Reuses vehicles (connections) instead of buying new ones
- ๐ง Maintains the vehicles so theyโre always ready
- ๐ Organizes which driver goes where
Setting It Up
Step 1: Tell your app to use the factory
// In Program.cs
builder.Services.AddHttpClient();
Thatโs it! One line. Your delivery manager is hired! ๐
Using It
public class WeatherService
{
private readonly IHttpClientFactory _factory;
public WeatherService(
IHttpClientFactory factory)
{
_factory = factory;
}
public async Task<string> GetWeather()
{
// Factory creates/reuses client
var client = _factory
.CreateClient();
return await client
.GetStringAsync(
"https://api.weather.com");
}
}
graph TD A["Your Code"] --> B["HttpClientFactory"] B --> C{Connection Pool} C --> D["Reused Connection 1"] C --> E["Reused Connection 2"] C --> F["Reused Connection 3"] D --> G["External API"] E --> G F --> G
Why Itโs Amazing
| Old Way | With Factory |
|---|---|
| New connection each time | Reuses connections |
| Can run out of ports | Managed pool |
| No retry logic | Built-in resilience |
| Hard to configure | Centralized setup |
๐ท๏ธ Named HttpClients: Specialized Delivery Drivers
The Idea
Imagine your restaurant orders from:
- ๐ฅฌ FreshFarms for vegetables
- ๐ฅฉ MeatMasters for meat
- ๐ง DairyDelight for dairy
Each supplier has different:
- Addresses
- Login credentials
- Delivery rules
Named HttpClients let you create a specialized driver for each!
Setting Up Named Clients
// In Program.cs
builder.Services.AddHttpClient(
"FreshFarms", client =>
{
client.BaseAddress = new Uri(
"https://api.freshfarms.com/");
client.DefaultRequestHeaders.Add(
"API-Key", "farm-secret-123");
client.Timeout = TimeSpan
.FromSeconds(30);
});
builder.Services.AddHttpClient(
"MeatMasters", client =>
{
client.BaseAddress = new Uri(
"https://api.meatmasters.com/");
client.DefaultRequestHeaders.Add(
"Auth", "meat-token-456");
});
Using Named Clients
public class OrderService
{
private readonly IHttpClientFactory _factory;
public OrderService(
IHttpClientFactory factory)
{
_factory = factory;
}
public async Task OrderVegetables()
{
// Get the FreshFarms specialist!
var client = _factory
.CreateClient("FreshFarms");
// BaseAddress already set!
var veggies = await client
.GetAsync("vegetables/today");
}
public async Task OrderMeat()
{
// Get the MeatMasters specialist!
var client = _factory
.CreateClient("MeatMasters");
var meat = await client
.GetAsync("cuts/premium");
}
}
graph TD A["OrderService"] --> B["HttpClientFactory"] B --> C[""FreshFarms" Client"] B --> D[""MeatMasters" Client"] B --> E[""DairyDelight" Client"] C --> F["FreshFarms API"] D --> G["MeatMasters API"] E --> H["DairyDelight API"]
Key Benefits
โ Organized โ Each API has its own config โ Reusable โ Request โFreshFarmsโ anywhere โ Safe โ Credentials stored centrally โ Flexible โ Easy to update one without affecting others
๐ Typed HttpClients: Expert Drivers with Deep Knowledge
The Evolution
Named clients are great, but what if your driver:
- Knew EXACTLY what to order?
- Could speak the supplierโs language fluently?
- Never made mistakes with order formats?
Thatโs a Typed HttpClient โ a specialized expert!
Creating a Typed Client
Step 1: Create a dedicated service class
public class GitHubClient
{
private readonly HttpClient _client;
public GitHubClient(HttpClient client)
{
// HttpClient is injected
_client = client;
_client.BaseAddress = new Uri(
"https://api.github.com/");
_client.DefaultRequestHeaders.Add(
"User-Agent", "MyApp");
}
// Typed method โ returns real objects!
public async Task<GitHubUser?>
GetUser(string username)
{
return await _client
.GetFromJsonAsync<GitHubUser>(
quot;users/{username}");
}
public async Task<List<Repo>?>
GetRepos(string username)
{
return await _client
.GetFromJsonAsync<List<Repo>>(
quot;users/{username}/repos");
}
}
public class GitHubUser
{
public string Login { get; set; }
public string Name { get; set; }
public int Followers { get; set; }
}
public class Repo
{
public string Name { get; set; }
public int Stars { get; set; }
}
Step 2: Register it
// In Program.cs
builder.Services.AddHttpClient<GitHubClient>();
Using Your Typed Client
public class ProfileController : Controller
{
private readonly GitHubClient _github;
public ProfileController(
GitHubClient github)
{
// Inject directly - no factory!
_github = github;
}
public async Task<IActionResult> Show(
string username)
{
// Clean, typed methods!
var user = await _github
.GetUser(username);
var repos = await _github
.GetRepos(username);
return View(new { user, repos });
}
}
graph TD A["Controller"] --> B["GitHubClient"] B --> C["HttpClient"] C --> D["GitHub API"] B --> E["GetUser Method"] B --> F["GetRepos Method"] E --> G["Returns GitHubUser"] F --> H["Returns List of Repos"]
Why Typed Clients Are Amazing
| Feature | Named Client | Typed Client |
|---|---|---|
| Configuration | โ Centralized | โ Centralized |
| Type Safety | โ Raw strings | โ Real objects |
| IntelliSense | โ No help | โ Full support |
| Testability | Medium | Excellent |
| Encapsulation | Low | High |
๐งฉ Complete Example: All Three Patterns Together
Hereโs how a real app might use all three patterns:
// Program.cs
// 1. Basic factory (for one-off requests)
builder.Services.AddHttpClient();
// 2. Named client (for weather API)
builder.Services.AddHttpClient(
"Weather", client =>
{
client.BaseAddress = new Uri(
"https://api.weather.com/");
});
// 3. Typed client (for GitHub)
builder.Services.AddHttpClient<GitHubClient>(
client =>
{
client.BaseAddress = new Uri(
"https://api.github.com/");
client.DefaultRequestHeaders.Add(
"User-Agent", "MyAwesomeApp");
});
๐ฏ Quick Decision Guide
Which pattern should I use?
graph TD A["Need to call an API?"] --> B{How often?} B -->|Once or rarely| C["Basic Factory"] B -->|Multiple times| D{Same API?} D -->|Different APIs| E["Named Clients"] D -->|Same API, many methods| F["Typed Client"] C --> G["IHttpClientFactory.CreateClient"] E --> H["CreateClient with name"] F --> I["Inject GitHubClient directly"]
๐ Key Takeaways
- Never create
new HttpClient()manually in a service - HttpClientFactory manages connection pooling for you
- Named Clients are perfect for multiple external APIs
- Typed Clients give you type safety and clean code
- All patterns work together beautifully!
๐ก Pro Tips
๐ Start Simple: Begin with basic
AddHttpClient(), then evolve to named/typed as your app grows.
๐ Connection Reuse: The factory automatically recycles connections every 2 minutes to pick up DNS changes.
๐ก๏ธ Add Polly: Combine with Polly library for automatic retries and circuit breakers!
// Bonus: Add resilience!
builder.Services.AddHttpClient<GitHubClient>()
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(3,
_ => TimeSpan.FromSeconds(1)));
๐ฌ Your Journey
You started with a chaotic restaurant hiring new drivers for every order. Now you have:
โ A smart delivery manager (HttpClientFactory) โ Specialized drivers for each supplier (Named Clients) โ Expert drivers who know everything (Typed Clients)
Your restaurant (app) runs smoothly, efficiently, and never runs out of delivery vehicles! ๐โจ
Now go build amazing APIs with confidence! ๐
