15

This may seem like a strange request. I was wondering if there is a way using a $http interceptor to catch the first URL that has a response status of 500, then stop all subsequent requests and processes and do something?

Zanon
  • 29,231
  • 20
  • 113
  • 126
Ben Kilah
  • 3,445
  • 9
  • 44
  • 56

3 Answers3

10

Thomas answer is correct, but this solution is currently deprecated. You should do something like this answer.

app.factory('errorInterceptor', function ($q) {

    var preventFurtherRequests = false;

    return {
        request: function (config) {

            if (preventFurtherRequests) {
                return;
            }

            return config || $q.when(config);
        },
        requestError: function(request){
            return $q.reject(request);
        },
        response: function (response) {
            return response || $q.when(response);
        },
        responseError: function (response) {
            if (response && response.status === 401) {
                // log
            }
            if (response && response.status === 500) {
                preventFurtherRequests = true;
            }

            return $q.reject(response);
        }
    };
});

app.config(function ($httpProvider) {
    $httpProvider.interceptors.push('errorInterceptor');    
});
Community
  • 1
  • 1
Zanon
  • 29,231
  • 20
  • 113
  • 126
9

You can catch all responses through $httpProvider.responseInterceptors.

To do this you have to create a factory like this:

app.factory('SecurityHttpInterceptor', function($q) {
        return function (promise) {
            return promise.then(function (response) {
                return response;
            },
            function (response) {
                if (response.status === 500) {
                    //DO WHAT YOU WANT
                }
                return $q.reject(response);
            });
        };
    });

In this factory you'll catch the response status if it's 500 do what you want. Then reject the response.

Now you have to put the factory in the responseInterceptors of $httProvider in your config module like that :

app.config(function ($routeProvider, $httpProvider) {
    $routeProvider
        .when('/', {
            templateUrl: 'views/home.html',
            controller: 'HomeCtrl'
        })
        .otherwise({
            templateUrl: 'views/404.html'
        });

    $httpProvider.responseInterceptors.push('SecurityHttpInterceptor');
})
Zanon
  • 29,231
  • 20
  • 113
  • 126
Thomas Pons
  • 7,709
  • 3
  • 37
  • 55
  • Sorry I should have said I knew how to make an interceptor, i was wondering if there was a way to actually "stop" the $http.pendingRequests queue the first time a status 500 was encountered. – Ben Kilah Oct 04 '13 at 01:01
  • I'm also looking for a solution to stopping all subsequent requests upon detecting a certain status code (403 in my case). I already have an interceptor set up, but don't know how to continue from there. Please share if you have a solution to this, thanks. – Mark Jun 12 '14 at 06:38
2

You could create a service and interceptor to deal with this,

.factory('ServerErrorService', function() {

        var _hasError = false

        return {
            get hasError() {
                return _hasError;
            },
            set hasError(value) {
                _hasError = value;
                // you could broadcast an event on $rootScope to notify another service that it has to deal with the error
            },
            clear: clear
        }

        function clear() {
            _hasError = false;
        }
})


.factory('ServerErrorInterceptor', function ($q, ServerErrorService) {

    var interceptor = {

        request: function(config) {

            if(ServerErrorService.hasError) {
                var q = $q.defer();
                q.reject('prevented request');
                return q.promise;
            }
            return config;
        },

        responseError: function(response) {

            if(response.status === 500) {
                ServerErrorService.hasError = true;
            }

            return $q.reject(response);
        }
    }

    return interceptor;
})

If you wanted to allow requests to be made again then you just need to call ServerErrorService.clear()

OR

You could use a modified version of this answer. Although i'm not sure how - if you wanted to - you'd cancel this action and allow subsequent requests.

https://stackoverflow.com/a/25475121/1798234

.factory('ServerErrorInterceptor', function ($q) {
    var canceller = $q.defer();
    var interceptor = {

        request: function(config) {
            config.timeout = canceller.promise;
            return config;
        },

        responseError: function(response) {

            if(response.status === 500) {
                canceller.resolve('server error');
            }

            return $q.reject(response);
        }
    }

    return interceptor;
})

One thing to remember for both of these solutions, if you have any other interceptors after this that have a responseErrormethod defined or any handlers set up with .catch to process the $http promise, they will receive a response object with {data:null, status:0}

Community
  • 1
  • 1
james
  • 4,150
  • 2
  • 30
  • 36