详解Angular.js中$http拦截器的介绍及使用


Posted in Javascript onJuly 04, 2017

前言

$http service在Angular中用于简化与后台的交互过程,其本质上使用XMLHttpRequest或JSONP进行与后台的数据交互。在与后台的交互过程中,可能会对每条请求发送到Server之前进行预处理(如加入token),或者是在Server返回数据到达客户端还未被处理之前进行预处理(如将非JSON格式数据进行转换);当然还有可能对在请求和响应过程过发生的问题进行捕获处理。所有这些需求在开发中都非常常见,所以Angular为我们提供了$http拦截器,用来实现上述需求。

什么是拦截器

顾名思义,拦截器就是在目标达到目的地之前对其进行处理以便处理结果更加符合我们的预期。Angular的$http拦截器是通过$httpProvider.interceptors数组定义的一组拦截器,每个拦截器都是实现了某些特定方法的Factory:

详解Angular.js中$http拦截器的介绍及使用

实现拦截器

http拦截器一般通过定义factory的方式实现:

myApp.factory('MyInterceptor', function($q) {
 return {
 // 可选,拦截成功的请求
 request: function(config) {
  // 进行预处理
  // ...
  return config || $q.when(config);
 },

 // 可选,拦截失败的请求
 requestError: function(rejection) {
  // 对失败的请求进行处理
  // ...
  if (canRecover(rejection)) {
  return responseOrNewPromise
  }
  return $q.reject(rejection);
 },



 // 可选,拦截成功的响应
 response: function(response) {
  // 进行预处理
  // ....
  return response || $q.when(reponse);
 },

 // 可选,拦截失败的响应
 responseError: function(rejection) {
  // 对失败的响应进行处理
  // ...
  if (canRecover(rejection)) {
  return responseOrNewPromise
  }
  return $q.reject(rejection);
 }
 };
});

随后,我们需要将实现的拦截器加入到$httpProvider.interceptors数组中,此操作一般在config方法中进行:

myApp.config(function($httpProvider) {
 $httpProvider.interceptors.push(MyInterceptor);
});

当然,我们也可以通过匿名factroy的方式实现:

$httpProvider.interceptors.push(function($q) {
 return {
 request: function(config) {
  // bala
 },

 response: function(response) {
  // bala
 },

 // bala
 };
});

可以看到,每个拦截器都可以实现4个可选的处理函数,分别对应请求(成功/失败)和响应(成功/失败)的拦截:

1、request:此函数在$http向Server发送请求之前被调用,在此函数中可以对成功的http请求进行处理,其包含一个http config对象作为参数,这里对config对象具有完全的处理权限,甚至可以重新构造,然后直接返回此对象或返回包含此对象的promise即可。如果返回有误,会造成$http请求失败。如开发中经常需要在请求头中加入token以便验证身份,我们可以作如下处理:

request: function(config) {
 config.headers = config.headers || {};
 if ($window.sessionStorage.token) {
  config.headers['X-Access-Token'] = $window.sessionStorage.token;
 }
 return config || $q.when(config);
}

2、requestError:此方法会在前一个拦截器抛出异常或进行了reject操作时被调用,在这里可以进行恢复请求的操作,或者进行一些对于请求时发起动作的处理(如取消loading等);

3、response:此函数在$http从Server接收到响应时被调用,在此函数中可以对成功的http响应进行处理,这里具有对响应的完全处理权限,甚至可以重新构造,然后直接返回响应或返回包含响应的promise即可。如果返回有误,会造成$http接收响应失败;

4、responseError:此方法会在前一个拦截器抛出异常或进行了reject操作时被调用,在这里可以进行恢复响应的操作,进行一些针对错误的处理。

使用用例

为演示Angular $http拦截器的使用方法,下面通过几个常用的用例来说明:

利用request拦截器模拟实现Angular的XSRF(即CSRF)防御

CSRF,即“跨站请求伪造”,不过不知道为什么Angular将其称为XSRF。当处理与后台交互时,Angular的$http会尝试从客户端cookie中读取一个token,其默认的key为XSRF-TOKEN,并构造一个名为X-XSRF-TOKEN的http头部,与http请求一起发送到后台。Server端就可以根据此token识别出请求来源于同域,当然跨域的请求$http不会加入X-XSRF-TOKEN头部。那我们可以利用request拦截器通过如下方式在同域请求头部中加入此头部以达到模拟Angular的XSRF(即CSRF)防御机制的实现效果:

/**
* 正式开发中Angular会主动进行XSRF防御(只要cookie中存在key为`XSRF-TOKEN`的token),
* 一般不需要手动进行,除非cookie中不存在key为`XSRF-TOKEN`的token,这里只是模拟实现
*/
request: function(config) {
 if(config.url.indexOf('SAME_DOMAIN_API_URL') > -1) {
 config.headers['X-XSRF-TOKEN'] = $cookies.get('XSRF-TOKEN');
 }
 return config;
}

如果初始http请求头部类似于:

"headers": {
 "Accept": "application/json, text/plain, */*"
}

那么经过上述的拦截器后,其http请求头部就变成了:

"headers": {
 "Accept": "application/json, text/plain, */*",
 "X-XSRF-TOKEN": X-XSRF-TOKEN-VALUE
}

利用response拦截器模拟实现Angular JSON易损性(JSON vulnerability)防御

Angular在$http请求安全性方面不仅为我们设计了XSRF(CSRF)防御,而且针对请求JSON数据的Url可能通过类似于<script>标签加载的方式被恶意网站获取到我们的JSON数据的情况,设计了Angular JSON易损性(JSON vulnerability)防御,即Server端返回的JSON数据头部可以添加")]}',\n"字符串,得到包含此前缀的响应数据后,Angular会将此前缀删去,将响应还原成正式的JSON数据。此时我们就可以通过response拦截器模拟此过程:

response: function(response) {
 var data = examineJSONResponse(response); // 假设存在这样一个方法
 if(!data) {
  response = validateJSONResponse(response); // 假设存在这样一个方法
 }
 return response || $q.when(reponse);
}

利用request拦截器和response拦截器计算http请求耗时

这个需求可能在开发中并不常用,这里只是作为同时使用request拦截器和response拦截器的例子,我们可以在request拦截器和response拦截器中分别计时,然后求得其差值即可:

myApp.factory('timestampMarker', [function() {
 return {
  request: function(config) {
   config.requestTimestamp = new Date().getTime();
   return config;
  },
  response: function(response) {
   response.config.responseTimestamp = new Date().getTime();
   return response;
  }
 };
}]);
myApp.config(['$httpProvider', function($httpProvider) {
 $httpProvider.interceptors.push('timestampMarker');
}]);

这样我们在每次请求后台时,就能够计算出相应请求的耗时了,如:

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

总结

$http作为Angular中的核心service,其功能非常强大便捷,今天描述了其子功能http拦截器的概念和描述方式,有理解不正确的地方,请大家留言告知。

Javascript 相关文章推荐
基于jQuery的树控件实现代码(asp.net+json)
Jul 11 Javascript
基于jQuery试卷自动排版系统
Jul 18 Javascript
Extjs407 getValue()和getRawValue()区别介绍
May 21 Javascript
js实现类bootstrap模态框动画
Feb 07 Javascript
JavaScript制作简单的框选图表
May 15 Javascript
ionic3实战教程之随机布局瀑布流的实现方法
Dec 28 Javascript
vue.js 底部导航栏 一级路由显示 子路由不显示的解决方法
Mar 09 Javascript
使用vue-route 的 beforeEach 实现导航守卫(路由跳转前验证登录)功能
Mar 22 Javascript
小程序实现展开/收起的效果示例
Sep 22 Javascript
vue实现的网易云音乐在线播放和下载功能案例
Feb 18 Javascript
Openlayers+EasyUI Tree动态实现图层控制
Sep 28 Javascript
Vue实现简单购物车功能
Dec 13 Vue.js
详解vue-cli 脚手架项目-package.json
Jul 04 #Javascript
Angular 2父子组件数据传递之@ViewChild获取子组件详解
Jul 04 #Javascript
详解node如何让一个端口同时支持https与http
Jul 04 #Javascript
Angular 2父子组件之间共享服务通信的实现
Jul 04 #Javascript
jQuery实现动态给table赋值的方法示例
Jul 04 #jQuery
Angular 2父子组件数据传递之局部变量获取子组件其他成员
Jul 04 #Javascript
Angular 4.X开发实践中的踩坑小结
Jul 04 #Javascript
You might like
PHP中限制IP段访问、禁止IP提交表单的代码
2011/04/23 PHP
php获取301跳转URL简单实例
2013/12/16 PHP
回帖脱衣服的图片实现代码
2014/02/15 PHP
php文件上传原理与实现方法详解
2019/12/20 PHP
比较简单实用的使用正则三种版本的js去空格处理方法
2007/11/18 Javascript
3款实用的在线JS代码工具(国外)
2012/03/15 Javascript
兼容主流浏览器的JS复制内容到剪贴板
2014/12/12 Javascript
node.js中的buffer.Buffer.isEncoding方法使用说明
2014/12/14 Javascript
使用CoffeeScrip优美方式编写javascript代码
2015/10/28 Javascript
详解vue父子模版嵌套案例
2017/03/04 Javascript
将Sublime Text 3 添加到右键中的简单方法
2017/12/12 Javascript
使用layui 渲染table数据表格的实例代码
2018/08/19 Javascript
Vue函数式组件-你值得拥有
2019/05/09 Javascript
深入学习TypeScript 、React、 Redux和Ant-Design的最佳实践
2019/06/17 Javascript
thinkjs微信中控之微信鉴权登陆的实现代码
2019/08/08 Javascript
vue本地打开build后生成的dist文件夹index.html问题
2019/09/04 Javascript
Vue实现计算器计算效果
2020/08/17 Javascript
[01:20:30]OG vs LGD 2018国际邀请赛淘汰赛BO3 第四场 8.26
2018/08/30 DOTA
python中关于日期时间处理的问答集锦
2013/03/08 Python
Python 实现购物商城,含有用户入口和商家入口的示例
2017/09/15 Python
python3.6 实现AES加密的示例(pyCryptodome)
2018/01/10 Python
特征脸(Eigenface)理论基础之PCA主成分分析法
2018/03/13 Python
Python使用Selenium模块模拟浏览器抓取斗鱼直播间信息示例
2018/07/18 Python
浅析python参数的知识点
2018/12/10 Python
Python Numpy库安装与基本操作示例
2019/01/08 Python
centos7中安装python3.6.4的教程
2019/12/11 Python
详解CSS3中Media Queries的相关使用
2015/07/17 HTML / CSS
HTML5 Canvas API中drawImage()方法的使用实例
2016/03/25 HTML / CSS
5 个强大的HTML5 API 函数推荐
2014/11/19 HTML / CSS
学校运动会开幕演讲稿
2014/01/04 职场文书
自我介绍演讲稿
2014/01/15 职场文书
医务工作者先进事迹材料
2014/01/26 职场文书
运动会入场词50字
2014/02/20 职场文书
悬空寺导游词
2015/02/05 职场文书
酒店员工管理制度
2015/08/05 职场文书
导游词之峨眉山
2019/12/16 职场文书