Inspired from here: http://www.thomaslevesque.com/2016/12/08/fun-with-the-httpclient-pipeline/
Usage:
var cookieContainer = new System.Net.CookieContainer();
using (var httpClientHandler = new HttpClientHandler() { UseCookies = true, CookieContainer = cookieContainer })
using (var errorLoggingHandler = new Handlers.LoggingHandler<AirWatchLogDto>(_httpGetLogger, httpClientHandler))
//using (var retryHandler = new RetryHandler(errorLoggingHandler) { RetryCounterCount = retryCounterCount })
using (var httpClient = new HttpClients.AirWatchCustomHeadersHttpClient(httpClientHandler, _appSettingsService))
{
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(httpMethod, requestUri);
if (httpMethod == HttpMethod.Post && postObject != null)
{
string formattedJson = Newtonsoft.Json.JsonConvert.SerializeObject(postObject);
httpRequestMessage.Content = new StringContent(formattedJson, Encoding.UTF8, "application/json");
}
var httpReponseMessage = await httpClient.SendAsync(httpRequestMessage);
string content = await httpReponseMessage.Content.ReadAsStringAsync();
return httpReponseMessage;
}
Retry Handler class:
public class RetryHandler : DelegatingHandler
{
private int _retryCounterCount = 3;
public int RetryCounterCount { get { return _retryCounterCount; } set { _retryCounterCount = value; } }
public RetryHandler(HttpMessageHandler innerHandler) : base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
int counter = 0;
while (true)
{
try
{
counter++;
// base.SendAsync calls the inner handler
var response = await base.SendAsync(request, cancellationToken);
if (counter >= RetryCounterCount)
{
return response;
}
if (response.StatusCode == HttpStatusCode.ServiceUnavailable)
{
// 503 Service Unavailable
// Wait a bit and try again later
await Task.Delay(5000, cancellationToken);
continue;
}
if (response.StatusCode == (HttpStatusCode)429)
{
// 429 Too many requests
// Wait a bit and try again later
await Task.Delay(1000, cancellationToken);
continue;
}
// Not something we can retry, return the response as is
return response;
}
catch (Exception ex) when (IsNetworkError(ex))
{
if (counter >= RetryCounterCount)
{
throw;
}
// Network error
// Wait a bit and try again later
await Task.Delay(2000, cancellationToken);
continue;
}
}
}
private static bool IsNetworkError(Exception ex)
{
// Check if it's a network error
if (ex is SocketException)
return true;
if (ex.InnerException != null)
return IsNetworkError(ex.InnerException);
return false;
}
}
Logging Handler:
//http://www.thomaslevesque.com/2016/12/08/fun-with-the-httpclient-pipeline/
public class LoggingHandler<T> : DelegatingHandler
{
private readonly IHttpClientEventLogger<T> _logger;
public LoggingHandler(IHttpClientEventLogger<T> logger, HttpMessageHandler innerHandler) : base(innerHandler)
{
_logger = logger;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
_logger.MapRequest(request);
var response = await base.SendAsync(request, cancellationToken);
_logger.MapResponse(response);
return response;
}
catch (Exception ex)
{
_logger.MapResponseException(ex);
throw;
}
finally
{
_logger.LogEvent();
}
}
}
public class ErrorLoggingHandler<T> : DelegatingHandler
{
private readonly IHttpClientEventLogger<T> _logger;
public ErrorLoggingHandler(IHttpClientEventLogger<T> logger, HttpMessageHandler innerHandler) : base(innerHandler)
{
_logger = logger;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
bool hasError = false;
try
{
_logger.MapRequest(request);
var response = await base.SendAsync(request, cancellationToken);
return response;
}
catch (Exception ex)
{
hasError = true;
_logger.MapResponseException(ex);
throw;
}
finally
{
if(hasError)
_logger.LogEvent();
}
}
}