Table of contents

Can I use dependency injection with HttpClient (C#)?

Yes, you can and should use dependency injection with HttpClient in C#. The recommended approach is using IHttpClientFactory, which was introduced in ASP.NET Core 2.1 to solve common issues like socket exhaustion and DNS changes that occur with direct HttpClient instantiation.

Why Use Dependency Injection with HttpClient?

  • Proper Resource Management: Prevents socket exhaustion and handles connection pooling
  • DNS Changes: Automatically respects DNS TTL settings
  • Testability: Easier to mock and unit test HTTP interactions
  • Configuration Management: Centralized configuration for all HTTP clients
  • Performance: Reuses connections and reduces overhead

Basic Setup

1. Register IHttpClientFactory

In Program.cs (or Startup.cs in older versions):

var builder = WebApplication.CreateBuilder(args);

// Register IHttpClientFactory
builder.Services.AddHttpClient();

// Register your services
builder.Services.AddScoped<IApiService, ApiService>();

var app = builder.Build();

2. Inject IHttpClientFactory

public interface IApiService
{
    Task<string> GetDataAsync(string endpoint);
}

public class ApiService : IApiService
{
    private readonly IHttpClientFactory _httpClientFactory;

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

    public async Task<string> GetDataAsync(string endpoint)
    {
        using var httpClient = _httpClientFactory.CreateClient();

        var response = await httpClient.GetAsync(endpoint);
        response.EnsureSuccessStatusCode();

        return await response.Content.ReadAsStringAsync();
    }
}

Advanced Patterns

Named Clients

Configure specific clients with predefined settings:

// Registration
builder.Services.AddHttpClient("ApiClient", client =>
{
    client.BaseAddress = new Uri("https://api.example.com/");
    client.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");
    client.DefaultRequestHeaders.Add("Accept", "application/json");
    client.Timeout = TimeSpan.FromSeconds(30);
});

// Usage
public class ApiService : IApiService
{
    private readonly IHttpClientFactory _httpClientFactory;

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

    public async Task<ApiResponse> GetUserAsync(int userId)
    {
        using var httpClient = _httpClientFactory.CreateClient("ApiClient");

        var response = await httpClient.GetAsync($"users/{userId}");
        response.EnsureSuccessStatusCode();

        var json = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<ApiResponse>(json);
    }
}

Typed Clients

Create strongly-typed HTTP clients:

public class GitHubApiClient
{
    private readonly HttpClient _httpClient;

    public GitHubApiClient(HttpClient httpClient)
    {
        _httpClient = httpClient;
        _httpClient.BaseAddress = new Uri("https://api.github.com/");
        _httpClient.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");
    }

    public async Task<GitHubUser> GetUserAsync(string username)
    {
        var response = await _httpClient.GetAsync($"users/{username}");
        response.EnsureSuccessStatusCode();

        var json = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<GitHubUser>(json);
    }

    public async Task<IEnumerable<GitHubRepo>> GetUserReposAsync(string username)
    {
        var response = await _httpClient.GetAsync($"users/{username}/repos");
        response.EnsureSuccessStatusCode();

        var json = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<IEnumerable<GitHubRepo>>(json);
    }
}

// Registration
builder.Services.AddHttpClient<GitHubApiClient>();

// Usage in controller
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly GitHubApiClient _gitHubClient;

    public UsersController(GitHubApiClient gitHubClient)
    {
        _gitHubClient = gitHubClient;
    }

    [HttpGet("{username}")]
    public async Task<IActionResult> GetUser(string username)
    {
        try
        {
            var user = await _gitHubClient.GetUserAsync(username);
            return Ok(user);
        }
        catch (HttpRequestException)
        {
            return NotFound();
        }
    }
}

Configuration with Options Pattern

Combine with the Options pattern for flexible configuration:

public class ApiClientOptions
{
    public string BaseUrl { get; set; } = string.Empty;
    public int TimeoutSeconds { get; set; } = 30;
    public string ApiKey { get; set; } = string.Empty;
}

// Registration
builder.Services.Configure<ApiClientOptions>(
    builder.Configuration.GetSection("ApiClient"));

builder.Services.AddHttpClient<ApiService>((serviceProvider, client) =>
{
    var options = serviceProvider.GetRequiredService<IOptions<ApiClientOptions>>().Value;

    client.BaseAddress = new Uri(options.BaseUrl);
    client.Timeout = TimeSpan.FromSeconds(options.TimeoutSeconds);
    client.DefaultRequestHeaders.Add("X-API-Key", options.ApiKey);
});

// appsettings.json
{
  "ApiClient": {
    "BaseUrl": "https://api.example.com/",
    "TimeoutSeconds": 60,
    "ApiKey": "your-api-key-here"
  }
}

Adding Resilience with Polly

Enhance your HTTP clients with retry policies:

builder.Services.AddHttpClient<ApiService>()
    .AddPolicyHandler(GetRetryPolicy());

static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return Policy
        .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
        .Or<HttpRequestException>()
        .WaitAndRetryAsync(
            retryCount: 3,
            sleepDurationProvider: retryAttempt => 
                TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
            onRetry: (outcome, timespan, retryCount, context) =>
            {
                Console.WriteLine($"Retry {retryCount} after {timespan} seconds");
            });
}

Best Practices

  1. Always use IHttpClientFactory instead of direct HttpClient instantiation
  2. Don't store HttpClient instances as fields - create them as needed
  3. Use using statements when creating clients from factory
  4. Configure base addresses and headers at registration time
  5. Implement proper error handling for HTTP operations
  6. Consider using typed clients for complex API interactions
  7. Add retry policies for improved resilience

Using dependency injection with HttpClient through IHttpClientFactory is the recommended pattern for modern C# applications, providing better resource management, testability, and maintainability.

Try WebScraping.AI for Your Web Scraping Needs

Looking for a powerful web scraping solution? WebScraping.AI provides an LLM-powered API that combines Chromium JavaScript rendering with rotating proxies for reliable data extraction.

Key Features:

  • AI-powered extraction: Ask questions about web pages or extract structured data fields
  • JavaScript rendering: Full Chromium browser support for dynamic content
  • Rotating proxies: Datacenter and residential proxies from multiple countries
  • Easy integration: Simple REST API with SDKs for Python, Ruby, PHP, and more
  • Reliable & scalable: Built for developers who need consistent results

Getting Started:

Get page content with AI analysis:

curl "https://api.webscraping.ai/ai/question?url=https://example.com&question=What is the main topic?&api_key=YOUR_API_KEY"

Extract structured data:

curl "https://api.webscraping.ai/ai/fields?url=https://example.com&fields[title]=Page title&fields[price]=Product price&api_key=YOUR_API_KEY"

Try in request builder

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon