Angular Default Request Headers and Interceptors

When building web applications with AngularJS, making requests using the $http service is usually a core concern. The documentation of $http is quite extensive. In this post, we will take a look at setting defaults for requests and at registering request interceptors.

First, defaults. Defining a request default is fairly simple


.config(function($httpProvider) {
    $httpProvider.defaults.headers.common['X-Requested-With'] = true;
}

You can also do this directly on $http somewhere in your code, but if you have default headers which won’t change, you can just as well set it in your app’s configuration step.
With these defaults, you can do simple things, such as setting default headers for your requests e.g. an Authorization Header for basic HTTP Auth:


// only use it for get requests
$http.defaults.headers.get.Authorization = 'Basic 1233225235'

You can also do more advanced things, such as transforming requests and responses by default:


$httpProvider.defaults.transformResponse = [...array of transformation functions...]

The transformReponse and transformRequest defaults take an array of transformation functions which represent a transformation chain.
This can be very useful if you have to transform the data you get from a server for every request and want to save some boilerplate code.

So basically you can save lots of code and reduce complexity by using $http’s defaults.

However, you can’t be very specific with these defaults, if they are set, they are used for all requests. Sometimes you might want to do certain transformations or apply certain headers just for a few select requests. Of course you can just deactivate the defaults and reactivate them again after the request, but this basically defeats the purpose of having them in the first place.

The solution to this are $http’s interceptors. Interceptors can be registered with four different modes:
* request – intercept outgoing request
* requestError – intercept outgoing request error
* response – intercept incoming response
* responseError – incoming incoming error response

The following is a simple example of an interceptor which, if it encounters a status code 403 (permission denied), will redirect the user to the login page.


angular.module('InterceptorApp',[])
  .factory('httpErrorInterceptor', function($q, $location) {
    return {
      'responseError': function(rejection) {
        // permission denied, better login!
        if(rejection.status === 403) {
          $location.url('app/login');
        }
        $q.reject(rejection);
      }
    };
  })
  .config(function($httpProvider) {
    $httpProvider.interceptors.push('httpErrorInterceptor');
});

The next example shows an interceptor which, when a url with ‘product’ in it is called, adds an Authorization Header.


angular.module('InterceptorApp',[])
  .factory('httpProductRequestTransformationInterceptor', function($q, $location) {
    return {
      'request': function(config) {
          if(config.url.indexOf('product')) {
              config.headers.Authorization = 'BASIC 12345';
          }
          return config;
      }
    };
  })
  .config(function($httpProvider) {
    $httpProvider.interceptors.push('httpProductReponseTransformationInterceptor');
});

There are countless examples of using interceptors to implicitly add or remove configuration for certain requests as well as transform responses.

Another thing that’s important to note is, that interceptors can also be pushed implicitly like in the following example, where, when we receive a 404 error, we broadcast a ‘page-not-found’ event, which we can then catch somewhere else for error handling:


angular.module('InterceptorApp',[])
  .factory('httpProductRequestTransformationInterceptor', function($q, $location) {
    return {
      'request': function(config) {
          if(config.url.indexOf('product')) {
              config.headers.Authorization = 'BASIC 12345';
          }
          return config;
      }
    };
  })
  .config(function($httpProvider) {
    $httpProvider.interceptors.push(function($q, $rootScope) {
        'responseError': function(rejection) {
            if(rejection.status === 404) {
                $rootScope.$broadcast('page-not-found');
            }
            $q.reject(rejection);
        }
    });
});

That’s it. Have fun playing around with these awesome AngularJS features :)

~m