Restangular – How to override Error Interceptors

All we need is an easy explanation of the problem, so here it is.

I am using AngularJS v1.2.16 with Restangular v1.4.0, and would like to know if it’s possible to override the ErrorInterceptor. If yes, how? if no, how can I work it around?

I’ve configured an error Interceptor like this:

RestangularProvider.setErrorInterceptor(
    function ( response ) {
        if ( response.status == 401 ) {
        dialogs.error("Unauthorized - Error 401", "You must be authenticated in order to access this content.")
            .result.then( function () {
                $location.path("/login");
            });
        }
        else {
            // Some other unknown Error.
            console.log( response );
            dialogs.error(response.statusText + " - Error " + response.status,
                "An unknown error has occurred.<br>Details: " + response.data);
        }
        // Stop the promise chain.
        return false;
    }
);

then, on another place, I make a POST call with an error handling.

function saveApple( apple ) {
    Restangular.all("apple/save").post( apple ).then(
        function ( response ) {
            console.log("Saved");
        },
        function ( response ) {
            // This is not being called.
            console.log("Error with status code", response.status);
        }
    );
}

I understand that, my “second” error handler is not being called because I’m returning false on the ErrorInterceptor.

But how can I work this around? I have lots of “REST operations” in my app, and only few of them, I want a customized behavior when something goes wrong.

So far, the only work-around I thought of, is to make the ErrorInterceptor return true, and for every other REST operations, I copy-paste the same error handler (a more general one). But this would be the last thing I would do.

Right now, is flowing like this:

  • ErrorInterceptor > End.

If possible, I want it to be like this: (Only for specific methods – not all).

  • ErrorInterceptor > Especific_ErrorHandler (if Exists) > End.

it can also be like this:

  • Especific_ErrorHandler (if Exists) > ErrorInterceptor > End.

Either way works for me.

References:

https://github.com/mgonto/restangular#seterrorinterceptor
https://github.com/mgonto/restangular#how-can-i-handle-errors

Thanks in Advance!

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Here is your code:

RestangularProvider.setErrorInterceptor(
    function ( response ) {
        if ( response.status == 401 ) {
        dialogs.error("Unauthorized - Error 401", "You must be authenticated in order to access this content.")
            .result.then( function () {
                $location.path("/login");
            });
        }
        else {
            // Some other unknown Error.
            console.log( response );
            dialogs.error(response.statusText + " - Error " + response.status,
                "An unknown error has occurred.<br>Details: " + response.data);
        }
        // Stop the promise chain.
        return false;
    }
);

Why are you using an interceptor?
– because you want dialogs across the board for displaying error messags.

Stopping promise chain, is it bad practice?

I don’t know. Many people use error callbacks; and one use-case for error callbacks is to do some cleanup. If you kill the promise chain, how do you ensure the cleanup is done? Stopping the promise chain means your error callbacks, your catch blocks, and your finally blocks won’t get called.

In the docs, the cleanup is done, you can see that deferred.reject is passed along.
https://github.com/mgonto/restangular#seterrorinterceptor
Maybe you misunderstood the example that was in the docs.

Possible solution #1

        // DON'T stop the promise chain.
        return true;

Possible solution #2

Don’t handle unknown errors in the error interceptor.

RestangularProvider.setErrorInterceptor(
    function ( response ) {
        if ( response.status == 401 ) {
            dialogs.error("Unauthorized - Error 401", "You must be authenticated in order to access this content.")
                .result.then( function () {
                   $location.path("/login");
                });
            // Stop the promise chain.
            // all unauthorized access are handled the same.
            return false;
        }
        // Some other unknown Error.
        console.log( response );
        dialogs.error(response.statusText + " - Error " + response.status,
            "An unknown error has occurred.<br>Details: " + response.data);
        }
        // DON'T stop promise chain since error is not handled
        return true;
    }
);

Possible solution #3

call reject when you stop the promise chain.

RestangularProvider.setErrorInterceptor(
    function ( response, deferred, responseHandler ) {
        if ( response.status == 401 ) {
            dialogs.error("Unauthorized - Error 401", "You must be authenticated in order to access this content.")
                .result.then( function () {
                   // continue promise chain in this callback.
                   deferred.reject("unauthorized");
                   $location.path("/login");
                });
            // Stop the promise chain here
            // all unauthorized access are handled the same.
            return false;
        }
        // Some other unknown Error.
        console.log( response );
        dialogs.error(response.statusText + " - Error " + response.status,
            "An unknown error has occurred.<br>Details: " + response.data);
        }
        // DON'T stop promise chain since error is not handled
        return true;
    }
);

Method 2

Yes, you can create custom httpErrorHandler.

  • First you need to create a .factory, let’s call it httpErrorHandler
.factory('httpErrorHandler', function ($rootScope) {
  return {
    'error': function (rejection) {
      if (rejection.status === 422) {
        $rootScope.$broadcast('422_error', rejection.data);
      } else if (rejection.status === 403) {
        $rootScope.$broadcast('403_error', rejection.data);
      } else {
        $rootScope.$broadcast('unknown', rejection.data);
      }
      return $q.reject(rejection);
    }
  };
}
  • Then register that factory to the $httpProvider interceptors
.config(function ($httpProvider) {
  $httpProvider.interceptors.push('httpErrorHandler');
});

Then just catch that $rootScope events anywhere in the application and handle them, or add some logic inside the interceptor.

I hope this will work as expected.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply