介绍:$http service
在Angular中用于简化与后台的交互过程,其本质上使用XMLHttpRequest或JSONP进行与后台的数据交互。在与后台的交互过程当中,可能会对每条请求发送到Server以前进行预处理(如加入token),或者是在Server返回数据到达客户端还未被处理以前进行预处理(如将非JSON格式数据进行转换);固然还有可能对在请求和响应过程过发生的问题进行捕获处理。全部这些需求在开发中都很是常见,因此Angular为咱们提供了$http拦截器,用来实现上述需求。javascript
Angular的$http拦截器是经过$httpProvider.interceptors
数组定义的一组拦截器,每一个拦截器都是实现了某些特定方法的Factory: html
实现:java
http拦截器通常经过定义factory的方式实现:git
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); } }; });angularjs
随后,咱们须要将实现的拦截器加入到$httpProvider.interceptors
数组中,此操做通常在config方法中进行:github
myApp.config(function($httpProvider) { $httpProvider.interceptors.push(MyInterceptor); });json
固然,咱们也能够经过匿名factroy的方式实现:api
$httpProvider.interceptors.push(function($q) {return { request: function(config) {// bala }, response: function(response) {// bala }, // bala }; });跨域
能够看到,每一个拦截器均可以实现4个可选的处理函数,分别对应请求(成功/失败)和响应(成功/失败)的拦截:数组
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);
}
requestError
:此方法会在前一个拦截器抛出异常或进行了reject操做时被调用,在这里能够进行恢复请求的操做,或者进行一些对于请求时发起动做的处理(如取消loading等);response
:此函数在$http从Server接收到响应时被调用,在此函数中能够对成功的http响应进行处理,这里具备对响应的彻底处理权限,甚至能够从新构造,而后直接返回响应或返回包含响应的promise便可。若是返回有误,会形成$http接收响应失败;responseError
:此方法会在前一个拦截器抛出异常或进行了reject操做时被调用,在这里能够进行恢复响应的操做,进行一些针对错误的处理
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 }
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拦截器的例子,咱们能够在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拦截器通常经过定义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个可选的处理函数,分别对应请求(成功/失败)和响应(成功/失败)的拦截:
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);
}
requestError
:此方法会在前一个拦截器抛出异常或进行了reject操做时被调用,在这里能够进行恢复请求的操做,或者进行一些对于请求时发起动做的处理(如取消loading等);response
:此函数在$http从Server接收到响应时被调用,在此函数中能够对成功的http响应进行处理,这里具备对响应的彻底处理权限,甚至能够从新构造,而后直接返回响应或返回包含响应的promise便可。若是返回有误,会形成$http接收响应失败;responseError
:此方法会在前一个拦截器抛出异常或进行了reject操做时被调用,在这里能够进行恢复响应的操做,进行一些针对错误的处理。为演示Angular $http拦截器的使用方法,下面经过几个经常使用的用例来讲明:
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 }
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拦截器的例子,咱们能够在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.');
});