Search code examples
javascriptangularjsangular-http-interceptors

Angularjs $http interceptors - Cannot generate call to requestError


I'm using $http interceptors to capture all events following an ajax submission. For some reason, I am not able to throw a requestError. I've set up a test app to try and call requestError, but so far I can only get multiple responseErrors.

From angularjs docs:

requestError: interceptor gets called when a previous interceptor threw an error or resolved with a rejection.

This is my test code.

        .factory('httpInterceptor',['$q',function(q){

            var interceptor = {};

            var uniqueId = function uniqueId() {
                return new Date().getTime().toString(16) + '.' + (Math.round(Math.random() * 100000)).toString(16);
            };

            interceptor.request = function(config){
                config.id = uniqueId();
                console.log('request ',config.id,config);
                return config;
            };

            interceptor.response = function(response){
                console.log('response',response);
                return response;                    
            };

            interceptor.requestError = function(config){
                console.log('requestError ',config.id,config);
                return q.reject(config);
            };

            interceptor.responseError = function(response){
                console.log('responseError ',response.config.id,response);
                return q.reject(response);                    
            };

            return interceptor;

        }])

        .config(['$httpProvider',function($httpProvider) {
            $httpProvider.interceptors.push('httpInterceptor');
        }])

        .controller('MainCtrl',['$http',function($http){

            var mainCtrl = this;

            mainCtrl.method = null;
            mainCtrl.url = null;

            var testHttp = function testHttp() {

                $http({method:mainCtrl.method,url:mainCtrl.url}).then(
                        (response)=>{console.log('ok',response);},
                        (response)=>{console.log('reject',response);}
                );
            };

            //api
            mainCtrl.testHttp = testHttp;

        }])

I've tried multiple ways of creating http errors, and every time only responseError gets called. Things I've tried:

  • Get server to return different types of error for every request, e.g. 400 and 500.
  • Get the server to sleep random times, to get some later requests to respond with an error before earlier requests. Same resource, same server response.
  • Generate 404 errors by requesting resources which don't exist.
  • Disconnecting from the internet (responseError -1).

SIMILAR QUESTIONS

1) This question seems to have the answer: When do functions request, requestError, response, responseError get invoked when intercepting HTTP request and response?

The key paragrapgh being:

A key point is that any of the above methods can return either an "normal" object/primitive or a promise that will resolve with an appropriate value. In the latter case, the next interceptor in the queue will wait until the returned promise is resolved or rejected.

but I think I'm doing what it stipulates, viz random sleep by the server but no luck. I am getting reponseErrors out of order from the request ie as soon as the server responds.

2) A similar question was asked about 1 year ago: Angular and Jasmine: How to test requestError / rejection in HTTP interceptor?

Unfortunately, it only provides an explanation for interceptors. It does not answer my question.

I have tested in Chrome and Firefox. I hope you understand, I've done my best to find a solution to this, but I haven't come across a solution as yet.


Solution

  • This happens because the request isn't rejected at any point. It is supposed to be used like that:

    app.factory('interceptor1', ['$q', function ($q) {
      return {
        request: function (config) {
          console.log('request', config);
          if (config.url === 'restricted')
            return $q.reject({ error: 'restricted', config: config });
        }
      };
    }]);
    
    app.factory('interceptor2', ['$q', function ($q) {
      return {
        requestError: function (rejection) {
          console.log('requestError', rejection);      
          if (rejection.error === 'restricted')
            return angular.extend(rejection.config, { url: 'allowed' });
    
          return $q.reject(rejection);
        }
      };
    }]);
    
    app.config(['$httpProvider',function($httpProvider) {
      $httpProvider.interceptors.push('interceptor1');
      $httpProvider.interceptors.push('interceptor2');
    }]);
    

    Notice that interceptors are supposed to work in stack (starting from transform* hooks in $http request), so the request can't be rejected and recovered within a single interceptor.