The TaskHub.Shared.Networking module is a high-performance, resilience-oriented inter-service communication stack. It provides a declarative framework for managing HttpClient instances, Polly-based retry policies, and automatic security context propagation.
The module uses a “Configuration-First” approach. When AddAppNetworkingSettings is called:
NetworkOptions containing global Defaults and a dictionary of named Services.OptionsResolver merges global defaults with service-specific overrides (e.g., a specific service might need a longer timeout than the default).HttpClient in the standard .NET IHttpClientFactory.AsyncRetryPolicy directly into the message handler pipeline of each client.BearerTokenHandler to forward authentication headers from the incoming request to the outgoing call.The module uses HttpPolicyExtensions.HandleTransientHttpError() to catch:
5xx Server Errors.408 Request Timeout.HttpRequestException (Network failures, DNS issues).The BearerTokenHandler is a DelegatingHandler that intercepts outgoing requests. It extracts the Authorization header from the current HttpContext and injects it into the outgoing HttpRequestMessage. This ensures that user identity is maintained throughout the microservice call chain without manual token management in business logic.
AddAppNetworkingSettings(Action<NetworkOptions> action)| Parameter | Type | Description |
| :— | :— | :— |
| action | Action<NetworkOptions> | Delegate to configure global defaults and service nodes. |
NetworkOptions (Configuration Schema)| Property | Type | Description |
| :— | :— | :— |
| Defaults | NetworkDefaults | Global fallback settings for all clients. |
| Services | Dictionary<string, NetworkNode> | Map of named service configurations. |
NodeSettings (Common Properties for Defaults and Nodes)| Property | Type | Description |
| :— | :— | :— |
| BaseUrl | string | (Node Only) The base URI for the remote service. |
| RetryCount | int | Number of retry attempts (Default: 3). |
| RetryDelayInMilliseconds | int | Base delay between retries (Default: 100ms). |
| TimeoutInMilliseconds | int | Global timeout for the request (Default: 5000ms). |
| RetryStrategy | RetryStrategy | Fixed or Exponential. |
| UserAgent | string | The string sent in the User-Agent header. |
builder.Services.AddAppNetworkingSettings(options =>
{
// 1. Global Resilience Defaults
options.Defaults = new NetworkDefaults
{
RetryCount = 2,
RetryStrategy = RetryStrategy.Exponential,
TimeoutInMilliseconds = 3000,
UserAgent = "TaskHub.Gateway/v2"
};
// 2. High-Priority Internal Service (Strict)
options.Services.Add("IdentityService", new NetworkNode
{
BaseUrl = "https://identity-internal:5001",
RetryCount = 1, // Fail fast
TimeoutInMilliseconds = 1000
});
// 3. Unreliable Third-Party API (Relaxed)
options.Services.Add("LegacyLegacyAPI", new NetworkNode
{
BaseUrl = "https://legacy-vendor.com/api",
RetryCount = 5,
RetryDelayInMilliseconds = 500,
RetryStrategy = RetryStrategy.Fixed,
TimeoutInMilliseconds = 20000
});
});
The networking module automatically registers all IClient implementations.
// Define your client contract
public interface IPaymentClient : IClient;
// Implement using the named HttpClient
public class StripeClient(IHttpClientFactory factory) : IPaymentClient
{
private readonly HttpClient _http = factory.CreateClient("StripeNode");
public async Task<Result> ChargeAsync(decimal amount, CancellationToken ct)
{
var res = await _http.PostAsJsonAsync("/v1/charges", new { amount }, ct);
return res.IsSuccessStatusCode ? ResultFactory.OnSuccess() : ResultFactory.OnFailed();
}
}
appsettings.json)"Networking": {
"Defaults": {
"RetryCount": 3,
"RetryDelayInMilliseconds": 200,
"RetryStrategy": "Exponential",
"TimeoutInMilliseconds": 10000,
"UserAgent": "TaskHub-Microservice"
},
"Services": {
"OrderService": {
"BaseUrl": "http://orders:8080"
},
"EmailProvider": {
"BaseUrl": "https://api.sendgrid.com",
"RetryCount": 10,
"RetryStrategy": "Fixed",
"TimeoutInMilliseconds": 30000
}
}
}
All outgoing requests are automatically instrumented via HttpClientInstrumentation:
HTTP {METHOD} (e.g., HTTP POST).http.url, http.status_code, http.host.The module attaches resilience metadata to the current Activity:
http.retry_count: The current attempt number if a retry occurred.http.resilience_strategy: e.g., ExponentialRetry.http_client_requests_duration_seconds: Histogram of latency per named client.http_client_retries_total: Counter for total retry attempts triggered by Polly.Exponential for inter-service communication to prevent “Retrying into a black hole.”BearerTokenHandler instead of manually copying headers in your service code.RetryCount to an extremely high number without a significant delay; it can cause a “Retry Storm.”HttpClient configuration; always use the Services dictionary in appsettings.json.CancellationToken to HttpClient methods to ensure that tracing context and timeouts are respected correctly.