49

I have an application built using AngularJS and a server-side backend that delivers all requests in JSON form. Each and every request is wrapped in a JSON container that contains a data variable which contains the data specific to the request. The other data, which are used to keep state and control within the application, check for errors and success messages, and check for session flags. All of these other variables are served with EVERY request and are examined first before the data variable is.

Right now I have a method to examine the contents of the JSON response first and then the data itself.

$http.get('something.json').success(function(response) {
   var data = examineJSONResponse(response);
   //do the data stuff
});

This works and the examineJSONResponse takes a look at the code and if there is something wrong then it throws an exception and reloads the page using window.location.href.

Is there any way that I can automate this within AngularJS so that each time a $http call is made then it checks this and ONLY returns the data variable contents as the JSON response?

Răzvan Flavius Panda
  • 21,730
  • 17
  • 111
  • 169
matsko
  • 21,895
  • 21
  • 102
  • 144

2 Answers2

93

You can intercept responses by adding an interceptor to $httpProvider.interceptors with Angular 1.1.4+ (see documentation here search for interceptors).

For a specific content type like json you can potentially reject changes or throw an exception even if the call was a success. You can modify the response.data that will get passed to your controller code as well here:

myModule.factory('myHttpInterceptor', function ($q) {
    return {
        response: function (response) {
            // do something on success
            if(response.headers()['content-type'] === "application/json; charset=utf-8"){
                // Validate response, if not ok reject
                var data = examineJSONResponse(response); // assumes this function is available

                if(!data)
                    return $q.reject(response);
            }
            return response;
        },
        responseError: function (response) {
            // do something on error
            return $q.reject(response);
        }
    };
});
myModule.config(function ($httpProvider) {
    $httpProvider.interceptors.push('myHttpInterceptor');
});

NOTE: Here is the original answer for versions prior to 1.1.4 (responseInterceptors were deprecated with Angular 1.1.4):

Maybe there's a better way but I think you can do something similar to this post with the http response interceptor (described here) (for a specific content type like json) where you potentially reject changes or throw an exception even though the call was a success. You can modify the response.data that will get passed to your controller code as well here.

myModule.factory('myHttpInterceptor', function ($q) {
    return function (promise) {
        return promise.then(function (response) {
            // do something on success
            if(response.headers()['content-type'] === "application/json; charset=utf-8"){
                // Validate response if not ok reject
                var data = examineJSONResponse(response); // assumes this function is available

                if(!data)
                    return $q.reject(response);
            }
            return response;
        }, function (response) {
            // do something on error
            return $q.reject(response);
        });
    };
});
myModule.config(function ($httpProvider) {
    $httpProvider.responseInterceptors.push('myHttpInterceptor');
});
Community
  • 1
  • 1
Gloopy
  • 37,767
  • 15
  • 103
  • 71
  • How do I actually register this interceptor with angular? – Mike Chamberlain Mar 15 '13 at 01:49
  • 1
    @MikeyCee I modified the example to include registering the interceptor. Hope this helps! – Gloopy Mar 15 '13 at 03:29
  • 1
    How to use this in my $http.get request? – BrMe Jun 13 '13 at 10:24
  • 2
    Just a heads up: As of 1.1.4 Response Interceptors have been DEPRECATED. See [1.1.4 docs](http://code.angularjs.org/1.1.4/docs/api/ng.$http) for the replacement. – JackMorrissey Jun 17 '13 at 18:28
  • 1
    @SwampDiner thanks for the comment! I've updated the code snippet to use the new 1.1.4+ style. – Gloopy Jun 20 '13 at 05:52
  • This has changed in angular 1.8 any one know how to implement it there? – Pablo Jomer Nov 07 '13 at 09:23
  • please kindly add a deprecation annotation to the code concerning versions pre 1.1.4 (for people like me who skim through explanation and are code-eager just to discover 30 minutes later that they should've read the whole thing) – Kosmotaur Jun 27 '14 at 16:14
  • 2
    @Kosmotaur thanks for the suggestion I've moved the pre 1.1.4 version to the bottom of the answer. – Gloopy Jul 01 '14 at 21:25
7

Another solution is to create a service and use that around the $http variable.

angular.module('App', [])
  .factory('myHttp',['$http',function($http) {
    return function(url, success, failure) {
      $http.get(url).success(function(json) {
          var data = examineJSONResponse(json);
          data && data.success ? success() : failure();
        }).error(failure);
      );
    }
}]);

And now this can be called like:

myHttp(url, onSuccess, onFailure);
matsko
  • 21,895
  • 21
  • 102
  • 144
  • 2
    Your code doesn't run because the second parameter of the `service` method needs to be a constructor function, like explained in [this thread in the AngularJS mailing list](https://groups.google.com/forum/#!msg/angular/56sdORWEoqg/b8hdPskxZXsJ). I think you probably meant to type `factory` instead of `service` and that would work. – tamakisquare Apr 08 '13 at 18:18
  • You're right. It should be a `factory`. It's now updated. Thank you. – matsko Apr 08 '13 at 21:37