Linger.HttpClient.Standard
1.0.0-preview2
dotnet add package Linger.HttpClient.Standard --version 1.0.0-preview2
NuGet\Install-Package Linger.HttpClient.Standard -Version 1.0.0-preview2
<PackageReference Include="Linger.HttpClient.Standard" Version="1.0.0-preview2" />
<PackageVersion Include="Linger.HttpClient.Standard" Version="1.0.0-preview2" />
<PackageReference Include="Linger.HttpClient.Standard" />
paket add Linger.HttpClient.Standard --version 1.0.0-preview2
#r "nuget: Linger.HttpClient.Standard, 1.0.0-preview2"
#:package Linger.HttpClient.Standard@1.0.0-preview2
#addin nuget:?package=Linger.HttpClient.Standard&version=1.0.0-preview2&prerelease
#tool nuget:?package=Linger.HttpClient.Standard&version=1.0.0-preview2&prerelease
Linger.HttpClient.Standard
Production-ready HTTP client implementation based on System.Net.Http.HttpClient.
Features
- Zero Dependencies: Built on standard .NET libraries
- HttpClientFactory Integration: Proper socket management and connection pooling
- Proper Resource Management: Automatic disposal tracking with ownership pattern to prevent resource leaks
- Comprehensive Logging: Built-in performance monitoring
- Linger.Results Integration: Seamless error mapping from server to client
- ProblemDetails Support: Native RFC 7807 support
Recent Updates
v1.0.0+ - Streaming Download Optimization
- ✅ Streaming Download Support: New
DownloadStreamAsyncandDownloadToFileAsyncmethods - ✅ Memory Optimization: Large file download memory usage reduced from 100% file size to ~8KB buffer (99.99% reduction)
- ✅ Progress Reporting:
DownloadToFileAsyncsupports real-time progress callbacks - ✅ HttpResponseMode: New
Buffered/Streamedmodes for different scenarios
v0.9.8 - Resource Management Improvements
- ✅ Fixed Resource Leak: Added ownership tracking to prevent disposing externally-provided
HttpClientinstances - ✅ Safe Disposal: Only disposes
HttpClientinstances that it created internally - ✅ HttpClientFactory Compatible: Properly handles
HttpClientinstances from factory without disposal issues
Installation
dotnet add package Linger.HttpClient.Standard
Basic Usage
✅ Recommended: Using HttpClientFactory (Best Practice)
// Register in DI container
services.AddHttpClient<IHttpClient, StandardHttpClient>();
// Use in service
public class UserService
{
private readonly IHttpClient _httpClient;
public UserService(IHttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<User?> GetUserAsync(int id)
{
var result = await _httpClient.CallApi<User>($"api/users/{id}");
return result.IsSuccess ? result.Data : null;
}
}
⚠️ Using Existing HttpClient Instance
If you already have an HttpClient instance (e.g., from HttpClientFactory), you can wrap it:
// The StandardHttpClient will NOT dispose the external HttpClient
var httpClient = httpClientFactory.CreateClient("MyClient");
using var standardClient = new StandardHttpClient(httpClient, logger);
var result = await standardClient.CallApi<User>("api/users/123");
⚠️ Direct Instantiation (Not Recommended for Production)
Only use this approach for testing or simple scenarios:
// ⚠️ Creates new HttpClient instance
// StandardHttpClient will dispose it when disposed
using var client = new StandardHttpClient("https://api.example.com", logger);
var result = await client.CallApi<User>("api/users/123");
// HttpClient is automatically disposed here
Why HttpClientFactory is Recommended:
- ✅ Proper connection pooling
- ✅ Automatic DNS refresh handling
- ✅ Prevents socket exhaustion
- ✅ Built-in lifetime management
Linger.Results Integration
Seamless integration with Linger.Results framework for unified error handling:
// Server using Linger.Results
[HttpGet("{id}")]
public async Task<IActionResult> GetUser(int id)
{
var result = await _userService.GetUserAsync(id);
return result.ToActionResult(); // Automatic HTTP status mapping
}
// Client automatically receives structured errors
var apiResult = await _httpClient.CallApi<User>($"api/users/{id}");
if (!apiResult.IsSuccess)
{
foreach (var error in apiResult.Errors)
Console.WriteLine($"Error: {error.Code} - {error.Message}");
}
ProblemDetails Support
Native support for RFC 7807 ProblemDetails format:
// Automatically parse ProblemDetails responses
var result = await _httpClient.CallApi<User>("api/users", HttpMethodEnum.Post, invalidUser);
if (!result.IsSuccess)
{
Console.WriteLine($"Error: {result.ErrorMsg}");
foreach (var error in result.Errors)
{
Console.WriteLine($"Field: {error.Code}, Error: {error.Message}");
}
}
Core Methods
CallApi<T>
public async Task<ApiResult<T>> CallApi<T>(string url, HttpMethodEnum method = HttpMethodEnum.Get,
object? data = null, Dictionary<string, string>? headers = null)
Streaming Download (New in v0.9.8+)
For large file downloads, use streaming methods to minimize memory consumption:
DownloadStreamAsync
// Download large file as stream (minimal memory usage)
var result = await _httpClient.DownloadStreamAsync("https://example.com/large-file.zip");
if (result.IsSuccess && result.Data is not null)
{
using var stream = result.Data;
// Process stream directly without loading entire file into memory
// Remember to dispose the stream when done
}
DownloadToFileAsync (Recommended)
// Download directly to file with progress reporting
var progress = new Progress<(long downloaded, long? total)>(p =>
{
var percent = p.total.HasValue ? (double)p.downloaded / p.total.Value * 100 : 0;
Console.WriteLine($"Downloaded: {p.downloaded} bytes ({percent:F1}%)");
});
var result = await _httpClient.DownloadToFileAsync(
url: "https://example.com/large-file.zip",
destinationPath: "output.zip",
progress: progress
);
if (result.IsSuccess)
{
Console.WriteLine("Download completed successfully!");
}
Benefits of Streaming Download:
- ✅ Minimal memory usage (~8KB buffer vs full file size)
- ✅ Supports files of any size
- ✅ Built-in progress reporting
- ✅ Cancellation token support
Streaming Download (New in v0.9.8+)
For large file downloads, use streaming methods to minimize memory consumption:
DownloadStreamAsync
// Download large file as stream (minimal memory usage)
var result = await _httpClient.DownloadStreamAsync("https://example.com/large-file.zip");
if (result.IsSuccess && result.Data is not null)
{
using var stream = result.Data;
// Process stream directly without loading entire file into memory
// Remember to dispose the stream when done
}
DownloadToFileAsync (Recommended)
// Download directly to file with progress reporting
var progress = new Progress<(long downloaded, long? total)>(p =>
{
var percent = p.total.HasValue ? (double)p.downloaded / p.total.Value * 100 : 0;
Console.WriteLine($"Downloaded: {p.downloaded} bytes ({percent:F1}%)");
});
var result = await _httpClient.DownloadToFileAsync(
url: "https://example.com/large-file.zip",
destinationPath: "output.zip",
progress: progress
);
if (result.IsSuccess)
{
Console.WriteLine("Download completed successfully!");
}
Benefits of Streaming Download:
- ✅ Minimal memory usage (~8KB buffer vs full file size)
- ✅ Supports files of any size
- ✅ Built-in progress reporting
- ✅ Cancellation token support
Performance Comparison (Downloading 500MB file):
| Method | Memory Usage | Notes |
|---|---|---|
CallApi<byte[]> |
~500MB | Loads entire file into memory |
DownloadStreamAsync |
~8KB | Only buffer memory usage |
DownloadToFileAsync |
~8KB | Customizable buffer size |
Supported HTTP methods:
- GET: Retrieve data
- POST: Create resource
- PUT: Update resource
- DELETE: Delete resource
- PATCH: Partial update
Error Handling
var result = await _httpClient.CallApi<User>("api/users/123");
if (result.IsSuccess)
{
var user = result.Data;
}
else
{
// Check HTTP status code
switch (result.StatusCode)
{
case HttpStatusCode.NotFound:
Console.WriteLine("User not found");
break;
case HttpStatusCode.Unauthorized:
Console.WriteLine("Authentication required");
break;
}
// Access detailed errors
foreach (var error in result.Errors)
{
Console.WriteLine($"Error: {error.Code} - {error.Message}");
}
}
Best Practices
- Use HttpClientFactory for dependency injection
- Use
usingstatements to ensure proper resource disposal - Enable detailed logging for debugging
- Set reasonable timeout values
- Handle network exceptions and timeouts
- Use streaming methods for large file downloads (
DownloadStreamAsyncorDownloadToFileAsync) to save memory
More Examples
For complete streaming download examples and performance comparisons, see STREAMING_DOWNLOAD_EXAMPLE.md
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 is compatible. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.7.2
- Linger.HttpClient.Contracts (>= 1.0.0-preview2)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.9)
-
.NETStandard 2.0
- Linger.HttpClient.Contracts (>= 1.0.0-preview2)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.9)
-
net10.0
- Linger.HttpClient.Contracts (>= 1.0.0-preview2)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0-rc.2.25502.107)
-
net8.0
- Linger.HttpClient.Contracts (>= 1.0.0-preview2)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.9)
-
net9.0
- Linger.HttpClient.Contracts (>= 1.0.0-preview2)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.9)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0-preview2 | 64 | 11/6/2025 |
| 1.0.0-preview1 | 70 | 11/5/2025 |
| 0.9.8 | 131 | 10/14/2025 |
| 0.9.7-preview | 119 | 10/13/2025 |
| 0.9.6-preview | 102 | 10/12/2025 |
| 0.9.5 | 105 | 9/28/2025 |
| 0.9.4-preview | 131 | 9/25/2025 |
| 0.9.3-preview | 151 | 9/22/2025 |
| 0.9.1-preview | 257 | 9/16/2025 |
| 0.9.0-preview | 84 | 9/12/2025 |
| 0.8.5-preview | 183 | 8/31/2025 |
| 0.8.4-preview | 301 | 8/25/2025 |
| 0.8.3-preview | 159 | 8/20/2025 |
| 0.8.2-preview | 192 | 8/4/2025 |
| 0.8.1-preview | 114 | 7/30/2025 |
| 0.8.0-preview | 560 | 7/22/2025 |
| 0.7.2 | 178 | 6/3/2025 |
| 0.7.1 | 176 | 5/21/2025 |
| 0.7.0 | 175 | 5/19/2025 |
| 0.6.0-alpha | 185 | 4/28/2025 |
| 0.5.0-alpha | 181 | 4/10/2025 |
| 0.4.0-alpha | 176 | 4/1/2025 |