Search code examples
angularjsangular-http-interceptors

Retry failed requests with $http interceptor


The API my webapp is talking to sometimes overloads and is sending 500 Internal Server Error if it cannot handle request.

There are 100+ different requests my web application can send, so if I implement retry on each individually, it will cost me hours of typing.

I'm already using $httpProvider interceptor, here it is (simplified)

$httpProvider.interceptors.push(function ($q) {
    return {
        responseError: function (response) {
            switch (response.status) {
                case 401 :
                    window.location = "/";
                    alert('Session has expired. Redirecting to login page');
                    break;
                case 500 :
                    // TODO: retry the request
                    break;
            }
            return $q.reject(response);
        }
    };
});

How could I resend a request after getting 500 response code from the server?


Solution

  • Angular provides reference to the config object which was used by $http service for doing the request in the response (response.config). That means if we can inject $http service in the interceptor we can easily resend the request. Simple injecting of $http service in the interceptor is not possible because of the circular dependency but luckily there is a workaround for that.

    This is an example how the implementation of a such interceptor can be done.

    $httpProvider.interceptors.push(function ($q, $injector) {
        var incrementalTimeout = 1000;
    
        function retryRequest (httpConfig) {
            var $timeout = $injector.get('$timeout');
            var thisTimeout = incrementalTimeout;
            incrementalTimeout *= 2;
            return $timeout(function() {
                var $http = $injector.get('$http');
                return $http(httpConfig);
            }, thisTimeout);
        };
    
        return {
            responseError: function (response) {
                if (response.status === 500) {
                    if (incrementalTimeout < 5000) {
                        return retryRequest(response.config);
                    }
                    else {
                        alert('The remote server seems to be busy at the moment. Please try again in 5 minutes');
                    }
                }
                else {
                    incrementalTimeout = 1000;
                }
                return $q.reject(response);
            }
        };
    });
    

    Note: In this example implementation the interceptor will retry the request until you receive a response with status that is different than 500. Improvement to this can be adding some timeout before retrying and retrying only once.