AngularJS Interceptors for logging service calls

I should start a series of angularJS posts called “stupid angularJS tricks”, because I’m always trying to figure out how to do this or that. Thank goodness for other peoples’ blogs!

In my project, I’m building out a suite of apps which (true to AngularJS form) get their data from services. It’s handy to log all service calls, to make sure caching is working, etc. Ultimately there will be quite a few service calls, so we may want to keep track of them, and it’s certainly handy for debugging.

Prior to my discovery of interceptors, I would write an entry similar to this just before every $http.get or $http.post call, similar to this insanely simplified example:

console.log('WS GET service ' + svcUrl);
$http.get(svcUrl).
  success(function (result, httpstatus, headers) {
  });

Although this does the job, it’s not foolproof – I might forget to include it into future code (resulting in an unlogged call), or worse, I may change my get to a post and the log message would be wrong.

Enter the interceptor.

This lovely mechanism allows us to “intercept” an http call and run our custom code at key points. In this case I want to log something prior to running any $http call. There are various other places I can plug in. It’s all here (search on “interceptor”).

Design your factory for injection

In this case, I’m just plugging into the “request” event, which will fire before the request actually occurs. We receive a configuration object which describes the request, and I fashioned a very crude filter. Why? As I discovered, all requests from angular to include views also come through here. I don’t want those logged – just my web service requests.

myapp.factory('serviceLogger', function () {
    return {
        request: function (config) {
            //weed out loading of views - we just want service requests.
            if (config.url.indexOf('html') == -1) {
                console.log("HTTP " + config.method + " request: " + config.url);
            }
            return config;
        }
    };
});

The interceptor above will do its work prior to the request being made.  There are 4 places where you can use this method to intercept an HTTP request (from the documentation):

  • request: (as above)  Interceptor gets called with http config object. The function is free to modify the config object or create a new one. The function needs to return the config object directly, or a promise containing the config or a new config object.
  • requestError: Interceptor gets called when a previous interceptor threw an error or resolved with a rejection.
  • response: interceptors get called with http response object. The function is free to modify the response object or create a new one. The function needs to return the response object directly, or as a promise containing the response or a new response object.
  • responseError: Interceptor gets called when a previous interceptor threw an error or resolved with a rejection.  Good place for global handling of exceptions thrown in web services!

If you implement requestError or responseError, be sure to return a $q rejection – otherwise the code in your controller will not recognize the error.

Add the functionality to the $httpProvider

Now you’ve got your interceptor – but you still need to make sure your app is using it!   Note that you can only do this in the config section – this is where the $httpProvider is available.

myapp.config(function ($routeProvider, $httpProvider) {
      //you probably have some sort of a routing table here.
      $routeProvider
      .when('/something', { templateUrl: 'views/something.html', controller: 'somethingCtl' })
      .when('/anotherthing', { templateUrl: 'views/anotherthing.html', controller: 'anotherthingCtl' })
      .otherwise({ redirectTo: '/something' });

      //here's where you add your interceptor
      $httpProvider.interceptors.push('serviceLogger');
  })

That’s it!

Be sure to check out all the other stuff that interceptors can do!

2 comments