feat: 支持Promise方式调用

This commit is contained in:
ivinwu
2019-01-24 10:26:51 +08:00
parent 5f81071d00
commit 399b465e9b
26 changed files with 461 additions and 370 deletions

View File

@@ -1,15 +1,31 @@
<p align="center"><img src="./image/logo.png" alt="weRequest" height="160"/></p>
<h2 align="center">v1.0.0</h2>
<h2 align="center">v1.2.0</h2>
<p align="center"><b>解决繁琐的小程序会话管理,一款自带登录态管理的网络请求组件。</b></p>
## 目标
让业务逻辑更专注不用再关注底层登录态问题。小程序对比以往的H5登录态管理逻辑要复杂很多。通过`weRequest`这个组件,希望能帮助开发者把更多精力放在业务逻辑上,而登录态管理问题只需通过一次简单配置,以后就不用再花精力管理了。
## 怎么使用
## 安装
### 1) 通过npm安装
```
npm install --save we-request
```
### 2直接下载`dist/weRequest.min.js`放到小程序包内
## 怎么使用
```javascript
var weRequest= require('../weRequest');
// ES6 模式
import weRequest from 'we-request';
// 若下载文件到本地,则直接引入对应文件,具体路径自己根据情况修改
// import weRequest from '../lib/weRequest.min'
// commonJs 模式
const weRequest= require('we-request');
// 若下载文件到本地,则直接引入对应文件,具体路径自己根据情况修改
// const weRequest = require('../lib/weRequest.min');
// 初始化配置
weRequest.init({
@@ -27,6 +43,16 @@ weRequest.request({
// 省略...
}
})
// 同时也支持Promise形式使用
weRequest.request({
url: 'order/detail',
data: {
id: '107B7615E04AE64CFC10'
}
}).then((data)=>{
// 省略...
})
```
- 引入`weRequest`组件
- 初始化组件配置
@@ -211,6 +237,7 @@ weRequest.init({
### .request(OBJECT)
[return Promise]
带上登录态发起一个请求,参数大部分与`wx.request`一致
#### OBJECT参数说明
@@ -251,6 +278,7 @@ weRequest.request({
### .uploadFile(Object)
[return Promise]
带上登录态,将本地资源上传到开发者服务器,客户端发起一个 HTTPS POST 请求,其中 content-type 为 multipart/form-data参数大部分与`wx.uploadFile`一致
#### OBJECT参数说明

View File

@@ -1,6 +1,6 @@
declare const _default: () => {
urlPerfix: string | (() => string) | undefined;
sessionExpireTime: any;
sessionExpireTime: number | undefined;
sessionExpireKey: string;
sessionExpire: any;
};

View File

@@ -1,2 +1,2 @@
declare const _default: (callback: Function) => void;
declare const _default: () => Promise<{}>;
export default _default;

View File

@@ -1,3 +1,3 @@
import { IRequestOption } from '../interface';
declare const _default: (obj: IRequestOption) => void;
declare const _default: (obj: IRequestOption) => any;
export default _default;

View File

@@ -1,3 +1,3 @@
import { IUploadFileOption } from "../interface";
declare const _default: (obj: IUploadFileOption) => void;
declare const _default: (obj: IUploadFileOption) => any;
export default _default;

75
build/interface.d.ts vendored Normal file
View File

@@ -0,0 +1,75 @@
/// <reference types="wx" />
export interface IInitOption {
codeToSession: ICodeToSessionOptions;
sessionName: string;
urlPerfix?: string | (() => string);
doNotCheckSession?: boolean;
reLoginLimit?: number;
errorCallback?: null | Function;
reportCGI?: boolean | ((name: string, startTime: number, endTime: number, request: Function) => void);
mockJson?: any;
globalData?: boolean | object | Function;
sessionExpireKey: string;
sessionExpireTime?: number;
loginTrigger?: (res: string | IAnyObject | ArrayBuffer) => boolean;
successTrigger: (res: string | IAnyObject | ArrayBuffer) => boolean;
successData: (res: string | IAnyObject | ArrayBuffer) => string | IAnyObject | ArrayBuffer;
errorTitle?: string | ((res: string | IAnyObject | ArrayBuffer) => string);
errorContent?: string | ((res: string | IAnyObject | ArrayBuffer) => string);
}
export interface ICodeToSessionOptions {
url: string;
method?: 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE' | 'CONNECT' | 'string';
codeName?: string;
data?: string | Function | IAnyObject | ArrayBuffer;
success: Function;
fail?: Function;
report?: string;
}
export interface IRequestOption extends IRequestObject {
beforeSend?: Function;
showLoading?: boolean | string;
report?: string;
cache?: boolean | Function;
noCacheFlash?: boolean;
success?: (res: string | IAnyObject | ArrayBuffer, cacheInfo?: object) => void;
complete?: () => void;
fail?: (res: string | IAnyObject | ArrayBuffer) => void;
catchError?: boolean;
}
export interface IRequestObject extends wx.RequestOption {
originUrl: string;
reLoginCount: number;
_reportStartTime: number;
_reportEndTime: number;
}
export interface IUploadFileOption extends IUploadFileObject {
beforeSend?: Function;
showLoading?: boolean | string;
report?: string;
success?: (res: string | IAnyObject | ArrayBuffer, cacheInfo?: object) => void;
complete?: () => void;
fail?: (res: string | IAnyObject | ArrayBuffer) => void;
catchError?: boolean;
}
export interface IUploadFileObject extends wx.UploadFileOption {
originUrl: string;
reLoginCount: number;
_reportStartTime: number;
_reportEndTime: number;
}
export interface IGetConfigResult {
urlPerfix?: string | (() => string);
sessionExpireTime?: number;
sessionExpireKey?: string;
sessionExpire?: number;
}
export interface weRequest {
init?: (obj: IInitOption) => void;
request?: (option: IRequestOption) => void;
uploadFile?: (option: IUploadFileOption) => void;
getSession?: () => string;
getConfig?: () => IGetConfigResult;
login?: (callback: Function) => void;
setSession?: (x: string) => void;
}

View File

@@ -1,7 +1,7 @@
import { IRequestOption, IUploadFileOption } from "../interface";
declare function format(originUrl: string): string;
declare function request(obj: IRequestOption): void;
declare function uploadFile(obj: IUploadFileOption): void;
declare function request(obj: IRequestOption): any;
declare function uploadFile(obj: IUploadFileOption): any;
declare const _default: {
format: typeof format;
request: typeof request;

View File

@@ -1,5 +1,5 @@
declare function delSession(): void;
declare function main(fn: Function): void;
declare function main(): Promise<{}>;
declare const _default: {
main: typeof main;
delSession: typeof delSession;

View File

@@ -1,7 +0,0 @@
declare function emit(key: string): void;
declare function wait(key: string, callback: Function): void;
declare const _default: {
wait: typeof wait;
emit: typeof emit;
};
export default _default;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -4,7 +4,7 @@ import status from '../store/status'
export default () => {
return {
urlPerfix: config.urlPerfix,
sessionExpireTime: status.sessionExpireTime,
sessionExpireTime: config.sessionExpireTime,
sessionExpireKey: config.sessionExpireKey,
sessionExpire: status.sessionExpire
}

View File

@@ -1,5 +1,5 @@
import sessionManager from '../module/sessionManager'
export default (callback: Function) => {
return sessionManager.main(callback)
export default () => {
return sessionManager.main()
}

View File

@@ -2,5 +2,5 @@ import requestHandler from '../module/requestHandler'
import { IRequestOption } from '../interface'
export default (obj: IRequestOption) => {
requestHandler.request(obj)
return requestHandler.request(obj)
}

View File

@@ -2,5 +2,5 @@ import requestHandler from '../module/requestHandler'
import { IUploadFileOption } from "../interface";
export default (obj: IUploadFileOption) => {
requestHandler.uploadFile(obj)
return requestHandler.uploadFile(obj)
}

View File

@@ -28,6 +28,8 @@ export interface IInitOption {
globalData?: boolean | object | Function;
/** session在本地缓存的key */
sessionExpireKey: string;
/* session在本地缓存的有效时间单位ms */
sessionExpireTime?: number;
/* 触发重新登录的条件参数为CGI返回的数据返回需要重新登录的条件 */
loginTrigger?: (res: string | IAnyObject | ArrayBuffer) => boolean;
/* 触发请求成功的条件参数为CGI返回的数据返回接口逻辑成功的条件 */
@@ -81,6 +83,8 @@ export interface IRequestOption extends IRequestObject {
complete?: ()=> void;
/** 接口调用失败 或 逻辑失败 的回调函数 */
fail?: (res: string | IAnyObject | ArrayBuffer)=> void;
/** 当使用Promise模式时开发者是否需要捕获错误默认不捕获统一自动处理错误 */
catchError?: boolean;
}
export interface IRequestObject extends wx.RequestOption{
@@ -107,6 +111,8 @@ export interface IUploadFileOption extends IUploadFileObject {
complete?: ()=> void;
/** 接口调用失败 或 逻辑失败 的回调函数 */
fail?: (res: string | IAnyObject | ArrayBuffer)=> void;
/** 当使用Promise模式时开发者是否需要捕获错误默认不捕获统一自动处理错误 */
catchError?: boolean;
}
export interface IUploadFileObject extends wx.UploadFileOption {

View File

@@ -4,11 +4,10 @@ function get(obj: IRequestOption) {
wx.getStorage({
key: obj.originUrl,
success (res) {
if (typeof obj.cache === "function" && obj.cache(res.data)) {
if (typeof obj.success === "function") {
obj.success(res.data, {isCache: true})
}
} else if (obj.cache === true) {
if (
obj.cache === true ||
(typeof obj.cache === "function" && obj.cache(res.data))
) {
if (typeof obj.success === "function") {
obj.success(res.data, {isCache: true})
}
@@ -21,7 +20,10 @@ function get(obj: IRequestOption) {
}
function set(obj: IRequestOption , realData: string | object) {
if (obj.cache === true || (typeof obj.cache === "function" && obj.cache(realData))) {
if (
obj.cache === true ||
(typeof obj.cache === "function" && obj.cache(realData))
) {
wx.setStorage({
key: obj.originUrl,
data: realData

View File

@@ -2,9 +2,10 @@ import config from '../store/config'
import { IRequestOption, IUploadFileOption } from "../interface";
function systemError(obj: IRequestOption | IUploadFileOption, res: wx.GeneralCallbackResult) {
doError("", res.errMsg);
if (typeof obj.fail === "function") {
obj.fail("");
obj.fail(res);
} else {
doError("", res.errMsg);
}
}

View File

@@ -1,10 +1,11 @@
import config from '../store/config'
import loading from '../util/loading'
import responseHandler from './responseHandler'
import { IRequestOption, IUploadFileOption } from "../interface";
import { IRequestOption, IUploadFileOption } from "../interface"
function get(obj: IRequestOption | IUploadFileOption, method: "request" | "uploadFile"): any {
if(!config.mockJson[obj.url] && !config.mockJson[obj.originUrl]) {
if(!(config.mockJson[obj.url] || config.mockJson[obj.originUrl])) {
// mock 没有对应接口的数据
console.error('mock 没有对应接口的数据');
return false;
@@ -18,7 +19,8 @@ function get(obj: IRequestOption | IUploadFileOption, method: "request" | "uploa
statusCode: 200
};
responseHandler(res, obj, method)
loading.hide();
return responseHandler(res, obj, method)
}
export default {

View File

@@ -4,11 +4,11 @@ import status from '../store/status'
import mockManager from './mockManager'
import cacheManager from './cacheManager'
import sessionManager from './sessionManager'
import errorHandler from './errorHandler'
import responseHandler from './responseHandler'
import durationReporter from "./durationReporter"
import url from '../util/url'
import {IRequestOption, IUploadFileOption} from "../interface"
import errorHandler from "./errorHandler";
// 格式化url
function format(originUrl: string) {
@@ -127,13 +127,14 @@ function doRequest(obj: IRequestOption) {
return resolve(res);
},
fail(res: wx.GeneralCallbackResult) {
errorHandler.systemError(obj, res);
return reject(res);
},
complete() {
if (typeof obj.complete === "function") {
obj.complete();
}
if(obj.showLoading) {
if (obj.showLoading) {
loading.hide()
}
}
@@ -143,7 +144,7 @@ function doRequest(obj: IRequestOption) {
function doUploadFile(obj: IUploadFileOption) {
obj = initializeUploadFileObj(obj);
return new Promise((resolve, reject) =>{
return new Promise((resolve, reject) => {
wx.uploadFile({
url: obj.url,
filePath: obj.filePath || '',
@@ -153,13 +154,14 @@ function doUploadFile(obj: IUploadFileOption) {
return resolve(res);
},
fail(res: wx.GeneralCallbackResult) {
errorHandler.systemError(obj, res);
return reject(res);
},
complete() {
if (typeof obj.complete === "function") {
obj.complete();
}
if(obj.showLoading) {
if (obj.showLoading) {
loading.hide()
}
}
@@ -167,42 +169,48 @@ function doUploadFile(obj: IUploadFileOption) {
})
}
function request(obj: IRequestOption): void {
function request(obj: IRequestOption): any {
return new Promise((resolve, reject) => {
obj = preDo(obj);
if (config.mockJson) {
mockManager.get(obj, 'request');
return;
let mockResponse = mockManager.get(obj, 'request');
if (mockResponse) {
return resolve(mockResponse);
}
}
if (obj.cache) {
cacheManager.get(obj);
}
sessionManager.main(() => {
doRequest(obj).then((res) => {
return responseHandler(res as wx.RequestSuccessCallbackResult, obj, 'request');
}).catch((res) => {
console.error(res);
return errorHandler.systemError(obj, res);
sessionManager.main().then(() => {
return doRequest(obj)
}).then((res) => {
let response = responseHandler(res as wx.RequestSuccessCallbackResult, obj, 'request');
return resolve(response);
}).catch((e) => {
return reject(e);
})
})
}
function uploadFile(obj: IUploadFileOption): void {
obj = preDo(obj) as IUploadFileOption;
function uploadFile(obj: IUploadFileOption): any {
return new Promise((resolve, reject) => {
obj = preDo(obj);
if (config.mockJson) {
mockManager.get(obj, 'uploadFile');
return;
}
sessionManager.main(() => {
doUploadFile(obj).then((res)=>{
return responseHandler(res as wx.UploadFileSuccessCallbackResult, obj, 'uploadFile')
}).catch((res)=>{
console.error(res);
return errorHandler.systemError(obj, res);
sessionManager.main().then(() => {
return doUploadFile(obj)
}).then((res) => {
let response = responseHandler(res as wx.UploadFileSuccessCallbackResult, obj, 'uploadFile');
return resolve(response);
}).catch((e) => {
return reject(e);
})
})
}

View File

@@ -18,8 +18,12 @@ function response(
try {
res.data = JSON.parse(res.data);
} catch (e) {
if(obj.catchError) {
throw new Error(e);
} else {
errorHandler.logicError(obj, res);
return false;
return;
}
}
}
@@ -45,17 +49,29 @@ function response(
// 如果为了保证页面不闪烁,则不回调,只是缓存最新数据,待下次进入再用
if(typeof obj.success === "function"){
obj.success(realData);
} else {
return realData;
}
}
// 缓存存储
cacheManager.set(obj, realData);
} else {
// 接口返回失败码
errorHandler.logicError(obj, res);
}
if(obj.catchError) {
let msg = errorHandler.getErrorMsg(res);
throw new Error(msg.content);
} else {
errorHandler.logicError(obj, res);
}
}
} else {
// https返回状态码非200
if(obj.catchError) {
throw new Error(res.statusCode.toString());
} else {
errorHandler.logicError(obj, res);
}
}
}
export default response;

View File

@@ -1,4 +1,3 @@
import flow from '../util/flow'
import status from '../store/status'
import config from '../store/config'
import errorHandler from './errorHandler'
@@ -7,9 +6,10 @@ import requestHandler from './requestHandler'
/* 生命周期内只做一次的checkSession */
let checkSessionPromise: any = null;
function checkSession() {
if (!checkSessionPromise) {
checkSessionPromise = new Promise((resolve, reject) => {
checkSessionPromise = new Promise((resolve) => {
console.log("wx.checkSession()");
const start = new Date().getTime();
wx.checkSession({
@@ -19,7 +19,8 @@ function checkSession() {
},
fail() {
// 登录态过期
return reject();
delSession();
return resolve();
},
complete() {
const end = new Date().getTime();
@@ -37,7 +38,7 @@ function isSessionExpireOrEmpty() {
// 如果缓存中没有session
return true
}
if (status.sessionExpireTime && new Date().getTime() > status.sessionExpire) {
if (config.sessionExpireTime && new Date().getTime() > status.sessionExpire) {
// 如果有设置本地session缓存时间且缓存时间已到
delSession();
return true
@@ -45,34 +46,40 @@ function isSessionExpireOrEmpty() {
return false
}
function checkLogin(callback: Function) {
function checkLogin() {
return new Promise((resolve, reject) => {
if (isSessionExpireOrEmpty()) {
if (status.logining) {
// 正在登录中,请求轮询稍后,避免重复调用登录接口
flow.wait('doLoginFinished', () => {
checkLogin(callback);
return doLogin().then(() => {
return resolve();
}, (res: any)=>{
return reject(res);
})
} else {
// 缓存中无session
status.logining = true;
getCode().then(() => {
callback();
status.logining = false;
flow.emit('doLoginFinished');
}).catch(({title, content}) => {
errorHandler.doError(title, content);
// 登录失败,解除锁,防止死锁
status.logining = false;
flow.emit('doLoginFinished');
});
}
} else {
// 缓存中有session且未过期
callback();
return resolve();
}
})
}
function getCode() {
/* 登陆流程的promise */
let loginPromise: any = null;
function doLogin() {
if (!loginPromise) {
loginPromise = new Promise((resolve, reject) => {
login().then(() => {
loginPromise = null;
return resolve();
}).catch((res) => {
loginPromise = null;
return reject(res);
});
})
}
return loginPromise;
}
function login() {
return new Promise((resolve, reject) => {
console.log('wx.login');
const start = new Date().getTime();
@@ -81,8 +88,8 @@ function getCode() {
if (res.code) {
code2Session(res.code).then(() => {
return resolve();
}).catch(({title, content}) => {
return reject({title, content});
}).catch((res) => {
return reject(res);
})
} else {
return reject({title: "登录失败", "content": "请稍后重试[code 获取失败]"});
@@ -134,8 +141,8 @@ function code2Session(code: string) {
// 换回来的session不需要再checkSession
config.doNotCheckSession = true;
// 如果有设置本地session过期时间
if (status.sessionExpireTime) {
status.sessionExpire = new Date().getTime() + status.sessionExpireTime;
if (config.sessionExpireTime) {
status.sessionExpire = new Date().getTime() + config.sessionExpireTime;
wx.setStorage({
key: config.sessionExpireKey,
data: String(status.sessionExpire)
@@ -170,19 +177,17 @@ function delSession() {
})
}
function main(fn: Function) {
if (!config.doNotCheckSession && status.session) {
checkSession().then(() => {
return checkLogin(fn)
}).catch(() => {
// 登录态过期清空session缓存
delSession();
return checkLogin(fn)
function main() {
return new Promise((resolve, reject) => {
return checkLogin().then(() => {
return config.doNotCheckSession ? Promise.resolve() : checkSession()
}, ({title, content}) => {
errorHandler.doError(title, content);
return reject({title, content});
}).then(() => {
return resolve();
})
})
} else {
// 不需要checkSession
return checkLogin(fn)
}
}
export default {

View File

@@ -1,9 +1,5 @@
export default {
session: '' as string,
// session在本地缓存的有效时间
sessionExpireTime: null,
// session过期的时间点
sessionExpire: Infinity as number,
// 正在登录中,其他请求轮询稍后,避免重复调用登录接口
logining: false as boolean
sessionExpire: Infinity as number
} as any

View File

@@ -1,32 +0,0 @@
const store: any = {};
function emit(key: string) {
const flow = getFlow(key);
const currentLength = flow.waitingList.length;
for (let i = 0; i < currentLength; i++) {
const callback = flow.waitingList.shift();
if(typeof callback == "function"){
callback();
}
}
}
function wait(key: string, callback: Function) {
const flow = getFlow(key);
flow.waitingList.push(callback)
}
function getFlow(key: string) {
if (!store[key]) {
store[key] = {
waitingList: []
}
}
return store[key];
}
export default {
wait,
emit
}

View File

@@ -3,13 +3,14 @@ function setParams(url: string = "", params: object) {
let kvp: any = {};
if (queryStringIndex >= 0) {
const oldQueryString = url.substr(queryStringIndex + 1).split("&");
// @ts-ignore
oldQueryString.forEach((x, i) => {
const kv: string[] = oldQueryString[i].split("=");
kvp[kv[0]] = kv[1];
});
}
kvp = { ...kvp, ...params };
kvp = {...kvp, ...params};
const queryString = Object.keys(kvp)
.map(key => {

View File

@@ -22,7 +22,8 @@ module.exports = [
path: path.join(__dirname, "build"),
filename: "weRequest.min.js",
library: "weRequest",
libraryTarget: "commonjs-module"
libraryTarget: "commonjs-module",
libraryExport: "default"
},
plugins: [
new webpack.BannerPlugin({
@@ -49,7 +50,8 @@ module.exports = [
path: path.join(__dirname, "build"),
filename: "weRequest.js",
library: "weRequest",
libraryTarget: "commonjs-module"
libraryTarget: "commonjs-module",
libraryExport: "default"
},
devtool: "inline-source-map",
plugins: [