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(); } } }