角度拦截器如何工作?

时间:2016-08-20 13:50:09

标签: javascript angularjs

我今天正在学习角度拦截器。我做了一些样本以更好地理解这个概念。这是一个小样本。



var app = angular.module("myApp", []);

app.factory("timestampMaker", [
  function() {
    var timestampMaker = {
      request: function(config) {
        console.log(config);
        config.requestTimestamp = new Date().getTime();
        return config;
      },
      response: function(response) {

        response.config.responseTimestamp = new Date().getTime();
        return response;
      }
    };
    return timestampMaker;
  }
]);

app.config(['$httpProvider',
  function($httpProvider) {
    $httpProvider.interceptors.push('timestampMaker');
  }
]);

app.run(function($http) {
  $http.get('https://api.github.com/users/naorye/repos').then(function(response) {
    console.log(response);
    var time = response.config.responseTimestamp - response.config.requestTimestamp;
    console.log("The request took" + (time / 1000) + "seconds")
  });
});

<html ng-app="myApp">

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>

</html>
&#13;
&#13;
&#13;

当我在请求函数中执行console.log(config)时,这是控制台上的输出。 enter image description here

我没有得到responseTimestamp如何出现在请求的配置对象中,其中定义了内部响应函数

1 个答案:

答案 0 :(得分:0)

注意我使用了1.2.x的源代码,这是问题中添加的。

$httpProvider是一个为您提供$http服务的提供商。

作为提供者,在配置时公开一个公共数组 - 在那里你只需添加你希望在响应/请求中注入的服务名称中的所有字符串。

这就是你在这里做的事情

app.config(['$httpProvider',
  function($httpProvider) {
    $httpProvider.interceptors.push('timestampMaker');
  }
]);

并且只能在配置时完成,因为在应用程序启动之前可以配置每docs个提供程序

您可以在第159行的source code中看到公开的数组

var responseInterceptorFactories = this.responseInterceptors = [];

当您请求$http服务时,将其注入您的服务/控制器, $get函数已执行。在该函数中,您的拦截器数组将被迭代,您可以在第179行的source code中看到

   var reversedInterceptors = [];
   forEach(responseInterceptorFactories, function(interceptorFactory, index) {
      var responseFn = isString(interceptorFactory) ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory);

      /**
       * Response interceptors go before "around" interceptors (no real reason, just
       * had to pick one.) But they are already reversed, so we can't use unshift, hence
       * the splice.
       */
      reversedInterceptors.splice(index, 0, {
        response: function(response) {
          return responseFn($q.when(response));
        },
        responseError: function(response) {
          return responseFn($q.reject(response));
        }
      });
    });

按照惯例,他们会颠倒顺序。你可以看到使用字符串,他们使用$injector.get$injector.invoke获得对函数的引用,并使用$q.when()我们可以将它们引入promises链,即使它们是同步代码 - 如果您不确定我对$q.when()

的含义,请参阅此Can I use $q.all in AngularJS with a function that does not return a .promise?

到目前为止,我们有一个带有函数的数组,这些函数都是有希望的(感谢$q.when())。当您通过$http这样的

请求数据时
$http.get('https://api.github.com/users/naorye/repos').then(function(response) {
    console.log(response);
    var time = response.config.responseTimestamp - response.config.requestTimestamp;
    console.log("The request took" + (time / 1000) + "seconds")
  });

即使您拥有.get(),也只是here所有相同功能的快捷方式

在代码中,相关部分就是这个:

首先,使用两个值创建一个链数组:一个内部函数,它对我们的目的并不重要(但它返回一个promise - 这很重要)和未定义的值。

我们对带有拦截器的数组进行迭代,并在开始时(在请求之前)添加请求拦截器并在结束时添加响应。喜欢这个

   function serverRequest {
      // some code
    };
     var chain = [serverRequest, undefined];
    var promise = $q.when(config);

    // apply interceptors
    forEach(reversedInterceptors, function(interceptor) {
      if (interceptor.request || interceptor.requestError) {
        chain.unshift(interceptor.request, interceptor.requestError);
      }
      if (interceptor.response || interceptor.responseError) {
        chain.push(interceptor.response, interceptor.responseError);
      }
    });

然后,让链完成(记住我们的拦截器数组充满了承诺),代码迭代它,使用.then()添加所有这些,导致所有这些都在链中执行,遵循承诺链接(你可以google)

    while(chain.length) {
      var thenFn = chain.shift();
      var rejectFn = chain.shift();

      promise = promise.then(thenFn, rejectFn);
    }

最后,您在successerror中添加的回调会添加到链的最后,并且会返回承诺

 promise.success = function(fn) {
  promise.then(function(response) {
    fn(response.data, response.status, response.headers, config);
  });
  return promise;
};

promise.error = function(fn) {
  promise.then(null, function(response) {
    fn(response.data, response.status, response.headers, config);
  });
  return promise;
};

return promise;

我不确定的唯一部分是在链数组中使用undefined

<强>摘要

您添加服务的名称,$HttpProvider使用$invoke服务来获取它们,并使用$q.when()将它们添加到承诺链中,并返回承诺。最后,添加了针对特定$http请求的回调。