IHttpClientFactory Integration Guide
This document explains how to use Unsplasharp with IHttpClientFactory
for modern .NET applications.
Overview
Unsplasharp now supports IHttpClientFactory
, Microsoft's recommended pattern for managing HttpClient instances in .NET applications. This provides several benefits:
- Proper HttpClient lifecycle management - Automatic DNS refresh and connection pooling
- Dependency injection integration - Seamless integration with DI containers
- Configuration centralization - Configure HTTP clients in one place
- Better testability - Easy to mock and test HTTP interactions
- Resource management - Prevents socket exhaustion and DNS issues
Quick Start
ASP.NET Core Integration
// Program.cs or Startup.cs
using Unsplasharp.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Add Unsplasharp with IHttpClientFactory
builder.Services.AddUnsplasharp("YOUR_APPLICATION_ID");
// Or with configuration
builder.Services.AddUnsplasharp(options =>
{
options.ApplicationId = "YOUR_APPLICATION_ID";
options.Secret = "YOUR_SECRET"; // Optional
options.ConfigureHttpClient = client =>
{
client.Timeout = TimeSpan.FromSeconds(60);
// Add custom headers, etc.
};
});
var app = builder.Build();
Using in Controllers
[ApiController]
[Route("api/[controller]")]
public class PhotosController : ControllerBase
{
private readonly UnsplasharpClient _unsplashClient;
public PhotosController(UnsplasharpClient unsplashClient)
{
_unsplashClient = unsplashClient;
}
[HttpGet("random")]
public async Task<IActionResult> GetRandomPhoto()
{
var photo = await _unsplashClient.GetRandomPhoto();
return Ok(photo);
}
[HttpGet("search")]
public async Task<IActionResult> SearchPhotos(string query, int page = 1)
{
var photos = await _unsplashClient.SearchPhotos(query, page);
return Ok(photos);
}
}
Manual Configuration
Console Application
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Unsplasharp;
using Unsplasharp.Extensions;
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
// Add logging
services.AddLogging(builder => builder.AddConsole());
// Add Unsplasharp
services.AddUnsplasharp("YOUR_APPLICATION_ID");
})
.Build();
// Use the client
var unsplashClient = host.Services.GetRequiredService<UnsplasharpClient>();
var photo = await unsplashClient.GetRandomPhoto();
Console.WriteLine($"Photo by {photo?.User?.Name}: {photo?.Urls?.Regular}");
Manual DI Setup
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Logging;
using Unsplasharp;
var services = new ServiceCollection();
// Add required services
services.AddHttpClient();
services.AddLogging();
// Add Unsplasharp manually
services.AddScoped<UnsplasharpClient>(provider =>
{
var httpClientFactory = provider.GetRequiredService<IHttpClientFactory>();
var logger = provider.GetService<ILogger<UnsplasharpClient>>();
return new UnsplasharpClient(
applicationId: "YOUR_APPLICATION_ID",
secret: null, // Optional
logger: logger,
httpClientFactory: httpClientFactory
);
});
var serviceProvider = services.BuildServiceProvider();
var client = serviceProvider.GetRequiredService<UnsplasharpClient>();
Advanced Configuration
Custom HttpClient Configuration
services.AddUnsplasharp(options =>
{
options.ApplicationId = "YOUR_APPLICATION_ID";
options.ConfigureHttpClient = client =>
{
// Custom timeout
client.Timeout = TimeSpan.FromMinutes(2);
// Custom headers
client.DefaultRequestHeaders.Add("X-Custom-Header", "MyValue");
// Custom user agent
client.DefaultRequestHeaders.UserAgent.ParseAdd("MyApp/1.0");
};
});
Multiple Clients with Different Configurations
// Register multiple named clients
services.AddHttpClient("unsplash-primary", client =>
{
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Client-ID", "PRIMARY_APP_ID");
client.Timeout = TimeSpan.FromSeconds(30);
});
services.AddHttpClient("unsplash-secondary", client =>
{
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Client-ID", "SECONDARY_APP_ID");
client.Timeout = TimeSpan.FromSeconds(60);
});
// Register clients manually
services.AddScoped<UnsplasharpClient>("primary", provider =>
{
var factory = provider.GetRequiredService<IHttpClientFactory>();
var logger = provider.GetService<ILogger<UnsplasharpClient>>();
return new UnsplasharpClient("PRIMARY_APP_ID", logger: logger, httpClientFactory: factory);
});
Configuration from appsettings.json
{
"Unsplash": {
"ApplicationId": "YOUR_APPLICATION_ID",
"Secret": "YOUR_SECRET",
"Timeout": "00:01:00"
}
}
// Configuration class
public class UnsplashOptions
{
public string ApplicationId { get; set; } = string.Empty;
public string? Secret { get; set; }
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30);
}
// In Program.cs
var unsplashOptions = builder.Configuration.GetSection("Unsplash").Get<UnsplashOptions>();
builder.Services.AddUnsplasharp(options =>
{
options.ApplicationId = unsplashOptions.ApplicationId;
options.Secret = unsplashOptions.Secret;
options.ConfigureHttpClient = client =>
{
client.Timeout = unsplashOptions.Timeout;
};
});
Backward Compatibility
The library maintains full backward compatibility. Existing code will continue to work:
// Legacy usage (still works)
var client = new UnsplasharpClient("YOUR_APPLICATION_ID");
var photos = await client.SearchPhotos("nature");
// With logging (still works)
var logger = loggerFactory.CreateLogger<UnsplasharpClient>();
var client = new UnsplasharpClient("YOUR_APPLICATION_ID", logger: logger);
Benefits of IHttpClientFactory
Automatic Resource Management
- Prevents socket exhaustion
- Handles DNS changes automatically
- Manages connection pooling
Better Performance
- Reuses connections efficiently
- Reduces memory allocations
- Optimizes network usage
Enhanced Reliability
- Built-in retry policies (when configured)
- Better error handling
- Improved timeout management
Improved Testability
// Easy to mock for testing
var mockFactory = new Mock<IHttpClientFactory>();
var mockHttpClient = new Mock<HttpClient>();
mockFactory.Setup(f => f.CreateClient("unsplash")).Returns(mockHttpClient.Object);
var client = new UnsplasharpClient("test", httpClientFactory: mockFactory.Object);
Migration Guide
From Legacy to IHttpClientFactory
Add the extension method:
// Old services.AddScoped(_ => new UnsplasharpClient("APP_ID")); // New services.AddUnsplasharp("APP_ID");
Update constructor calls:
// Old var client = new UnsplasharpClient("APP_ID"); // New (in DI context) // Inject UnsplasharpClient directly
Configuration changes:
// Old - manual HttpClient management // No centralized configuration // New - centralized configuration services.AddUnsplasharp(options => { options.ApplicationId = "APP_ID"; options.ConfigureHttpClient = client => { /* configure */ }; });
Troubleshooting
Common Issues
- Missing HttpClient registration: Ensure
AddHttpClient()
is called - DI scope issues: Register as Scoped or Transient, not Singleton
- Configuration not applied: Check that ConfigureHttpClient is called correctly
Debug Logging
Enable debug logging to see HTTP client creation:
services.AddLogging(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Debug);
});
This will show when HttpClients are created and reused.