Linger.HttpClient.Contracts
0.5.0-alpha
dotnet add package Linger.HttpClient.Contracts --version 0.5.0-alpha
NuGet\Install-Package Linger.HttpClient.Contracts -Version 0.5.0-alpha
<PackageReference Include="Linger.HttpClient.Contracts" Version="0.5.0-alpha" />
<PackageVersion Include="Linger.HttpClient.Contracts" Version="0.5.0-alpha" />
<PackageReference Include="Linger.HttpClient.Contracts" />
paket add Linger.HttpClient.Contracts --version 0.5.0-alpha
#r "nuget: Linger.HttpClient.Contracts, 0.5.0-alpha"
#addin nuget:?package=Linger.HttpClient.Contracts&version=0.5.0-alpha&prerelease
#tool nuget:?package=Linger.HttpClient.Contracts&version=0.5.0-alpha&prerelease
Linger.HttpClient.Contracts
Table of Contents
- Overview
- Features
- Installation
- ApiResult and Linger.Results Integration
- Dependency Injection Integration
- Basic Usage
- Error Handling
- Automatic Token Refresh
- Best Practices
Overview
Linger.HttpClient.Contracts defines standard interfaces and contracts for HTTP client operations, serving as the foundation for Linger HTTP client implementations. By using unified contracts, you can easily switch between different HTTP client implementations without modifying your business code.
Key Components
- Linger.HttpClient.Contracts: Core interfaces and contracts (this project)
- Linger.HttpClient.Standard: Implementation based on .NET standard HttpClient
Features
- Strongly typed HTTP client interfaces
- Support for various HTTP methods (GET, POST, PUT, DELETE)
- File upload capabilities
- Request/response handling
- User-friendly error handling
- Timeout management
Installation
# Install interfaces and contracts
dotnet add package Linger.HttpClient.Contracts
# Install implementation based on standard HttpClient
dotnet add package Linger.HttpClient.Standard
# For resilience features (automatic retries, circuit breaker, etc.)
dotnet add package Microsoft.Extensions.Http.Resilience
ApiResult and Linger.Results Integration
The ApiResult
type is specifically designed to work seamlessly with the Linger.Results
project, especially with its ASP.NET Core integration. The Errors
property in ApiResult
is specifically intended to receive error information converted by the ToActionResult()
method from the Linger.Results.AspNetCore
project.
Error Information Format Mapping
When an API server uses Linger.Results
to return results and converts them using the ToActionResult()
method:
// Server code
public Result<UserDto> GetUser(int id)
{
if (userNotFound)
return Result<UserDto>.NotFound("User not found");
return Result.Success(userDto);
}
// Controller
[HttpGet("{id}")]
public ActionResult<UserDto> GetUser(int id)
{
var result = _userService.GetUser(id);
return result.ToActionResult(); // Converts to JSON response with errors on failure
}
The client can receive and process these error messages directly through ApiResult
:
// Client code
var apiResult = await _httpClient.CallApi<UserDto>($"api/users/{id}");
if (!apiResult.IsSuccess)
{
// apiResult.Errors will contain error information converted by ToActionResult()
foreach (var error in apiResult.Errors)
{
Console.WriteLine($"Error code: {error.Code}, Message: {error.Message}");
}
}
Error Structure Mapping
The Error
record type in Linger.Results
is mapped to the ApiResult.Errors
collection:
Linger.Results (Server) | ApiResult (Client) |
---|---|
Error.Code | ApiError.Code |
Error.Message | ApiError.Message |
This design ensures consistent and complete transfer of error information from server to client, allowing the client to precisely understand and handle business errors returned by the server.
Dependency Injection Integration
Using HttpClientFactory
The recommended way to use Linger HTTP clients is with Microsoft's HttpClientFactory and HTTP Resilience:
// In your startup configuration
services.AddHttpClient<IHttpClient, StandardHttpClient>(client =>
{
client.BaseAddress = new Uri("https://api.example.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.Timeout = TimeSpan.FromSeconds(30);
})
.AddResilienceHandler("Default", builder =>
{
// Configure retry behavior
builder.AddRetry(new HttpRetryStrategyOptions
{
MaxRetryAttempts = 3,
Delay = TimeSpan.FromSeconds(2),
ShouldHandle = args =>
{
// Retry on server errors and rate limiting
return ValueTask.FromResult(args.Outcome.Result?.StatusCode is
HttpStatusCode.RequestTimeout or // 408
HttpStatusCode.TooManyRequests or // 429
HttpStatusCode.BadGateway or // 502
HttpStatusCode.ServiceUnavailable or // 503
HttpStatusCode.GatewayTimeout); // 504
}
});
});
In Service Classes
Once registered, you can inject and use IHttpClient in your services:
public class UserService
{
private readonly IHttpClient _httpClient;
public UserService(IHttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<ApiResult<UserInfo>> GetUserInfoAsync(string userId)
{
return await _httpClient.CallApi<UserInfo>($"api/users/{userId}");
}
public async Task<ApiResult<UserInfo>> CreateUserAsync(UserCreateModel model)
{
return await _httpClient.CallApi<UserInfo>("api/users", HttpMethodEnum.Post, model);
}
}
Basic Usage
GET Requests
// Simple GET request
var result = await _httpClient.CallApi<UserData>("api/users/1");
// With query parameters
var users = await _httpClient.CallApi<List<UserData>>("api/users",
new { page = 1, pageSize = 10 });
POST Requests
// POST with JSON body
var newUser = await _httpClient.CallApi<UserData>("api/users",
HttpMethodEnum.Post,
new { Name = "John", Email = "john@example.com" });
File Upload
// File upload with form data
byte[] fileData = File.ReadAllBytes("document.pdf");
var formData = new Dictionary<string, string>
{
{ "description", "Sample document" }
};
var uploadResult = await _httpClient.CallApi<UploadResponse>(
"api/files/upload",
HttpMethodEnum.Post,
formData,
fileData,
"document.pdf"
);
Error Handling
var result = await _httpClient.CallApi<UserData>($"api/users/{userId}");
if (result.IsSuccess)
{
// Process successful response
var user = result.Data;
Console.WriteLine($"User: {user.Name}");
}
else
{
// Handle error
Console.WriteLine($"Error: {result.ErrorMsg}");
// Check for specific status codes
if (result.StatusCode == HttpStatusCode.Unauthorized)
{
// Handle authentication error
}
}
Automatic Token Refresh
You can implement automatic token refresh using Microsoft.Extensions.Http.Resilience:
// Create a token refresh handler
public class TokenRefreshHandler
{
private readonly AppState _appState;
private readonly IServiceProvider _serviceProvider;
private readonly SemaphoreSlim _semaphore = new(1, 1);
public TokenRefreshHandler(AppState appState, IServiceProvider serviceProvider)
{
_appState = appState;
_serviceProvider = serviceProvider;
}
public void ConfigureTokenRefreshResiliencePipeline(ResiliencePipelineBuilder<HttpResponseMessage> builder)
{
builder.AddRetry(new HttpRetryStrategyOptions
{
MaxRetryAttempts = 1, // Only try refreshing once
ShouldHandle = args =>
{
bool shouldRetry = args.Outcome.Result?.StatusCode == HttpStatusCode.Unauthorized;
return ValueTask.FromResult(shouldRetry);
},
OnRetry = async context =>
{
// Thread-safe token refresh
await _semaphore.WaitAsync();
try
{
await RefreshTokenAsync();
}
finally
{
_semaphore.Release();
}
},
BackoffType = DelayBackoffType.Constant,
Delay = TimeSpan.Zero // Retry immediately after token refresh
});
}
private async Task RefreshTokenAsync()
{
// Implementation of token refresh logic
// ...
}
}
// Register and use in your application
services.AddSingleton<TokenRefreshHandler>();
services.AddHttpClient<IHttpClient, StandardHttpClient>(/* ... */)
.AddResilienceHandler("TokenRefresh", (builder, context) =>
{
var tokenRefreshHandler = context.ServiceProvider.GetRequiredService<TokenRefreshHandler>();
tokenRefreshHandler.ConfigureTokenRefreshResiliencePipeline(builder);
});
Best Practices
- Use HttpClientFactory for lifecycle management
- Set reasonable timeout values
- Implement proper error handling
- Use strongly typed models for API responses
- Enable automatic token refresh for better user experience
- Use Microsoft.Extensions.Http.Resilience for retry logic
For more information, see the Linger.HttpClient.Standard documentation.
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. |
.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 (>= 0.5.0-alpha)
- System.Net.Http (>= 4.3.4)
-
.NETStandard 2.0
- Linger.Utils (>= 0.5.0-alpha)
-
net8.0
- Linger.Utils (>= 0.5.0-alpha)
-
net9.0
- Linger.Utils (>= 0.5.0-alpha)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Linger.HttpClient.Contracts:
Package | Downloads |
---|---|
Linger.HttpClient.Flurl
Package Description |
|
Linger.HttpClient.Standard
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.5.0-alpha | 115 | 4/10/2025 |
0.4.0-alpha | 114 | 4/1/2025 |