Files
weRequest/build/weRequest.js

751 lines
61 KiB
JavaScript
Raw Normal View History

module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./src/weRequest.js");
/******/ })
/************************************************************************/
/******/ ({
/***/ "./src/lib/flow.js":
/*!*************************!*\
!*** ./src/lib/flow.js ***!
\*************************/
/*! no static exports found */
/***/ (function(module, exports) {
var store = {};
function emit (key){
var flow = getFlow(key);
console.log("waitingList Length: " + flow.waitingList.length);
var currentLength = flow.waitingList.length;
for (var i = 0; i < currentLength; i ++) {
var callback = flow.waitingList.shift();
typeof callback == "function" && callback();
}
}
function wait (key,callback){
var flow = getFlow(key);
flow.waitingList.push(callback)
}
function getFlow(key){
if(!store[key]){
store[key] = {
waitingList:[]
}
}
return store[key];
}
module.exports = {
wait: wait,
emit: emit
}
/***/ }),
/***/ "./src/loading.js":
/*!************************!*\
!*** ./src/loading.js ***!
\************************/
/*! no static exports found */
/***/ (function(module, exports) {
function show(txt) {
wx.showToast({
title: typeof txt === 'boolean' ? '加载中' : txt,
icon: 'loading',
mask: true,
duration: 60000
})
}
function hide() {
wx.hideToast();
}
module.exports = {
show: show,
hide: hide
}
/***/ }),
/***/ "./src/weRequest.js":
/*!**************************!*\
!*** ./src/weRequest.js ***!
\**************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
const loading = __webpack_require__(/*! ./loading */ "./src/loading.js");
const flow = __webpack_require__(/*! ./lib/flow */ "./src/lib/flow.js");
//params
var sessionName = "session";
var loginTrigger = function () {
return false
};
var codeToSession = {};
var successTrigger = function () {
return true
};
var urlPerfix = "";
var successData = function (res) {
return res
};
var errorTitle = "操作失败";
var errorContent = function (res) {
return res
};
var reLoginLimit = 3;
var errorCallback = null;
var reportCGI = false;
var mockJson = false;
var globalData = false;
2018-09-24 21:58:20 +08:00
// session在本地缓存的有效时间
var sessionExpireTime = null;
// session在本地缓存的key
var sessionExpireKey = "sessionExpireKey";
// session过期的时间点
var sessionExpire = Infinity;
//global data
var session = '';
var sessionIsFresh = false;
// 正在登录中,其他请求轮询稍后,避免重复调用登录接口
var logining = false;
// 正在查询session有效期中避免重复调用接口
var isCheckingSession = false;
function checkSession(callback, obj) {
if (isCheckingSession) {
flow.wait('checkSessionFinished', function () {
checkSession(callback, obj)
})
} else if (!sessionIsFresh && session) {
isCheckingSession = true;
obj.count++;
// 如果还没检验过session是否有效则需要检验一次
obj._checkSessionStartTime = new Date().getTime();
console.log('wx.checkSession');
wx.checkSession({
success: function () {
// 登录态有效,且在本生命周期内无须再检验了
sessionIsFresh = true;
},
fail: function () {
// 登录态过期
session = '';
},
complete: function () {
isCheckingSession = false;
obj.count--;
obj._checkSessionEndTime = new Date().getTime();
if (typeof reportCGI === "function") {
reportCGI('wx_checkSession', obj._checkSessionStartTime, obj._checkSessionEndTime, request);
}
doLogin(callback, obj);
flow.emit('checkSessionFinished');
}
})
} else {
// 已经检验过了
doLogin(callback, obj);
}
}
function doLogin(callback, obj) {
2018-09-24 21:58:20 +08:00
if (obj.isLogin) {
// 登录接口,直接放过
typeof callback === "function" && callback();
2018-09-24 21:58:20 +08:00
} else if(session) {
// 缓存中有session
if(sessionExpireTime && new Date().getTime() > sessionExpire) {
// 如果有设置本地session缓存时间且缓存时间已到
session = '';
doLogin(callback, obj);
} else {
typeof callback === "function" && callback();
}
} else if (logining) {
// 正在登录中,请求轮询稍后,避免重复调用登录接口
flow.wait('doLoginFinished', function () {
doLogin(callback, obj);
})
} else {
// 缓存中无session
logining = true;
obj.count++;
// 记录调用wx.login前的时间戳
obj._loginStartTime = new Date().getTime();
console.log('wx.login');
wx.login({
complete: function () {
obj.count--;
// 记录wx.login返回数据后的时间戳用于上报
obj._loginEndTime = new Date().getTime();
if (typeof reportCGI === "function") {
reportCGI('wx_login', obj._loginStartTime, obj._loginEndTime, request);
}
typeof obj.complete === "function" && obj.count == 0 && obj.complete();
},
success: function (res) {
if (res.code) {
var data;
// codeToSession.data支持函数
if (typeof codeToSession.data === "function") {
data = codeToSession.data();
} else {
data = codeToSession.data || {};
}
data[codeToSession.codeName] = res.code;
obj.count++;
requestWrapper({
url: codeToSession.url,
data: data,
method: codeToSession.method,
isLogin: true,
report: codeToSession.report || codeToSession.url,
success: function (s) {
session = s;
sessionIsFresh = true;
2018-09-24 21:58:20 +08:00
// 如果有设置本地session过期时间
if(sessionExpireTime) {
sessionExpire = new Date().getTime() + sessionExpireTime;
wx.setStorage({
key: sessionExpireKey,
data: sessionExpire
})
}
typeof callback === "function" && callback();
wx.setStorage({
key: sessionName,
data: session
})
},
complete: function () {
obj.count--;
typeof obj.complete === "function" && obj.count == 0 && obj.complete();
logining = false;
flow.emit('doLoginFinished');
},
fail: codeToSession.fail || null
});
} else {
fail(obj, res);
console.error(res);
// 登录失败,解除锁,防止死锁
logining = false;
flow.emit('doLoginFinished');
}
},
fail: function (res) {
fail(obj, res);
console.error(res);
// 登录失败,解除锁,防止死锁
logining = false;
flow.emit('doLoginFinished');
}
})
}
}
function preDo(obj) {
typeof obj.beforeSend === "function" && obj.beforeSend();
// 登录态失效,重复登录计数
if (typeof obj.reLoginLimit === "undefined") {
obj.reLoginLimit = 0;
} else {
obj.reLoginLimit++;
}
if (typeof obj.count === "undefined") {
obj.count = 0;
}
if (obj.showLoading) {
loading.show(obj.showLoading);
obj.complete = (function (fn) {
return function () {
loading.hide();
typeof fn === "function" && fn.apply(this, arguments);
}
})(obj.complete)
}
return obj;
}
function request(obj) {
obj.count++;
if (!obj.data) {
obj.data = {};
}
if (obj.url != codeToSession.url && session) {
obj.data[sessionName] = session;
}
// 如果有全局参数,则添加
var gd = {};
if (typeof globalData === "function") {
gd = globalData();
} else if (typeof globalData === "object") {
gd = globalData;
}
obj.data = Object.assign({}, gd, obj.data);
obj.method = obj.method || 'GET';
// 如果请求的URL中不是http开头的则自动添加配置中的前缀
var url = obj.url.startsWith('http') ? obj.url : ((typeof urlPerfix === "function" ? urlPerfix() : urlPerfix) + obj.url);
// 如果请求不是GET则在URL中自动加上登录态和全局参数
if (obj.method != "GET") {
if (session) {
if (url.indexOf('?') >= 0) {
url += '&' + sessionName + '=' + encodeURIComponent(session);
} else {
url += '?' + sessionName + '=' + encodeURIComponent(session);
}
}
// 如果有全局参数则在URL中添加
for (var i in gd) {
if (url.indexOf('?') >= 0) {
url += '&' + i + '=' + gd[i];
} else {
url += '?' + i + '=' + gd[i];
}
}
}
// 如果有上报字段配置,则记录请求发出前的时间戳
if (obj.report) {
obj._reportStartTime = new Date().getTime();
}
wx.request({
url: url,
data: obj.data,
method: obj.method,
header: obj.header || {},
dataType: obj.dataType || 'json',
success: function (res) {
if (res.statusCode == 200) {
// 如果有上报字段配置,则记录请求返回后的时间戳,并进行上报
if (obj.report && typeof reportCGI === "function") {
obj._reportEndTime = new Date().getTime();
reportCGI(obj.report, obj._reportStartTime, obj._reportEndTime, request);
}
if (obj.isLogin) {
// 登录请求
var s = "";
try {
s = codeToSession.success(res.data);
} catch (e) {
}
if (s) {
obj.success(s);
} else {
fail(obj, res);
}
} else if (loginTrigger(res.data) && obj.reLoginLimit < reLoginLimit) {
// 登录态失效,且重试次数不超过配置
session = '';
wx.removeStorage({
key: sessionName,
complete: function () {
doLogin(function () {
requestWrapper(obj);
}, obj)
}
})
} else if (successTrigger(res.data) && typeof obj.success === "function") {
// 接口返回成功码
var realData = null;
try {
realData = successData(res.data);
} catch (e) {
console.error("Function successData occur error: " + e);
}
if(!obj.noCacheFlash) {
// 如果为了保证页面不闪烁,则不回调,只是缓存最新数据,待下次进入再用
obj.success(realData);
}
if (obj.cache === true || (typeof obj.cache === "function" && obj.cache(realData))) {
wx.setStorage({
key: obj.url,
data: realData
})
}
} else {
// 接口返回失败码
fail(obj, res);
}
} else {
fail(obj, res);
}
},
fail: function (res) {
fail(obj, res);
console.error(res);
},
complete: function () {
obj.count--;
typeof obj.complete === "function" && obj.count == 0 && obj.complete();
}
})
}
function uploadFile(obj) {
obj.count++;
if (!obj.formData) {
obj.formData = {};
}
obj.formData[sessionName] = session;
// 如果有全局参数,则添加
var gd = {};
if (typeof globalData === "function") {
gd = globalData();
} else if (typeof globalData === "object") {
gd = globalData;
}
obj.formData = Object.assign({}, gd, obj.formData);
obj.dataType = obj.dataType || 'json';
// 如果请求的URL中不是http开头的则自动添加配置中的前缀
var url = obj.url.startsWith('http') ? obj.url : ((typeof urlPerfix === "function" ? urlPerfix() : urlPerfix) + obj.url);
// 在URL中自动加上登录态和全局参数
if (session) {
if (url.indexOf('?') >= 0) {
url += '&' + sessionName + '=' + session;
} else {
url += '?' + sessionName + '=' + session;
}
}
// 如果有全局参数则在URL中添加
for (var i in gd) {
if (url.indexOf('?') >= 0) {
url += '&' + i + '=' + gd[i];
} else {
url += '?' + i + '=' + gd[i];
}
}
// 如果有上报字段配置,则记录请求发出前的时间戳
if (obj.report) {
obj._reportStartTime = new Date().getTime();
}
wx.uploadFile({
url: url,
filePath: obj.filePath || '',
name: obj.name || '',
formData: obj.formData,
success: function (res) {
if (res.statusCode == 200 && res.errMsg == 'uploadFile:ok') {
// 如果有上报字段配置,则记录请求返回后的时间戳,并进行上报
if (obj.report && typeof reportCGI === "function") {
obj.endTime = new Date().getTime();
reportCGI(obj.report, obj._reportStartTime, obj._reportEndTime, request);
}
if (obj.dataType == 'json') {
try {
res.data = JSON.parse(res.data);
} catch (e) {
fail(obj, res);
return false;
}
}
if (loginTrigger(res.data) && obj.reLoginLimit < reLoginLimit) {
// 登录态失效,且重试次数不超过配置
session = '';
wx.removeStorage({
key: sessionName,
complete: function () {
doLogin(function () {
uploadFileWrapper(obj);
}, obj)
}
})
} else if (successTrigger(res.data) && typeof obj.success === "function") {
// 接口返回成功码
obj.success(successData(res.data));
} else {
// 接口返回失败码
fail(obj, res);
}
} else {
fail(obj, res);
}
},
fail: function (res) {
fail(obj, res);
console.error(res);
},
complete: function () {
obj.count--;
typeof obj.complete === "function" && obj.count == 0 && obj.complete();
}
})
}
function fail(obj, res) {
if (typeof obj.fail === "function") {
obj.fail(res);
} else {
var title = "";
if (typeof errorTitle === "function") {
try {
title = errorTitle(res.data)
} catch (e) {
}
} else if (typeof errorTitle === "string") {
title = errorTitle;
}
var content = "";
if (typeof errorContent === "function") {
try {
content = errorContent(res.data)
} catch (e) {
}
} else if (typeof errorContent === "string") {
content = errorContent;
}
wx.showModal({
title: title,
content: content || "网络或服务异常,请稍后重试",
showCancel: false
})
}
// 如果有配置统一错误回调函数,则执行它
if (typeof errorCallback === "function") {
errorCallback(obj, res);
}
console.error(res);
}
function getCache(obj, callback) {
if (obj.cache) {
wx.getStorage({
key: obj.url,
success: function (res) {
typeof obj.beforeSend === "function" && obj.beforeSend();
if (typeof obj.cache === "function" && obj.cache(res.data)) {
typeof obj.success === "function" && obj.success(res.data, {isCache: true});
} else if (obj.cache == true) {
typeof obj.success === "function" && obj.success(res.data, {isCache: true});
}
typeof obj.complete === "function" && obj.complete();
// 成功取出缓存,还要去请求拿最新的再存起来
callback(obj);
},
fail: function() {
// 找不到缓存,直接发起请求,且不再防止页面闪烁(本来就没缓存了,更不存在更新页面导致的闪烁)
obj.noCacheFlash = false;
callback(obj);
}
})
} else {
callback(obj);
}
}
function login(callback) {
checkSession(callback, {})
}
function init(params) {
sessionName = params.sessionName || 'session';
loginTrigger = params.loginTrigger || function () {
return false
};
codeToSession = params.codeToSession || {};
successTrigger = params.successTrigger || function () {
return true
};
urlPerfix = params.urlPerfix || "";
successData = params.successData || function (res) {
return res
};
errorTitle = params.errorTitle || "操作失败";
errorContent = params.errorContent || false;
reLoginLimit = params.reLoginLimit || 3;
errorCallback = params.errorCallback || null;
sessionIsFresh = params.doNotCheckSession || false;
reportCGI = params.reportCGI || false;
mockJson = params.mockJson || false;
globalData = params.globalData || false;
2018-09-24 21:58:20 +08:00
sessionExpireTime = params.sessionExpireTime || null;
sessionExpireKey = params.sessionExpireKey || "sessionExpireKey";
try {
session = wx.getStorageSync(sessionName) || '';
2018-09-24 21:58:20 +08:00
sessionExpire = wx.getStorageSync(sessionExpireKey) || Infinity;
// 如果有设置本地session过期时间且验证已过期则直接清空session
if(new Date().getTime() > sessionExpire) {
session = '';
}
} catch (e) {
}
}
function requestWrapper(obj) {
obj = preDo(obj);
if (mockJson && mockJson[obj.url]) {
// mock 模式
mock(obj);
} else {
getCache(obj, function (obj) {
checkSession(function () {
request(obj);
}, obj)
}
)
}
}
function uploadFileWrapper(obj) {
obj = preDo(obj);
checkSession(function () {
uploadFile(obj);
}, obj)
}
function setSession(s) {
session = s;
sessionIsFresh = true;
}
function mock(obj) {
var res = {
data: mockJson[obj.url]
};
if (successTrigger(res.data) && typeof obj.success === "function") {
// 接口返回成功码
obj.success(successData(res.data));
} else {
// 接口返回失败码
fail(obj, res);
}
if (typeof obj.complete === "function") {
obj.complete();
}
}
function getSession() {
return session;
}
function getConfig() {
return {
2018-09-24 21:58:20 +08:00
urlPerfix: urlPerfix,
sessionExpireTime: sessionExpireTime,
sessionExpireKey: sessionExpireKey,
sessionExpire: sessionExpire
}
}
module.exports = {
init: init,
request: requestWrapper,
uploadFile: uploadFileWrapper,
setSession: setSession,
login: login,
getSession: getSession,
getConfig: getConfig
};
2018-09-24 21:58:20 +08:00
/***/ })
/******/ });
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly93ZVJlcXVlc3Qvd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vd2VSZXF1ZXN0Ly4vc3JjL2xpYi9mbG93LmpzIiwid2VicGFjazovL3dlUmVxdWVzdC8uL3NyYy9sb2FkaW5nLmpzIiwid2VicGFjazovL3dlUmVxdWVzdC8uL3NyYy93ZVJlcXVlc3QuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBSztBQUNMO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLHlEQUFpRCxjQUFjO0FBQy9EOztBQUVBO0FBQ0E7QUFDQTtBQUNBLG1DQUEyQiwwQkFBMEIsRUFBRTtBQUN2RCx5Q0FBaUMsZUFBZTtBQUNoRDtBQUNBO0FBQ0E7O0FBRUE7QUFDQSw4REFBc0QsK0RBQStEOztBQUVySDtBQUNBOzs7QUFHQTtBQUNBOzs7Ozs7Ozs7Ozs7QUNuRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtQkFBbUIsbUJBQW1CO0FBQ3RDO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEM7Ozs7Ozs7Ozs7O0FDOUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsQzs7Ozs7Ozs7Ozs7QUNoQkE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFD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