AngularJS HTTP request cache for IndexeDB and $cacheFactory
npm install ng-request-cacheAngularJS HTTP request cache for IndexeDB and $cacheFactory
angularJS 下的restFul数据请求缓存服务及服务器端缓存服务, 实现服务器-客户端的数据请求三级缓存服务架构.
> * 服务器端采用Memcache或Redis作为缓存服务器, 缓存数据库中的数据, 提供给客户端只读操作查询;
> * 客户端第一级本地缓存采用angularJs的$cacheFactory服务, 数据存放在内存中, 当第一次启动项目或$cacheFactory中没有指定数据时查询二级缓存
> * 客户端第二级本地缓存采用IndexedDB数据库, 进行永久数据缓存
> * 客户端对服务器数据库的写操作使用requestCacheFactory.request()方法, 服务器端处理完客户端的写操作请求后自动删除对应的服务器缓存,以待下一次客户端读操作请求时重建
> * 客户端对服务器数据库的读操作使用requestCacheFactory.readonly()方法
> > * 如果读操作请求操作前端已做缓存, 同时请求时间在最后缓存时间的configs.cache_timeout设置时间内, 则本次请求不向服务器发出请求, 返回的结果是前端已做的缓存.
> > * 如果读操作请求操作前端已做缓存, 但请求时间在最后缓存时间的configs.cache_timeout设置时间外, 则本次请求将向服务器发出缓存校验参数, 服务器验证校验参数一致, 服务器不做数据返回, 数据仍从前端缓存中获取, 以减少服务器的带宽压力和前端的流量压力; 如果服务器验证校验参数不一致, 则从服务器向前端返回最终数据.
> > * 如果读操作请求操作前端未做缓存, 则从服务器向前端返回最终数据.
AngularJs Router 中cache不能按需缓存和请求数据!AngularJs Router 中cache打开的情况下, 只有第一次访问该路由, 才向服务器发出数据请求, 后续对该路由的访问都是得到客户端已缓存的数据AngularJs Router 中cache关闭的情况下, 每次访问该路由都向服务器发出数据请求index.html入口页面引入的CSS文件html
`
* 需要在index.html入口页面引入的JS文件
`html
`
* 加载到你的模块, 并进行的配置.
`javascript
var app = angular.module('myApp', ['oc.lazyLoad', 'angular.filter', 'xc.indexedDB', 'ng-unit', 'ng-request-cache']);
app.constant('app_config', {
db_name: 'myIndexedDB',
swal: {
allowOutsideClick: false, // 如果设置为“true”,用户可以通过点击警告框以外的区域关闭警告框。
confirmButtonColor: "#DD6B55", // 该参数用来改变确认按钮的背景颜色(必须是一个HEX值)。
confirmButtonText: "确定", // 该参数用来改变确认按钮上的文字。如果设置为"true",那么确认按钮将自动将"Confirm"替换为"OK"。
type: 'info', // 窗口的类型。有4种类型的图标动画:"warning", "error", "success" 和 "info".可以将它放在"type"数组或通过第三个参数传递。
title: null, // 窗口的名称。可以通过对象的"title"属性或第一个参数进行传递。
text: null, // 窗口的描述。可以通过对象的"text"属性或第二个参数进行传递。
showCancelButton: false, // 如果设置为“true”,“cancel”按钮将显示,点击可以关闭警告框。
showConfirmButton: true, // 如果设置为“false”,“Confirm”按钮将不显示。
cancelButtonText: '取消', // 该参数用来改变取消按钮的文字。
closeOnConfirm: true, // 如果希望以后点击了确认按钮后模态窗口仍然保留就设置为"false"。该参数在其他SweetAlert触发确认按钮事件时十分有用。
timer: null // 警告框自动关闭的时间。单位是ms。
},
spin: {
type: 'spinner',
opts: {
lines: 13, // The number of lines to draw
length: 38, // The length of each line
width: 22, // The line thickness
radius: 49, // The radius of the inner circle
scale: 1, // Scales overall size of the spinner
corners: 1, // Corner roundness (0..1)
color: '#333333', // CSS color or array of colors
fadeColor: 'transparent', // CSS color or array of colors
opacity: 0.3, // Opacity of the lines
rotate: 0, // The rotation offset
direction: 1, // 1: clockwise, -1: counterclockwise
speed: 1, // Rounds per second
trail: 60, // Afterglow percentage
fps: 20, // Frames per second when using setTimeout() as a fallback in IE 9
zIndex: 2e9, // The z-index (defaults to 2000000000)
className: 'spinner', // The CSS class to assign to the spinner
top: '50%', // Top position relative to parent
left: '50%', // Left position relative to parent
shadow: 'none', // Box-shadow for the lines
position: 'absolute' // Element positioning
}
},
sendSmsWait: 60,
extended_timeout: 2000,
duration_timeout: 10000,
hint_type: 'toastr',
restful: {
url: 'http://www.yoursite.com/app.php?s=/Api/angular.html',
cache: false,
timeout: 1000 * 15,
},
cache_timeout: 5601000,
});
`ng-unit unitFactory for Ionic & Cordova 工具类自定义服务
* 需要在index.html入口页面引入的CSS文件
`html
`
* 需要在index.html入口页面引入的JS文件
`html
`
* 需要安装的 cordova 插件
`cmd
cordova plugin add cordova-plugin-x-toast
cordova plugin add cordova-plugin-http
`
* 加载到你的模块, 并进行的配置.
`javascript
var app = angular.module('myApp', ['ionic', 'ngCordova', 'oc.lazyLoad', 'angular.filter', 'xc.indexedDB', 'ng-unit', 'ng-request-cache']);
app.constant('app_config', {
db_name: 'myIndexedDB',
swal: {
allowOutsideClick: false, // 如果设置为“true”,用户可以通过点击警告框以外的区域关闭警告框。
confirmButtonColor: "#DD6B55", // 该参数用来改变确认按钮的背景颜色(必须是一个HEX值)。
confirmButtonText: "确定", // 该参数用来改变确认按钮上的文字。如果设置为"true",那么确认按钮将自动将"Confirm"替换为"OK"。
type: 'info', // 窗口的类型。有4种类型的图标动画:"warning", "error", "success" 和 "info".可以将它放在"type"数组或通过第三个参数传递。
title: null, // 窗口的名称。可以通过对象的"title"属性或第一个参数进行传递。
text: null, // 窗口的描述。可以通过对象的"text"属性或第二个参数进行传递。
showCancelButton: false, // 如果设置为“true”,“cancel”按钮将显示,点击可以关闭警告框。
showConfirmButton: true, // 如果设置为“false”,“Confirm”按钮将不显示。
cancelButtonText: '取消', // 该参数用来改变取消按钮的文字。
closeOnConfirm: true, // 如果希望以后点击了确认按钮后模态窗口仍然保留就设置为"false"。该参数在其他SweetAlert触发确认按钮事件时十分有用。
timer: null // 警告框自动关闭的时间。单位是ms。
},
spin: {
type: 'spinner',
opts: {
lines: 13, // The number of lines to draw
length: 38, // The length of each line
width: 22, // The line thickness
radius: 49, // The radius of the inner circle
scale: 1, // Scales overall size of the spinner
corners: 1, // Corner roundness (0..1)
color: '#333333', // CSS color or array of colors
fadeColor: 'transparent', // CSS color or array of colors
opacity: 0.3, // Opacity of the lines
rotate: 0, // The rotation offset
direction: 1, // 1: clockwise, -1: counterclockwise
speed: 1, // Rounds per second
trail: 60, // Afterglow percentage
fps: 20, // Frames per second when using setTimeout() as a fallback in IE 9
zIndex: 2e9, // The z-index (defaults to 2000000000)
className: 'spinner', // The CSS class to assign to the spinner
top: '50%', // Top position relative to parent
left: '50%', // Left position relative to parent
shadow: 'none', // Box-shadow for the lines
position: 'absolute' // Element positioning
}
},
sendSmsWait: 60,
extended_timeout: 2000,
duration_timeout: 10000,
hint_type: 'toastr',
restful: {
url: 'http://www.yoursite.com/app.php?s=/Api/angular.html',
cache: false,
timeout: 1000 * 15,
},
cache_timeout: 5601000,
});
`#### 依赖/加载与配置
1. PC WEB 项目和 Cordova APP 项目都必须引入的文件
* 下载本服务所使用的$indexedDB本地数据库服务封装, 并且在你的index.html中加载它.
# CMD 安装
bower install --save angular-indexed-db
# HTML 引入
* 下载本服务所使用的SweetAlert弹框服务封装, 并且在你的index.html中加载它.
# CMD 安装
npm install sweetalert
# HTML 引入
2. 针对不同的平台引入相应的Toast消息提醒模块
* PC WEB 项目安装并引入
# CMD 安装
npm install --save angularjs-toaster
# HTML 引入
* Cordova APP 项目安装 $cordovaToast 插件
# CMD 安装
cordova plugin add https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin.git
3. 安装(使用
GIT克隆 或者 使用NPM安装)
* 使用GIT克隆
`cmd
git clone https://github.com/GadflyBSD/ng-request-cache.git
`
* 使用NPM安装
`cmd
npm install ng-request-cache
`4. 在你的index.html 加载它.
// PC WEB 项目引入
// Cordova APP 项目引入
5. 加载到你的模块, 并进行配置.
var app = angular.module('myApp', ['xc.indexedDB', 'request-cache-indexeddb']);
app.constant('configs', {
db_name: 'myIndexedDB',
restful_url: 'http://www.yoursite.com/app.php?s=',
http_timeout: 15,
cache_timeout: 5*60,
});
app.run(function($window, configs){
var initialize_APP = $window.localStorage.getItem('initialize_APP');
if (!initialize_APP || angular.isUndefined(initialize_APP) || initialize_APP === null ){
$indexedDBProvider.connection(configs.db_name)
.upgradeDatabase(1.0.0, function(event, db, tx){
var memcacheStore = db.createObjectStore('memcache', {keyPath: 'key', autoIncrement: true});
memcacheStore.createIndex('md5', 'md5', {unique: false});
memcacheStore.createIndex('sha1', 'sha1', {unique: false});
var documentStore = db.createObjectStore('document', {keyPath: 'id'});
documentStore.createIndex('did', 'did', {unique: false});
documentStore.createIndex('title', 'title', {unique: false});
documentStore.createIndex('category_id', 'category_id', {unique: false});
documentStore.createIndex('category_name', 'category_name', {unique: false});
documentStore.createIndex('category_title', 'category_title', {unique: false});
documentStore.createIndex('url', 'url', {unique: false});
documentStore.createIndex('level', 'level', {unique: false});
documentStore.createIndex('status', 'status', {unique: false});
});
$window.localStorage.setItem('initialize_APP', true);
}
});
app.config(function($stateProvider, $urlRouterProvider, $httpProvider, $indexedDBProvider) {
$httpProvider.defaults.headers = {
post: {
'Content-Type': 'application/x-www-form-urlencoded',
'cache':false
},
get: {
'Content-Type': 'application/x-www-form-urlencoded'
},
put: {
'Content-Type': 'application/x-www-form-urlencoded'
},
delete: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
$httpProvider.defaults.cache = false;
$httpProvider.defaults.transformRequest = function(obj) {
var str = [];
for (var p in obj) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
return str.join("&");
};
$stateProvider.state('app.mTenderTasks',{
url:'/mTenderTasks',
cache:false, // 请取消路由中的缓存
views:{
'tab-my':{
templateUrl:'templates/tab-my/mTenderTasks.html',
controller: 'myTenderTasksCtrl'
}
}
});
});
#### 基本用法
##### 一. 前端数据请求
* 常规请求模式
app.controller('yourCtrl', function($scope, requestCacheFactory){
/**
* # 常规数据请求, 根据服务器返回值缓存数据或直接返回数据
* @param param Object 请求参数, 服务器端具体方法所接收的请求参数
* @param router Object 请求的服务器端路由参数,
对象成员action代表服务器控制器名,
对象成员model代表服务器模型名,
对象成员module代表服务器方法名,
对象成员key代表请求结果通过$cacheFactory进行缓存的KEY值(不设定则不做缓存),
默认"{action: 'Restful', module: 'getServiceData'}",
请求的服务器端路由参数如指定, action或model必须指定一个,
如果两个都指定, 服务器端调用model模型中的module方法返回数据.
* @param method String 请求方法: get(默认), post, put, delete
*/
requestCacheFactory.request(param, router, method).then(function(success){
Success Request do someing ...
}, function(error){
Error Request do someing ...
});
});
* 请求获取服务器已缓存的指定数据(校验缓存)
> * 如果请求操作前端已做缓存, 同时请求时间在最后缓存时间的
configs.cache_timeout设置时间内, 则本次请求不向服务器发出请求, 返回的结果是前端已做的缓存.
> * 如果请求操作前端已做缓存, 但请求时间在最后缓存时间的configs.cache_timeout设置时间外, 则本次请求将向服务器发出缓存校验参数, 服务器验证校验参数一致, 服务器不做数据返回, 数据仍从前端缓存中获取, 以减少服务器的带宽压力和前端的流量压力; 如果服务器验证校验参数不一致, 则从服务器向前端返回最终数据.
> * 如果请求操作前端未做缓存, 则从服务器向前端返回最终数据. app.controller('yourCtrl', function($scope, requestCacheFactory){
/**
* 请求获取服务器已缓存的指定数据(校验缓存)
* @param getItem String|Array // 向服务器缓存请求数据的KEY值
* @param merge: { Object // 合并请求数据的请求参数, 不设定则不向服务器合并请求数据
* action: String, // 对应服务器控制器名,非必须
* model: String, // 对应服务器模型名,非必须
* module: String // 对应服务器方法名,非必须
* key: String // 请求结果通过$cacheFactory进行缓存的KEY值(不设定则不做缓存), 非必须
* param: Object // 服务器端具体方法所接收的请求参数, 非必须
* },
*/
requestCacheFactory.readonly(getItem, merge).then(function(success){
Success Request do someing ...
}, function(error){
Error Request do someing ...
});
});
##### 二. 服务器端数据请求的接收与返回
> 本例中采用
Memcache作为服务器缓存服务, 也可以选择使用Redis作为缓存服务使用
* 服务器接收到前端客户端所传递的数据参数格式:
`json
{
"action": "服务器控制器名",
"model": "服务器模型名",
"module": "服务器方法名",
"data": {
"uid": "用户uid",
"uuid": "用户UUID",
......
},
"check": [
{"key": "需要验证的服务器缓存KEY1", "md5": "md5", "sha1": "sha1"},
{"key": "需要验证的服务器缓存KEY2", "md5": "md5", "sha1": "sha1"},
...
{"key": "需要验证的服务器缓存KEYn", "md5": "md5", "sha1": "sha1"}
],
"merge": {
"action": "合并请求服务器控制器名",
"model": "合并请求服务器模型名",
"module": "合并请求服务器方法名",
}
}
`* 服务器传递给客户端的数据格式:
> 1. 所有返回的需要缓存的数据(
localStorage, sessionStorage, indexeddb, cache)请使用key-value的对象形式返回({"key": "data"}), 如有多个类型的数据请以key-value的数组对象形式返回([{"key1": "data1"}, {"key1": "data1"}, ... {"keyN": "dataN"}]).
> 2. indexeddb.structure是客户端IndexdDB数据库对象的创建结构, 可以不指定, 不指定时将默认创建一个db.createObjectStore('key', {keyPath: 'id', autoIncrement: true})的对象存储空间
> 3. 服务器端返回数据的相关JSON格式约定:
`json
{
"type": "请求返回状态: Success/Error/Info",
"msg": "请求返回说明",
"localStorage": {
"key1": { // 此处key值为服务器端数据KEY
"key": "服务器端数据KEY",
"md5": "服务器端缓存数据的MD5校验值",
"sha1": "服务器端缓存数据的SHA1校验值",
"verify": "服务器对比客户端校验结果, 如果为true则不会有data返回, 说明客户端与服务器数据一致",
"data": "需要客户端进行localStorage方式缓存的数据, 对象或数据对象"
},
"key2": { // 此处key值为服务器端数据KEY
"key": "服务器端数据KEY",
"md5": "服务器端缓存数据的MD5校验值",
"sha1": "服务器端缓存数据的SHA1校验值",
"verify": "服务器对比客户端校验结果, 如果为true则不会有data返回, 说明客户端与服务器数据一致",
"data": "需要客户端进行localStorage方式缓存的数据, 对象或数据对象"
},
......
"keyN": { // 此处key值为服务器端数据KEY
"key": "服务器端数据KEY",
"md5": "服务器端缓存数据的MD5校验值",
"sha1": "服务器端缓存数据的SHA1校验值",
"verify": "服务器对比客户端校验结果, 如果为true则不会有data返回, 说明客户端与服务器数据一致",
"data": "需要客户端进行localStorage方式缓存的数据, 对象或数据对象"
}
},
"sessionStorage": "需要客户端进行sessionStorage方式缓存的数据, 对象或数据对象, 结构同localStorage",
"indexeddb": {
"key1": { // 此处key值为服务器端数据KEY
"key": "服务器端数据KEY",
"md5": "服务器端缓存数据的MD5校验值",
"sha1": "服务器端缓存数据的SHA1校验值",
"structure": {
"storeName": "对象空间名称",
"keyPath": "指定每条记录中的某个指定字段作为键值",
"autoIncrement": "是否自动生成的递增数字作为键值, 可以不指定, 如果与keyPath同时使用, 对象中有keyPath指定的属性则不生成新的键值,如果没有自动生成递增键值,填充keyPath指定属性",
"createIndex": [
{"name": "索引名称", "idx": "索引字段名", "unique": "是否唯一"}
......
]
},
"verify": "服务器对比客户端校验结果, 如果为true则不会有data返回, 说明客户端与服务器数据一致",
"data": "需要客户端进行indexedDB方式缓存的数据, 对象或数据对象"
},
"key2": { // 此处key值为服务器端数据KEY
"key": "服务器端数据KEY",
"md5": "服务器端缓存数据的MD5校验值",
"sha1": "服务器端缓存数据的SHA1校验值",
"structure": {
"storeName": "对象空间名称",
"keyPath": "指定每条记录中的某个指定字段作为键值",
"autoIncrement": "是否自动生成的递增数字作为键值, 可以不指定, 如果与keyPath同时使用, 对象中有keyPath指定的属性则不生成新的键值,如果没有自动生成递增键值,填充keyPath指定属性",
"createIndex": [
{"name": "索引名称", "idx": "索引字段名", "unique": "是否唯一"}
......
]
},
"verify": "服务器对比客户端校验结果, 如果为true则不会有data返回, 说明客户端与服务器数据一致",
"data": "需要客户端进行indexedDB方式缓存的数据, 对象或数据对象"
},
......
"keyN": { // 此处key值为服务器端数据KEY
"key": "服务器端数据KEY",
"md5": "服务器端缓存数据的MD5校验值",
"sha1": "服务器端缓存数据的SHA1校验值",
"structure": {
"storeName": "对象空间名称",
"keyPath": "指定每条记录中的某个指定字段作为键值",
"autoIncrement": "是否自动生成的递增数字作为键值, 可以不指定, 如果与keyPath同时使用, 对象中有keyPath指定的属性则不生成新的键值,如果没有自动生成递增键值,填充keyPath指定属性",
"createIndex": [
{"name": "索引名称", "idx": "索引字段名", "unique": "是否唯一"}
......
]
},
"verify": "服务器对比客户端校验结果, 如果为true则不会有data返回, 说明客户端与服务器数据一致",
"data": "需要客户端进行indexedDB方式缓存的数据, 对象或数据对象"
}
},
"cache": "需要客户端进行$cacheFactory方式缓存的数据, 对象或数据对象, 结构同localStorage",
"data": "服务器返回的一般数据",
"merge": "服务器返回合并请求数据"
}
``