Search code examples
angularjsdeferredangularjs-service

AngularJS Handle different error codes in different places


I have a service that contains a generic call to $http that all ajax calls uses. Here I have "centralized" error handling where the status codes results in redirects.

"FieldsSync" service:

return $http({
    method: 'POST',
    url: url,
    data: $.extend(defaultPostData, postData)
}).error(function(data, status) {
    switch (status) {
        case 401: // "Unauthorized". Not logged in. 
            redirect.toLoginPage();
            break;
        case 403: // "Forbidden". Role has changed.
            redirect.toLogoutPage();
            break;
    }
});

When calling one of the service functions from the controller I always return the deferred object to be able to hook up more error callbacks to be able to handle errors that should result in some kind of feedback for the user.

Controller:

fieldsSync.createField(newField).success(function(data) {
    ...
}).error(function(data, status) {
    switch (status) { // <--- DO NOT WANT
        case 401:
        case 403:
            return; // These errors are handled (=redirects) in the generic error callback and we don't want to show do anything while it redirects.
    }
    ... // "Individual" error handling. =Displaying messages and stuff
});

But because I don't want error messages popping up before the redirect occurs I have to exit the error callback if the status codes already have been handled.

The question is: How do i get rid of the switch cases in my controllers? Is it possible to exit the chain of error callbacks when a specific error code has been handled? Or is it possible to work around this in a less ugly way? :)

This is a reoccurring problem for me, and my mind seems stuck.

I have checked the docs and cant find any pretty solution for this in either $http or $q.


Solution

  • I propose you to use a responseInterceptor to handle those cases:

    http://docs.angularjs.org/api/ng.$http

    I wrote the following, when I had the same "problem" as you. Its only a first draft, but you can improve it to your needs

    InternalServerErrorResponseInterceptor = function($q, InternalServerErrorService) {
        return function(promise) {
            return promise.then(function(response) {
                return response;
            }, function(response) {
                if (response.status > 499) {
                    InternalServerErrorService.handleError();
                    return $q.reject(response);
                }
                return $q.reject(response);
            });
        };
    };
    module.factory('InternalServerErrorResponseInterceptor', ['$q', 'InternalServerErrorService', InternalServerErrorResponseInterceptor]);
    module.config(['$httpProvider', function($httpProvider) {
            $httpProvider.responseInterceptors.push('InternalServerErrorResponseInterceptor');
        }]);
    

    Now your specific InternalServerErrorService can handle it the way you want it to be handled :)

    regards