Recursively intercepting $http

随声附和 提交于 2019-12-11 11:34:01

问题


In order to better consume a hateoas enabled rest api I got the idea to intercept http calls and add some methods to my resource objects before returning them.

The idea is that if the resource has an array of links I'll add methods to easy further http requests.

The below code is push to the interceptors array of a $httpProvider and does pretty much what I want.

define(['angular'], function (angular) {

    var $http = angular.injector(['ng']).get('$http');

    function flattenLinks(links) {
        for (var key in links) {
            var rel = links[key].rel;
            var href = links[key].href;
            links[rel] = href;
        }
    }

    return {
        'response': function responseHandler(response) {
            var data = response.data ? response.data : response;
            if(typeof data === 'string' || !data.hasOwnProperty('links')) {
                return response;
            }

            if(data.links instanceof Array) {
                flattenLinks(data.links);
            }

            if(data instanceof Array) {
                for(var key in data) {
                    responseHandler(data[key]);
                }
            }

            data.hasLink = function(link) {
                return link in data.links;
            };

            data.get = function(rel) {
                return $http.get(data.links[rel]);
            };

            data.delete = function(rel) {
                return $http.delete(data.links[rel]);
            };

            data.post = function(rel) {
                return $http.post(data.links[rel], data);
            };

            data.put = function(rel) {
                return $http.put(data.links[rel], data);
            };

            return response;
        }
    };
});

The problem is that when I use, as seen below, my added methods to do requests the response isn't handled by my interceptor. The $http.get, $http.delete, etc. done from within my interceptor isn't intercepted (naturally!).

    vm.someResourceObj.get('self').then(function(response) {
        console.log(response);
    });

So the question is. How do I get the internal calls to $http handled?


回答1:


First of all - this is not an answer to you question =)

But it's a kind of advice: man, you are really try to develop a bicycle.

According to developers guide you should use $resource instead of $http to interact with REST api. Especially with RESTful.

$resource service will provide you functionality to make exactly what you want - to make requests like User.get() and after this User.save(), User.update(), etc.

My suggest is to create a factory for each entity(like "user", "account", etc), or for each workflow(like "sign up").

'use strict';

angular.module('app.factories.user', ['ngResource'])

    .factory('UserFactory', function ($resource) {

      var _userApi = {
        user: $resource('/user', {}, {})
      };

      return {
        getCurrentUser: function () {
          return _userApi.user.get(function (data) {
            // here we got data.user;
          }, function (response) {
            console.error(response.status);
          }).$promise;
        }
      };

    })
;

And use it in a controller:

.controller('UserPageCtrl', function ($scope, UserFactory) {

      var currentUser;

      $scope.getCurrentUser = function () {
        return UserFactory.getCurrentUser().success(function (user) {
          //here we got user
          currentUser = user;

          //now we able do stuf like
          //currentUser.save()
          //etc.
        }).error(function (err) {
          console.error(err);
        });
      };

    });

So, don't mind if examples above little bit... you know... let's say if you don't like it ))

Just take a look at $resource documentation (and this)

If you don't like resource, you can use third-party restanguar (or perhaps something else). But restangular is much more strict, so choose it only if you know for sure that your backend developers aren't idiots.

UPD1: About interceptors (I'll put here my working code for error intercepting, the idea of this code is to logout user when any request return 403 error status):

You are able to add all facctory to global $httpProvider(at module config section).

Like this:

app.config(function  ($urlRouterProvider, $httpProvider) {
      //Process backend errors
      $httpProvider.interceptors.push('errorsFactory');
})

And here is errorsFactory:

'use strict';

angular.module('app.factories.errors', [])

    .factory('errorsFactory', function ($q) {

      var HTTP_STATUS = {
        FORBIDDEN: 403
      };

      return {
        response: function (response) { 
          if (response.status === HTTP_STATUS.FORBIDDEN) {
            //logout here
          }
          return response || $q.when(response);
        },
        responseError: function (rejection) { 
          if (rejection.status === HTTP_STATUS.FORBIDDEN) {
            //logout here
          }
          return $q.reject(rejection);
        }
      };
    })
;



回答2:


Use $injector to get $http instead of accessing it via angular..

Detailed Answer: The problem is how you inject $http. It seems that you get some $http that is outside of your app (I can't exactly say why, though). var $http = angular.injector(['ng']).get('$http'); doesn't do what you want (or what you think it does).

You could do var $http = angular.element(document).injector().get('$http'); (or probably better use $document there), but imo you should use the $injector-service normally: you can just add $injector to your dependencies/inject statement and inside your interceptor use that to retrieve the service you want like: var $http = $injector.get("$http");

With that your interceptor will intercept the $http-calls it makes.



来源:https://stackoverflow.com/questions/36221715/recursively-intercepting-http

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!