10

I have the following code in one of my Controllers to handle a 401 gracefully:

ChannelsService.query(function(response) {
    $scope.channels = response;
}, function(error) {
    if (error.status == 401) {
        $state.go('login');
    }

});

and my corresponding service:

myServices.factory('ChannelsService', function ($resource) {
    return $resource('/channels', {}, {
        query: { method: 'GET', isArray: true },
        create: { method: 'POST' }
    })
});

I would like to know how to handle 401's globally so that I don't have to work this logic into every controller. Is it an interceptor that I need and if so could someone share some code?

Thanks

tommyd456
  • 10,443
  • 26
  • 89
  • 163

1 Answers1

10

For purposes of global error handling, authentication, or any kind of synchronous or asynchronous pre-processing of request or postprocessing of responses, it is desirable to be able to intercept requests before they are handed to the server and responses before they are handed over to the application code that initiated these requests. The interceptors leverage the promise APIs to fulfill this need for both synchronous and asynchronous pre-processing.

You can add an interceptor to the $httpProvider when configuring your application

app.config(['$httpProvider', function($httpProvider) {

    $httpProvider.interceptors.push(function($q) {

        return {

            'responseError': function(rejection){

                var defer = $q.defer();

                if(rejection.status == 401){
                    console.dir(rejection);
                }

                defer.reject(rejection);

                return defer.promise;

            }
        };
    });

}]);

As the name already suggests, this will intercept each request and call the provided function if there is a responseError (You could add interceptors for succeeded requests, too)

For further information, see the $http docs

Wottensprels
  • 3,307
  • 2
  • 29
  • 38
  • Thanks - so i've added this block in but in my controller I had access to `state` so that I could redirect using `$state.go()` - when I try to use this in the config I get errors – tommyd456 May 18 '14 at 08:48
  • `Unknown provider: $state` – tommyd456 May 18 '14 at 08:51
  • should I be redirecting from the interceptor? – tommyd456 May 18 '14 at 08:51
  • Well, that provider is not mentioned in the block above, so this error must origin from somewhere else in your application. About the redirecting, it depends on what you are trying to do. As `401` means unauthorized, I'd shoot for a redirection to the login page, yes. – Wottensprels May 18 '14 at 08:54
  • I include a reference to `$state` in my controller but this same approach doesn't work in the interceptor - how would you redirect from the interceptor? – tommyd456 May 18 '14 at 08:57
  • Yes - but when I include `$location` I get exactly the same error as when I try to include `$state` - anyway I will tick your answer because it answered my original question – tommyd456 May 18 '14 at 09:08
  • This did the trick: http://blog.thesparktree.com/post/75952317665/angularjs-interceptors-globally-handle-401-and-other – tommyd456 May 18 '14 at 09:15
  • I'm getting an "unknown provider" error when including $state. ui.router is included in the list of app modules and is being used in other places, why would it not be found when adding this? – SomethingOn Oct 12 '15 at 16:02
  • @SomethingOn I suggest posting a new question with details – Wottensprels Oct 12 '15 at 19:48