Code

XHR Interceptor in an AngularJS web app

, , 9 Comments

Some time ago I wrote about intercepting XHR responses in an AngularJS web app.
Several months have passed since that post… and as someone pointed out (here) the method I described has been deprecated.
During this time Angularjs 1.2 timely-delivery was delivered (I really admire the guy who choose names for AngularJS releases), and we now have a stable and better way to intercept XHR requests and responses in our web app.
We’ll start by looking at how to define a new interceptor, using this new best practice.

The following code must to be placed inside the config section of the web app (the only place of your app where the service providers are accessible).


$httpProvider.interceptors.push(['$q', function($q) {
	return {
		
		/* All the following methods are optional */

		request: function(config) {
			/*
			  Called before send a new XHR request.
			  This is a good place where manipulate the
			  request parameters.
			*/

			return config || $q.when(config);
		},

		requestError: function(rejection) {
			// Called when another request fails.

			// I am still searching a good use case for this.
			// If you are aware of it, please write a comment

			return $q.reject(rejection);
		},

		response: function(response) {
			// Called before a promise is resolved.

			return response || $q.when(response);
		},

		responseError: function(rejection) {
			/*
			  Called when another XHR request returns with
			  an error status code.
			*/

			return $q.reject(rejection);
		}

	}

}]);

Now that it’s clear how to create XHR interceptor, let’s take a look at why we should use XHR interceptor in a web application.

XHR request interceptor

It may surprise some readers, but by default in AngularJS the payload of a POST request is always in JSON format. This means that, for example, if PHP is your backend language, you won’t find any data in your $_POST array. Instead, you can use the PHP function file_get_contents to read the payload.


if(stripos($_SERVER["CONTENT_TYPE"], "application/json") === 0) {
	$data = json_decode(file_get_contents("php://input"), true);
}

All this pain can be avoided with the savvy use of request interceptors.
To achieve this, first change the default content-type header used by AngularJS (need more info about request header?).
This should do the trick:


$http.defaults.headers.post["Content-Type"] 
  = "application/x-www-form-urlencoded; charset=UTF-8;";
// You could place this everywhere; the only condition is 
// that the $http service is in that scope.

We can then use the request interceptor to encode the payload:


$httpProvider.interceptors.push(['$q', function($q) {

	return {
		request: function(config) {
			if (config.data && typeof config.data === 'object') {

				/*
				  Before the request starts, the interceptor serializes
				  the data object as a string.
				*/

				config.data = serialize(config.data);
				// Check https://gist.github.com/brunoscopelliti/7492579 for a possible
				// implementation of the serialize function.

			}
			return config || $q.when(config);
		}
	};

}]);

XHR response interceptor

In my experience response interceptors are particularly helpful to handle specific error case.
In my previous post about XHR interceptors, I wrote about how to intercept a 401 (Unauthorized) error response.
Let’s rewrite that example with this new approach.


$httpProvider.interceptors.push(['$q', function($q) {

	return {
		response: function(response) {
			// response.status === 200
			return response || $q.when(response);
		},
		responseError: function(rejection) {
			// Executed only when the XHR response has an error status code

			if (rejection.status == 401) {

				// The interceptor "blocks" the error;
				// and the success callback will be executed.

				rejection.data = { stauts: 401, description: 'unauthorized' }
				return rejection.data;
			}

			/*
			  $q.reject creates a promise that is resolved as
			  rejected with the specified reason. 
			  In this case the error callback will be executed.
			*/
			
			return $q.reject(rejection);
		}
	}

}]);

Bruno

I'm Bruno Scopelliti, web developer from Bologna, Italy. I use this space to share my experiences, experiments and thoughts. I'm also on Google+, Twitter, and LinkedIn.

More Posts

Standard
  • Mobile apps development-EIW

    At last i find my solution for angular js…
    Thanks!

  • Chandan Singh

    Hey Bruno,
    I want to check for status 0 (connection failure or timeout) and in this case resend the request. Is it even possible from interceptors?

    • http://brunoscopelliti.com/ Bruno Scopelliti

      Hi Chandan,
      I never tried… but i suppose it is possible.

  • Khaled Ali

    dear,

    im using the folowing

    —-

    function PhpCtrl($scope, $http, $templateCache) {
    var method = ‘POST';
    var url = ‘http://localhost/angular';
    $scope.codeStatus = “”;
    var FormData = {
    ‘name’ : ‘kaled’,
    ‘password’ : ‘1966’
    };
    $http({
    method: method,
    url: url,
    data: FormData,
    headers: {‘Content-Type': ‘application/x-www-form-urlencoded; charset=UTF-8;’},
    cache: $templateCache
    }).
    success(function(data) {
    $scope.posts = data;
    }).
    error(function(response) {
    $scope.codeStatus = response || “Request failed”;
    });
    return false;
    }

    i got some data when using the upper code,
    but when i use u’r code in my php file the page hanging & no responce

    i need to pass some parameter in POST to the server

    kindly help

    if(stripos($_SERVER[“CONTENT_TYPE”], “application/json”) === 0) {
    $data = json_decode(file_get_contents(“php://input”), true);
    }

    kd.alshaikh@gmail.com

  • seoazhar

    Wow ! All this good info is really worth saving ! thanks a lot! Web Development services in Hyderabad

  • Fernando Araújo

    This helped me a lot! I had an interceptor that messing up the response error. Thanks!

  • Fabrizio Moscon

    Very well written tutorial.

    In addition it might be helpful to notice that using $http.get( /*... */)
    .success()
    .error()
    will resolve the $http response object. If you want to resolve some mapped data from the HTTP response object in the controller.
    If you need to map the data contained as payload in the response use $http.get( /*... */).then() like so: http://www.jjask.com/20190/angularjs-$routeprovider-resolve-returns-response-instead

  • http://www.whitepaperit.com/ Renold Scott

    Thanks a lot for the great script code shared here.
    http://www.whitepaperit.com

  • Eva Smith

    Great article on developing xhr interceptor..
    Web Design And Development http://www.ati-erp.com