问题
- 默认情况下,angular的$http仅仅缓存“get”和’jsonp’的请求数据.
angular $http缓存的源码分析
// line 11737
if ((config.cache || defaults.cache) && config.cache !== false &&
(config.method === 'GET' || config.method === 'JSONP')) {//判断是否要缓存
cache = isObject(config.cache) ? config.cache
: isObject(defaults.cache) ? defaults.cache
: defaultCache;
console.log('cache',cache);//选择缓存对象
}
if (cache) {
cachedResp = cache.get(url);//获取缓存
...
}
- 首先我们要通过 config.method和config.cache的校验
- 其次要一个唯一的url,可以利用angular自带的URL编码服务$httpParamSerializer,编码data对象来生成,其他的方法也可以,只要唯一就行
- 另外,如果只是单纯的改成get请求,发给后端的时候,就真是是get请求了。而我们发给后台的应该是post请求。所以第一次请求,必须原封不动的发给后台。这就导致了,我们只能自己创建一个容器来缓存数据。并且是cacheFactory创建的容器,这样http才能识别。
方法
- 将post请求伪装成get请求,让$http可识别
- 缓存POST数据并不难,主要是要让http读取post的缓存之后,正确的执行后来的success和error方法的回调.所以才需要伪装,才能通过http的缓存校验,让$http的代码能够执行下去.
$http.post(url,data).then(success,error);
思路
新建一个拦截器
var myapp=angular.module('myapp',[]); myapp.config(function ($httpProvider) { $httpProvider.interceptors.push(function ($q) { return { request:function (config) { return config }, response:function (response) { return response; } } }) });
创建一个缓存post数据的容器
var cache=angular.injector(['ng']).get('$injector').get('$cacheFactory')('$httpPost');
在request请求时,判断是否为POST请求,并且设置了cache:true,如果是,并且cache过数据,则伪装为’GET’方法,如果没有cache过则直接pass.
request:function (config) { if(config.cache&&config.method==='POST'){ //开始计数 var url=config.url+'?'+config.paramSerializer(config.data); console.log('url',url,cache.get(url)); if(cache.get(url)){ config.cache=cache; config.method='GET'; config.url=url; } } return config },
在response返回时,判断是否为POST请求,并且设置了cache:true,如果是则cache它
response:function (response) { var config=response.config; if(response.config.cache&&response.config.method==='POST'){ var url=config.url+'?'+config.paramSerializer(config.data); cache.put(url,response.data); console.log('save Cache',url,cache.get(url)); } return response; }
代码
- 完整demo(angular version 1.5.8)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="./angular.js"></script>
<div ng-app="myapp">
<div ng-controller="myctrl">
<span ng-bind="name"></span>
<span ng-bind="age"></span>
<button ng-click="load()">加载</button>
</div>
</div>
<script>
var myapp=angular.module('myapp',[]);
myapp.config(function ($httpProvider) {
$httpProvider.interceptors.push(function ($q) {
var cache=angular.injector(['ng']).get('$injector').get('$cacheFactory')('$httpPost');
console.info('cache',cache);
return {
request:function (config) {
if(config.cache&&config.method==='POST'){
//开始计数
var url=config.url+'?'+config.paramSerializer(config.data);
console.log('url',url,cache.get(url));
if(cache.get(url)){
config.cache=cache;
config.method='GET';
config.url=url;
}
}
return config
},
requestError:function (rejection) {
console.log('rejection',rejection);
return rejection;
},
response:function (response) {
var config=response.config;
if(response.config.cache&&response.config.method==='POST'){
var url=config.url+'?'+config.paramSerializer(config.data);
cache.put(url,response.data);
console.log('save Cache',url,cache.get(url));
}
return response;
}
}
})
});
myapp.controller('myctrl',function ($scope,$http) {
$scope.name='demo';
$scope.age=100;
$scope.load=function () {
$http({
url:'data.json',
method:'post',
data:{name:1},
cache:true}).success(function (data) {
console.log('ajax result',data);
$scope.name=data.name;
$scope.age=data.age;
}).error(function (error) {
console.error('error',error);
});
};
$scope.load();
})
</script>
</body>
</html>
- data.json
{
"name":"gyanxie",
"age":"万岁万万岁"
}