【问题标题】:Recursively intercepting $http递归拦截$http
【发布时间】:2016-03-25 14:20:12
【问题描述】:

为了更好地使用启用了 hateoas 的 rest api,我想到了拦截 http 调用并在返回它们之前向我的资源对象添加一些方法。

这个想法是,如果资源有一个链接数组,我将添加方法来简化进一步的 http 请求。

下面的代码被推送到 $httpProvider 的拦截器数组中,几乎可以满足我的要求。

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;
        }
    };
});

问题在于,如下所示,当我使用我添加的请求方法时,我的拦截器没有处理响应。从我的拦截器中完成的 $http.get、$http.delete 等不会被拦截(自然而然!)。

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

所以问题是。如何处理对 $http 的内部调用?

【问题讨论】:

  • 不太清楚问题是什么。拦截器将为所有请求运行
  • 只要你用$http发出http请求,你的拦截器就会被调用,你能补充更多细节/解释会发生什么吗?
  • 拦截器不适用于从拦截器内部完成的 $http 调用。在get、delete、post和put函数中执行的调用不会被拦截。

标签: angularjs restful-architecture


【解决方案1】:

首先 - 这不是你问题的答案 =)

但这是一种建议:伙计,你真的在​​尝试开发一辆自行车。

根据开发人员指南,您应该使用 $resource 而不是 $httpREST api 交互。尤其是 RESTful

$resource 服务将为您提供功能,让您完全按照自己的意愿进行操作 - 发出 User.get() 之类的请求,然后是 User.save()User.update() 等。

我的建议是为每个实体(如“用户”、“帐户”等)或每个工作流程(如“注册”)创建一个 factory

'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;
        }
      };

    })
;

并在控制器中使用它:

.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);
        });
      };

    });

所以,如果上面的例子有点......你知道......让我们说如果你不喜欢它))

看看$resourcedocumentation(和this

如果您不喜欢资源,可以使用第三方restanguar(或其他方式)。但是restangular要严格得多,所以只有在你确定你的后端开发人员不是白痴的情况下才选择它。

UPD1:关于拦截器(我将在这里放我的错误拦截工作代码,这段代码的想法是当任何请求返回403 error状态时注销用户):

您可以将所有工厂添加到全局$httpProvider(在模块配置部分)。

像这样:

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

这里是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);
        }
      };
    })
;

【讨论】:

  • 但是不是基于 $resource 和 restangular "path" 而不是基于 hatoas 吗?
  • @user672009 当然可以,但这有意义吗?无论如何,我已经更新了我的答案,也许它会对你有所帮助
  • 我理解你的代码,这是有道理的 :-) 但这几乎是我想要避免的。我不想为我的每个业务对象都有一个工厂,我希望我的资源通过它们的 hatoas 链接相关联。因此,如果用户有亲戚,我想通过 user.get('relatives') 等来检索他们。
  • @user672009 好的,如果你知道怎么做,请在这里分享你的结果
【解决方案2】:

使用$injector 获取$http 而不是通过angular. 访问它。

详细回答: 问题是你如何注入$http。似乎你得到了一些在你的应用程序之外的$http(不过我不能确切地说出为什么)。 var $http = angular.injector(['ng']).get('$http'); 没有做你想做的(或你认为的)。

您可以使用var $http = angular.element(document).injector().get('$http');(或者可能更好地使用$document),但我认为您应该正常使用$injector-service:您可以将$injector 添加到您的依赖项/注入语句和拦截器中使用它来检索您想要的服务,例如:var $http = $injector.get("$http");

您的拦截器将拦截它发出的$http-调用。

【讨论】:

    猜你喜欢
    • 2023-03-13
    • 2023-03-28
    • 1970-01-01
    • 2014-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-28
    • 2017-11-07
    相关资源
    最近更新 更多