Retrying with .net WebAPI client

We’ve been building a prototype Windows 8 dashboard to show management information on our Azure application.

One thing we were conscious of was unreliable mobile network connections so we wanted to make some attempt to retry if we get a failure.

We achieved this with a custom HttpMessageHandler.  We extend the DelegatingHandler so we can pass on to the real HttpClientHandler – this usefully also allows us to resend requests (if you try and send the same request twice you’ll get an exception when not using a delegating handler).  It’s pretty simple but seems to work ok (it doesn’t have any back off or handle specific types of errors – it is the simplest thing we can get away with right now).

    public class RetryingMessageHandler : DelegatingHandler
    {
        const int MaximumRetries = 10;

        public RetryingMessageHandler(HttpMessageHandler httpMessageSender, IEnumerable<IHttpResponseInspector> inspectors)
            : base(httpMessageSender)
        {
            Inspectors = inspectors;
        }

        public IEnumerable<IHttpResponseInspector> Inspectors { get; private set; }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            Exception lastExcpetion = null;
            int i;
            var maxRetries = MaximumRetries + 1;
            for (i = 0; i < maxRetries; i++)
            {
                var retryRequired = false;
                HttpResponseMessage result;
                try
                {
                    result = await base.SendAsync(request, cancellationToken);
                }
                catch (Exception ex)
                {
                    lastExcpetion = ex;
                    continue;
                }
                foreach (var inspector in Inspectors)
                {
                    var inspectionResult = await inspector.Inspect(result);
                    if (inspectionResult == InspectionResult.Retry)
                    {
                        retryRequired = true;
                        if (inspector.MaxRetries < maxRetries)
                        {
                            maxRetries = inspector.MaxRetries + 1;
                        }
                    }
                }
                if (!retryRequired)
                {
                    return result;
                }
            }

            throw new ServiceRetryLimitExceededException(string.Format("Retry count exceeded, attempted call {0} times", i), lastExcpetion);
        }
    }

You’ll see you can pass in your own ‘Response Inspectors’ which can vote as to whether they want to retry or abort the call.  We use this to handle seamlessly re-logging in a user when their cookie expires.  Something which would be good to do is have different max retries for different inspectors – but we haven’t needed it yet.

We wrapped this up in a simple factory for creating a HttpClient.

public class HttpClientFactory : IHttpClientFactory
{
    private readonly CookieContainer _cookieContainer = new CookieContainer();

    public HttpMessageHandler CreateHandler()
    {
        return CreateHandler(new IHttpResponseInspector[0]);
    }

    public HttpMessageHandler CreateHandler(IEnumerable<IHttpResponseInspector> responseInspectors)
    {
        return new RetryingMessageHandler(_CreateHttpClientHandler(), responseInspectors);
    }

    HttpMessageHandler _CreateHttpClientHandler()
    {
        return _ConfigureClientHandler(new HttpClientHandler());
    }

    private HttpClientHandler _ConfigureClientHandler(HttpClientHandler handler)
    {
        handler.CookieContainer = _cookieContainer;
        handler.AllowAutoRedirect = false;
        return handler;
    }
}

public static class HttpClientFactoryExtensions
{
    public static HttpClient Create(this IHttpClientFactory httpClientFactory)
    {
        return new HttpClient(httpClientFactory.CreateHandler());
    }
}

All in all it’s pretty simple to extend the WebAPI and the delegating handler is a simple AOPish method for adding generic behaviour to all service calls.

Advertisements

About Tom Peplow

C# .Net developer based in London and the South Coast
This entry was posted in Uncategorized and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s