HttpClient with Error Logging Handler and Retry Handler

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

Leave a Reply

Your email address will not be published. Required fields are marked *