HttpClient Patterns

Back

Loading concept...

๐Ÿš€ 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

  1. HttpClientFactory โ€” The delivery team manager
  2. Named HttpClients โ€” Specialized drivers for different suppliers
  3. 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:

  1. Buy a new car ๐Ÿš—
  2. Drive to the store
  3. Get milk
  4. 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["&quot;FreshFarms&quot; Client"] B --> D["&quot;MeatMasters&quot; Client"] B --> E["&quot;DairyDelight&quot; 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

  1. Never create new HttpClient() manually in a service
  2. HttpClientFactory manages connection pooling for you
  3. Named Clients are perfect for multiple external APIs
  4. Typed Clients give you type safety and clean code
  5. 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! ๐Ÿš€

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.