Linger.HttpClient.Contracts
1.5.1-preview
See the version list below for details.
dotnet add package Linger.HttpClient.Contracts --version 1.5.1-preview
NuGet\Install-Package Linger.HttpClient.Contracts -Version 1.5.1-preview
<PackageReference Include="Linger.HttpClient.Contracts" Version="1.5.1-preview" />
<PackageVersion Include="Linger.HttpClient.Contracts" Version="1.5.1-preview" />
<PackageReference Include="Linger.HttpClient.Contracts" />
paket add Linger.HttpClient.Contracts --version 1.5.1-preview
#r "nuget: Linger.HttpClient.Contracts, 1.5.1-preview"
#:package Linger.HttpClient.Contracts@1.5.1-preview
#addin nuget:?package=Linger.HttpClient.Contracts&version=1.5.1-preview&prerelease
#tool nuget:?package=Linger.HttpClient.Contracts&version=1.5.1-preview&prerelease
Linger.HttpClient.Contracts
Standard interfaces and contracts for HTTP client operations.
Features
- Interface Decoupling: Separate business logic from HTTP implementations
- Implementation Flexibility: Support multiple HTTP client implementations
- Testing Friendly: Easy unit testing and mocking
- Strongly Typed: Generic
ApiResult<T>for type safety - Async Support: Full async/await pattern
Installation
# Core contracts
dotnet add package Linger.HttpClient.Contracts
# Production implementation
dotnet add package Linger.HttpClient.Standard
Core Interfaces
IHttpClient
public interface IHttpClient
{
Task<ApiResult<T>> CallApi<T>(
string url,
object? queryParams = null,
int? timeout = null,
CancellationToken cancellationToken = default);
Task<ApiResult<T>> CallApi<T>(
string url,
HttpMethodEnum method,
object? requestBody = null,
object? queryParams = null,
int? timeout = null,
CancellationToken cancellationToken = default);
Task<ApiResult<Stream>> DownloadStreamAsync(
string url,
int? timeout = null,
CancellationToken cancellationToken = default);
Task<ApiResult> DownloadToFileAsync(
string url,
string destinationPath,
int? timeout = null,
int bufferSize = 8192,
IProgress<(long downloaded, long? total)>? progress = null,
CancellationToken cancellationToken = default);
}
ApiResult<T>
public class ApiResult<T>
{
public bool IsSuccess { get; }
public T Data { get; set; }
public string? ErrorMsg { get; set; }
public HttpStatusCode? StatusCode { get; set; }
public IEnumerable<Error> Errors { get; set; }
}
Basic Usage
// Register in DI
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;
}
}
Linger.Results Integration
ApiResult seamlessly integrates with Linger.Results:
// Server using Linger.Results (generic Result: returns data)
public async Task<Result<User>> GetUserAsync(int id)
{
var user = await _userRepository.GetUserAsync(id);
return user is not null ? Result<User>.Success(user) : Result<User>.NotFound("User not found");
}
// Server using Linger.Results (non-generic Result: only indicates success/failure)
public async Task<Result> UpdateUserAsync(UpdateUserRequest request)
{
await _userRepository.UpdateUserAsync(request);
return Result.Success();
}
// Client receives structured errors
var apiResult = await _httpClient.CallApi<User>($"api/users/{id}");
if (!apiResult.IsSuccess)
{
// Automatically mapped error information
foreach (var error in apiResult.Errors)
Console.WriteLine($"Error: {error.Code} - {error.Message}");
}
// For non-generic Result, the response usually has no Data; check IsSuccess / StatusCode / Errors
ApiResult commandResult = await _httpClient.DownloadToFileAsync("api/files/export", "export.zip");
if (commandResult.IsSuccess)
{
Console.WriteLine("Operation succeeded");
}
else
{
Console.WriteLine($"HTTP Status: {commandResult.StatusCode}");
Console.WriteLine($"Error Message: {commandResult.ErrorMsg}");
}
Error Handling
var result = await _httpClient.CallApi<User>("api/users/123");
if (result.IsSuccess)
{
var user = result.Data;
// Handle success
}
else
{
// Handle error
Console.WriteLine($"HTTP Status: {result.StatusCode}");
Console.WriteLine($"Error Message: {result.ErrorMsg}");
foreach (var error in result.Errors)
{
Console.WriteLine($"Detailed Error: {error.Code} - {error.Message}");
}
}
JSON Serialization Configuration
HttpClientBase provides default JSON serialization configuration with a "secure-by-default" approach:
Response Deserialization Configuration
HttpClientBase.DefaultResponseOptions is used for deserializing HTTP responses:
- Encoder:
JavaScriptEncoder.Default(safer escaping strategy) - Number handling: Lenient (allows reading numbers from strings,
AllowReadingFromString) - Other settings: Case-insensitive properties, CamelCase naming, ignore nulls, disallow trailing commas and comments, ignore cycles
- Built-in converters:
JsonObjectConverter,DateTimeConverter,DateTimeNullConverter,DataTableJsonConverter
Request Serialization Configuration
HttpClientBase.DefaultRequestOptions is used for serializing HTTP requests:
- Encoder:
JavaScriptEncoder.Default - Based on standard Web defaults
- Converters: Only includes
DateTimeConverter
Unified JSON Configuration Management
It's recommended to use Linger.Json.JsonDefaults for unified JSON configuration:
using Linger.Json;
// Use factory methods to get pre-configured options
var responseOptions = JsonDefaults.CreateResponseOptions(); // HTTP responses
var requestOptions = JsonDefaults.CreateRequestOptions(); // HTTP requests
// Apply configuration in WebAPI
builder.Services.AddControllers()
.AddJsonOptions(options =>
JsonDefaults.ApplyDefaultConfiguration(options.JsonSerializerOptions));
For detailed configuration documentation, see Linger/Json/JsonDefaults.README.md
Custom Configuration
Prefer overriding GetRequestJsonOptions() / GetResponseJsonOptions() to provide custom JSON options rather than replacing the entire serialization implementation. Example:
using Linger.Json;
using Linger.Json.JsonConverter;
public class CustomHttpClient : HttpClientBase
{
protected override JsonSerializerOptions GetRequestJsonOptions()
{
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
WriteIndented = true
};
options.Converters.Add(new DateTimeConverter());
return options;
}
protected override JsonSerializerOptions GetResponseJsonOptions()
{
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
PropertyNameCaseInsensitive = true
};
options.Converters.Add(new DateTimeConverter());
options.Converters.Add(new JsonObjectConverter());
return options;
}
}
If you need full control over serialization you can still override CreateHttpContent, but prefer the two methods above to keep behavior consistent.
Best Practices
- Use dependency injection to manage HTTP client lifecycle
- Leverage
ApiResult's structured error handling - Inherit from existing implementations when implementing custom error handling
- Use
CancellationTokento support request cancellation - Use mock implementations in unit tests
| 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.Utils (>= 1.5.1-preview)
-
.NETStandard 2.0
- Linger.Utils (>= 1.5.1-preview)
-
net10.0
- Linger.Utils (>= 1.5.1-preview)
-
net8.0
- Linger.Utils (>= 1.5.1-preview)
-
net9.0
- Linger.Utils (>= 1.5.1-preview)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Linger.HttpClient.Contracts:
| Package | Downloads |
|---|---|
|
Linger.HttpClient.Standard
A lightweight implementation of Linger.HttpClient.Contracts using standard .NET HttpClient. Provides typed response parsing, per-request headers, and streaming file transfers. Seamlessly integrates with .NET's HttpClientFactory for optimal connection management. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.0-preview.1 | 32 | 8/29/2026 |
| 1.6.4 | 140 | 8/16/2026 |
| 1.6.3 | 131 | 8/5/2026 |
| 1.6.2 | 159 | 8/2/2026 |
| 1.6.0 | 148 | 7/25/2026 |
| 1.5.5 | 147 | 7/23/2026 |
| 1.5.4-preview | 116 | 7/21/2026 |
| 1.5.3-preview | 125 | 7/20/2026 |
| 1.5.2-preview | 144 | 7/19/2026 |
| 1.5.1-preview | 125 | 7/15/2026 |
| 1.5.0-preview | 131 | 7/14/2026 |
| 1.4.4-preview | 145 | 6/16/2026 |
| 1.4.3-preview | 141 | 6/15/2026 |
| 1.4.2 | 174 | 5/20/2026 |
| 1.4.1-preview | 146 | 5/12/2026 |
| 1.4.0 | 162 | 5/6/2026 |
| 1.3.3-preview | 131 | 5/5/2026 |
| 1.3.2-preview | 135 | 4/29/2026 |
| 1.3.1-preview | 145 | 4/28/2026 |
| 1.3.0-preview | 143 | 4/27/2026 |