Closed
Description
I register HttpClient via IServiceCollection.AddHttpClient<T>()
. However, it seems there is no way to create an HttpClient for T. Because WebApplicationFactory has only CreateClient(); method.
public void ConfigureServices(IServiceCollection services)
{
...
services.SetWaitAndRetryPolicy<CustomHttpClient>();
}
public static class IServiceCollectionExtension
{
public static void SetWaitAndRetryPolicy<T>(this IServiceCollection services) where T : class
{
//https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests
//https://github.com/App-vNext/Polly/wiki/Polly-and-HttpClientFactory
Random random = new Random();
Config config = null;
services.AddHttpClient<T>((sp, client) =>
config = sp.GetService<IOptions<Config>>().Value)
.AddPolicyHandler((service, request) =>
//TransientErrors:
//Network failures(System.Net.Http.HttpRequestException)
//HTTP 5XX status codes(server errors)
//HTTP 408 status code(request timeout)
HttpPolicyExtensions.HandleTransientHttpError()
//Wait and retry with exponential backoff with Randomisation
.WaitAndRetryAsync(config.Retry.RetryCount,
retryCount => TimeSpan.FromSeconds(Math.Pow(2, retryCount)) + //2,4,8,...
TimeSpan.FromMilliseconds(random.Next(100, 990)), //random in millisecs
onRetry: (outcome, timespan, retryCount, context) =>
{
service.GetService<ILog>().Info("Delaying for {delay}ms, then making retry {retry}.",
timespan.TotalMilliseconds, retryCount);
}
)
)
.AddPolicyHandler((service, request) =>
HttpPolicyExtensions.HandleTransientHttpError()
//Further external requests are blocked for 30 seconds if five failed attempts occur sequentially.
//Circuit breaker policies are stateful.All calls through this client share the same circuit state.
.CircuitBreakerAsync(config.CircuitBreaker.HandledEventsAllowedBeforeBreaking,
TimeSpan.FromSeconds(config.CircuitBreaker.DurationOfBreakInSeconds),
(result, timeSpan, context)=>
service.GetService<ILog>().Info("CircuitBreaker onBreak for {delay}ms", timeSpan.TotalMilliseconds),
context =>
service.GetService<ILog>().Info("CircuitBreaker onReset")));
}
}
public class HttpClientService
{
private readonly HttpClient _client;
private readonly ILog _logger;
public HttpClientService(HttpClient client, ILog logger)
{
_client = client;
_logger = logger;
}
public async Task<HttpStatusCode> PostAsync(string url, Dictionary<string, string> headers, string body)
{
using (var content = new StringContent(body, Encoding.UTF8, "application/json"))
{
foreach (var keyValue in headers)
{
content.Headers.Add(keyValue.Key, keyValue.Value);
}
var response = await _client.PostAsync(url, content);
response.EnsureSuccessStatusCode();
return response.StatusCode;
}
}
}