transfer from svn
This commit is contained in:
1
build/weRequest.js
Normal file
1
build/weRequest.js
Normal file
File diff suppressed because one or more lines are too long
314
docs/README.md
Normal file
314
docs/README.md
Normal file
@@ -0,0 +1,314 @@
|
||||
<img src="image/logo.png" alt="logo" height="160" align="center" />
|
||||
|
||||
# weRequest
|
||||
|
||||
_解决繁琐的小程序会话管理,一款自带登录态管理的网络请求组件。_
|
||||
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
|
||||
## 简介
|
||||
|
||||

|
||||
|
||||
上图是小程序官方文档中的**登录时序图**。此图涵盖了前后端,详细讲解了包括登录态的生成,维护,传输等各方面的问题。
|
||||
|
||||
具体到业务开发过程中的前端来说,我认为上图还不够完整,于是我画了下面这张以**前端逻辑**为出发点的、包含循环的**流程图**。
|
||||
我认为前端每一次**发起网络请求**,跟后台进行数据交互,都适用于下图的**流程**:
|
||||

|
||||
|
||||
- **hasChecked:** 用一状态标识本生命周期内是否执行过`wx.checkSession`,判断该标识,若否,开始执行`wx.checkSession`,若是,进入下一步
|
||||
- **wx.checkSession():** 调用接口判断登录态是否过期,若是,重新登录;若否,进入下一步
|
||||
> wx.checkSession()是小程序提供的检测登录态是否过期的接口,生命周期内只需调用一次即可。用户越久未使用小程序,用户登录态越有可能失效。反之如果用户一直在使用小程序,则用户登录态一直保持有效。具体时效逻辑由微信维护,对开发者透明
|
||||
|
||||
- **wx.getStorage(session):** 尝试获取本地的`session`。如果之前曾经登录过,则能获取到;否则,本地无`session`
|
||||
- **wx.login():** 小程序提供的接口,用于获取`code`(code有效期为5分钟)
|
||||
- **wx.request(code):** 将`code`通过后台提供的接口,换取`session`
|
||||
- **wx.setStorage(session):** 将后台接口返回的`session`存入到localStorage,以备后续使用
|
||||
- **wx.request(session):** 真正发起业务请求,请求中带上`session`
|
||||
- **parse(data):** 对后台返回的数据进行预解析,若发现登录态失效,则重新执行登录;若成功,则真正获取到业务数据
|
||||
|
||||
只要遵循上图的流程,我们就无需在业务逻辑中关注登录态的问题了,相当于把登录态的管理问题**耦合**到了发起网络请求当中,本组件则完成了上述流程的封装,让开发者不用再关心以上逻辑,把精力放回在业务的开发上。
|
||||
|
||||
## 目标
|
||||
让业务逻辑更专注,不用再关注底层登录态问题。小程序对比以往的H5,登录态管理逻辑要复杂很多。通过`weRequest`这个组件,希望能帮助开发者把更多精力放在业务逻辑上,而登录态管理问题只需通过一次简单配置,以后就不用再花精力管理了。
|
||||
|
||||
## 怎么使用
|
||||
|
||||
```javascript
|
||||
var weRequest= require('../weRequest');
|
||||
|
||||
// 初始化配置
|
||||
weRequest.init({
|
||||
// 关于配置内容,将在后文详述
|
||||
// 此处暂时省略...
|
||||
})
|
||||
|
||||
// 发起请求
|
||||
weRequest.request({
|
||||
url: 'order/detail',
|
||||
data: {
|
||||
id: '107B7615E04AE64CFC10'
|
||||
},
|
||||
success: function (data) {
|
||||
// 省略...
|
||||
}
|
||||
})
|
||||
```
|
||||
- 引入`weRequest`组件
|
||||
- 初始化组件配置
|
||||
- **就像使用`wx.request`那样去使用它**
|
||||
|
||||
## 演示DEMO
|
||||
|
||||
### 自动带上登录态参数
|
||||

|
||||
|
||||
可以看到,通过`weRequest`发出的请求,将会自动带上登录态参数。
|
||||
对应的流程为下图中**红色**的指向:
|
||||

|
||||
|
||||
### 没有登录态时,自动登录
|
||||

|
||||
|
||||
当本地没有登录态时,按照流程图,`weRequest`将会自动执行`wx.login()`后的一系列流程,得到`code`并调用后台接口换取`session`,储存在localStorage之后,重新发起业务请求。
|
||||
对应的流程为下图中**红色**的指向:
|
||||

|
||||
|
||||
### 登录态过期时,自动重新登录
|
||||

|
||||
|
||||
对后台数据进行预解析之后,发现登录态过期,于是重新执行登录流程,获取新的`session`之后,重新发起请求。
|
||||
对应的流程为下图中**红色**的指向:
|
||||

|
||||
|
||||
## 文档
|
||||
|
||||
### .init(OBJECT)
|
||||
|
||||
对组件进行初始化配置,使用组件发起请求前必须进行至少一次的配置
|
||||
|
||||
#### OBJECT参数说明
|
||||
|
||||
|参数名|类型|必填|默认值|说明|
|
||||
| :-------- | :-------| :------ | :------ |:------ |
|
||||
|sessionName|String|否|session|储存在localStorage的session名称,且CGI请求的data中会自动带上以此为名称的session值;可不配置,默认为session|
|
||||
|urlPerfix|String|否||请求URL的固定前缀,如果配置了,后续请求的URL都会自动加上这个前缀|
|
||||
|loginTrigger|Function|是||触发重新登录的条件;参数为CGI返回的数据,返回需要重新登录的条件|
|
||||
|codeToSession|Object|是||用code换取session的CGI配置|
|
||||
|reLoginLimit|Int|否|3|登录重试次数,当连续请求登录接口返回失败次数超过这个次数,将不再重试登录|
|
||||
|successTrigger|Function|是||触发请求成功的条件;参数为CGI返回的数据,返回接口逻辑成功的条件|
|
||||
|successData|Function|否||成功之后返回数据;参数为CGI返回的数据,返回逻辑需要使用的数据|
|
||||
|errorTitle|String/Function|否|操作失败|接口逻辑失败时,错误弹窗的标题|
|
||||
|errorContent|String/Function|否||接口逻辑失败时,错误弹窗的内容|
|
||||
|errorCallback|Function|否||当出现接口逻辑错误时,会执行统一的回调函数,这里可以做统一的错误上报等处理|
|
||||
|doNotCheckSession|Boolean|否|false|是否需要调用checkSession,验证小程序的登录态过期;若业务不需要使用到session_key,则可配置为true|
|
||||
|reportCGI|Function|否||接口返回成功之后,会执行统一的回调函数,这里可以做统一的耗时上报等处理|
|
||||
|mockJson|Object|否||可为接口提供mock数据|
|
||||
|globalData|Object/Function|否||所有请求都会自动带上这里的参数|
|
||||
|
||||
##### codeToSession参数说明
|
||||
|
||||
|参数名|类型|必填|默认值|说明|
|
||||
| :-------- | :-------| :------ | :------ |:------ |
|
||||
|url|String|是||CGI的url|
|
||||
|method|String|否|GET|调用改CGI的方法|
|
||||
|codeName|String|否|code|CGI中传参时,存放code的名称|
|
||||
|data|Object|否||登录接口需要的其他参数|
|
||||
|success|Function|是||接口返回成功的函数;需要返回session的值|
|
||||
|fail|Function|否||code换取session的接口逻辑出错时,执行的函数,若配置了此函数,则不再默认弹窗报错|
|
||||
|
||||
##### reportCGI返回参数说明
|
||||
|参数名|类型|说明|
|
||||
| :-------- | :-------| :------ |
|
||||
|name|String|调用的接口名字,可在request接口的report字段配置|
|
||||
|startTime|Int|发起请求时的时间戳|
|
||||
|endTime|Int|请求返回时的时间戳|
|
||||
|request|Function|请求方法,可用于上报|
|
||||
|
||||
#### 示例代码
|
||||
|
||||
```javascript
|
||||
weRequest.init({
|
||||
sessionName: "session",
|
||||
urlPerfix: "https://www.example.com/",
|
||||
// 触发重新登录的条件,res为CGI返回的数据
|
||||
loginTrigger: function (res) {
|
||||
// 此处例子:当返回数据中的字段errcode等于-1,会自动触发重新登录
|
||||
return res.errcode == -1;
|
||||
},
|
||||
codeToSession: {
|
||||
url: 'user/login',
|
||||
method: 'GET',
|
||||
codeName: 'code',
|
||||
data: {},
|
||||
// CGI中返回的session值
|
||||
success: function (res) {
|
||||
// 此处例子:CGI返回数据中的字段session即为session值
|
||||
return res.session;
|
||||
}
|
||||
},
|
||||
reLoginLimit: 2,
|
||||
successTrigger: function (res) {
|
||||
// 此处例子:当返回数据中的字段errcode等于0时,代表请求成功,其他情况都认为业务逻辑失败
|
||||
return res.errcode == 0;
|
||||
},
|
||||
successData: function (res) {
|
||||
// 此处例子:返回数据中的字段data为业务接受到的数据
|
||||
return res.data;
|
||||
},
|
||||
errorTitle: function(res) {
|
||||
// 此处例子:当返回数据中的字段errcode等于0x10040730时,错误弹框的标题是“温馨提示”,其他情况下则是“操作失败”
|
||||
return res.errcode == 0x10040730 ? '温馨提示' : '操作失败'
|
||||
},
|
||||
errorContent: function(res) {
|
||||
// 此处例子:返回数据中的字段msg为错误弹框的提示内容文字
|
||||
return res.msg
|
||||
},
|
||||
errorCallback: function(obj, res) {
|
||||
// do some report
|
||||
},
|
||||
doNotCheckSession: true,
|
||||
// 上报耗时的函数,name为上报名称,startTime为接口调用开始时的时间戳,endTime为接口返回时的时间戳
|
||||
reportCGI: function(name, startTime, endTime, request) {
|
||||
// 这里可以自行上报耗时
|
||||
//wx.reportAnalytics(name, {
|
||||
// time: endTime - startTime
|
||||
//});
|
||||
//request({
|
||||
// url: 'reportCGI',
|
||||
// data: {
|
||||
// name: name,
|
||||
// cost: endTime - startTime
|
||||
// },
|
||||
// fail: function() {
|
||||
//
|
||||
// }
|
||||
//})
|
||||
console.log(name + ":" + (endTime - startTime));
|
||||
},
|
||||
mockJson: require("../../mock.json"),
|
||||
globalData: function() {
|
||||
return {
|
||||
version: getApp().version
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### .request(OBJECT)
|
||||
|
||||
带上登录态发起一个请求,参数大部分与`wx.request`一致
|
||||
|
||||
#### OBJECT参数说明
|
||||
|
||||
|参数名|类型|必填|默认值|说明|是否与wx.request不一致|
|
||||
| :-------- | :-------| :------ | :------ |:------ |:------ |
|
||||
|url|String|是||开发者服务器接口地址,若在init()时有配置urlPerfix,则这里会自动拼接前缀|是|
|
||||
|data|Object/String|否||请求的参数||
|
||||
|header|Object|否||设置请求的 header,header 中不能设置 Referer。||
|
||||
|method|String|否|GET|(需大写)有效值:OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT||
|
||||
|dataType|String|否|json|如果设为json,会尝试对返回的数据做一次 JSON.parse||
|
||||
|beforeSend|Function|否||发起请求前执行的函数|是|
|
||||
|success|Function|否||收到开发者服务成功返回,且执行`successTrigger`成功后的回调函数,参数为`successData`返回的参数|是|
|
||||
|fail|Function|否||接口调用失败,或执行`successTrigger`失败后的回调函数,若这里有配置,则不再默认弹窗报错|是|
|
||||
|complete|Function|否||接口调用结束的回调函数(调用成功、失败都会执行)||
|
||||
|showLoading|Boolean|否|false|请求过程页面是否展示全屏的loading|是|
|
||||
|report|String|否||接口请求成功后将自动执行init()中配置的reportCGI函数,其中的name字段值为这里配置的值|是|
|
||||
|
||||
#### 示例代码
|
||||
|
||||
```javascript
|
||||
weRequest.request({
|
||||
url: 'order/detail',
|
||||
showLoading: true,
|
||||
report: 'detail',
|
||||
data: {
|
||||
id: '123'
|
||||
},
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
},
|
||||
fail: function(obj, res) {
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### .uploadFile(Object)
|
||||
|
||||
带上登录态,将本地资源上传到开发者服务器,客户端发起一个 HTTPS POST 请求,其中 content-type 为 multipart/form-data,参数大部分与`wx.uploadFile`一致
|
||||
|
||||
#### OBJECT参数说明
|
||||
|
||||
|参数名|类型|必填|默认值|说明|是否与wx.uploadFile不一致|
|
||||
| :-------- | :-------| :------ | :------ |:------ |:------ |
|
||||
|url|String|是||开发者服务器接口地址,若在init()时有配置urlPerfix,则这里会自动拼接前缀|是|
|
||||
|filePath|String|是||要上传文件资源的路径||
|
||||
|name|String|是||文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容||
|
||||
|header|Object|否||设置请求的 header,header 中不能设置 Referer。||
|
||||
|formData|Object|否||HTTP 请求中其他额外的 form data||
|
||||
|beforeSend|Function|否||发起请求前执行的函数|是|
|
||||
|success|Function|否||收到开发者服务成功返回,且执行`successTrigger`成功后的回调函数,参数为`successData`返回的参数|是|
|
||||
|fail|Function|否||接口调用失败,或执行`successTrigger`失败后的回调函数,若这里有配置,则不再默认弹窗报错|是|
|
||||
|complete|Function|否||接口调用结束的回调函数(调用成功、失败都会执行)||
|
||||
|showLoading|Boolean|否|false|请求过程页面是否展示全屏的loading|是|
|
||||
|report|String|否||接口请求成功后将自动执行init()中配置的reportCGI函数,其中的name字段值为这里配置的值|是|
|
||||
|
||||
### .login()
|
||||
|
||||
<font color=red>[不建议使用]</font> 在不发起业务请求的情况下,单独执行登录逻辑
|
||||
|
||||
### .setSession(String)
|
||||
|
||||
<font color=red>[不建议使用]</font> 设置用户票据的值
|
||||
|
||||
## FAQ
|
||||
|
||||
### 我希望在请求时候,页面能出现最简单的loading状态,该怎么办?
|
||||
|
||||
只需要在请求的时候,加上参数`showLoading: true`即可,如:
|
||||
```javascript
|
||||
weRequest.request({
|
||||
url: 'order/detail',
|
||||
showLoading: true,
|
||||
data: {
|
||||
id: '123'
|
||||
},
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
}
|
||||
})
|
||||
```
|
||||
当然,如果你希望使用个性化的loading样式,你可以直接使用beforeSend参数来进行自定义展示个性化的loading,并且在complete的时候将它隐藏。
|
||||
|
||||
### 某些请求在返回错误时,我不希望触发通用的错误提示框,而想用特别的逻辑去处理,该怎么办?
|
||||
|
||||
只需要在请求的时候,加上参数`fail: function(){ ... }`即可,如:
|
||||
```javascript
|
||||
weRequest.request({
|
||||
url: 'order/detail',
|
||||
slience: true,
|
||||
data: {
|
||||
id: '123'
|
||||
},
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
},
|
||||
fail: function(res) {
|
||||
console.log(res);
|
||||
}
|
||||
})
|
||||
```
|
||||
此时,如果接口返回错误码,将触发这里定义的fail函数,且默认错误弹框将不会出现。
|
||||
|
||||
### 为什么工具在发起请求之前,不主动去判断第三方session是否过期,而要通过接口结果来判断,这不是浪费了一次请求往返吗?
|
||||
|
||||
每个小程序对于自身生成的session都有自己的一套管理方案,微信官方也没有指明一套通用的方案来要求开发者,仅仅要求了**应该保证其安全性且不应该设置较长的过期时间**。
|
||||
原文如下:
|
||||
>通过 wx.login() 获取到用户登录态之后,需要维护登录态。开发者要注意不应该直接把 session_key、openid 等字段作为用户的标识或者 session 的标识,而应该自己派发一个 session 登录态(请参考登录时序图)。对于开发者自己生成的 session,应该保证其安全性且不应该设置较长的过期时间。session 派发到小程序客户端之后,可将其存储在 storage ,用于后续通信使用。
|
||||
|
||||
因此,不能要求所有后端接口都要返回session的过期时间给前端,甚至有些后端逻辑对于session的管理是动态的,会随调用情况来更新session的生命周期,这样的话逻辑就更复杂了。但是无论任何一种管理策略,都必须会有兜底策略,即前端传入过期的session,后端必须要返回特定标识告知前端此session过期。因此作为一个通用的工具组件,我需要确保更多的开发者能够低门槛地使用,所以并没有针对各种特别策略去优化,而且我相信,对于正常使用小程序的用户来说,登录态过期是一个相对低概率的事情,对整体效率性能来说,是微乎其微的,使用通用的兜底策略去应对这种情况,我认为已经是足够的了。
|
||||
BIN
docs/autoLogin.gif
Normal file
BIN
docs/autoLogin.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 140 KiB |
BIN
docs/auto_session.png
Normal file
BIN
docs/auto_session.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
BIN
docs/relogin.gif
Normal file
BIN
docs/relogin.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 194 KiB |
46
example/index.js
Normal file
46
example/index.js
Normal file
@@ -0,0 +1,46 @@
|
||||
const weRequest = require('./request');
|
||||
|
||||
Page({
|
||||
data: {},
|
||||
onLoad: function (option) {
|
||||
this.getData(option.orderid);
|
||||
},
|
||||
getData: function (id) {
|
||||
weRequest.request({
|
||||
url: 'order/detail',
|
||||
data: {
|
||||
id: id
|
||||
},
|
||||
showLoading: true,
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
},
|
||||
codeToSessionFail: function() {
|
||||
|
||||
},
|
||||
fail:function(obj, res) {
|
||||
if(codeToSessionFail) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
// code to session
|
||||
|
||||
// ...
|
||||
}
|
||||
})
|
||||
},
|
||||
upload: function() {
|
||||
weRequest.uploadFile({
|
||||
url: 'user/setapplyinfo',
|
||||
filePath: 'xxxxx.png',
|
||||
name: 'pic',
|
||||
formData: {
|
||||
'other': 'params'
|
||||
},
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
85
example/request.js
Normal file
85
example/request.js
Normal file
@@ -0,0 +1,85 @@
|
||||
var weRequest = require('../src/weRequest');
|
||||
|
||||
weRequest.init({
|
||||
// 存在localStorage的session名称,且CGI请求的data中会自动带上以此为名称的session值;可不传,默认为session
|
||||
sessionName: "session",
|
||||
// 请求URL的固定前缀;可不传,默认为空
|
||||
urlPerfix: "https://payapp.weixin.qq.com/",
|
||||
// 触发重新登录的条件,res为CGI返回的数据
|
||||
loginTrigger: function (res) {
|
||||
// 此处例子:当返回数据中的字段errcode等于0x10040009,会自动触发重新登录
|
||||
return res.errcode == 0x10040009;
|
||||
},
|
||||
// 用code换取session的CGI配置
|
||||
codeToSession: {
|
||||
// CGI的URL
|
||||
url: 'user/login',
|
||||
// 调用改CGI的方法;可不传,默认为GET
|
||||
method: 'GET',
|
||||
// CGI中传参时,存放code的名称,此处例子名称就是code;可不传,默认值为code
|
||||
codeName: 'code',
|
||||
// 登录接口需要的其他参数;可不传,默认为{}
|
||||
data: {},
|
||||
// CGI中返回的session值
|
||||
success: function (res) {
|
||||
// 此处例子:CGI返回数据中的字段session即为session值
|
||||
return res.session;
|
||||
},
|
||||
fail: function(obj, res) {
|
||||
|
||||
}
|
||||
},
|
||||
// 登录重试次数,当连续请求登录接口返回失败次数超过这个次数,将不再重试登录
|
||||
reLoginLimit: 2,
|
||||
// 触发请求成功的条件
|
||||
successTrigger: function (res) {
|
||||
// 此处例子:当返回数据中的字段errcode等于0时,代表请求成功,其他情况都认为业务逻辑失败
|
||||
return res.errcode == 0;
|
||||
},
|
||||
// 成功之后返回数据;可不传
|
||||
successData: function (res) {
|
||||
// 此处例子:返回数据中的字段data为业务接受到的数据
|
||||
return res.data;
|
||||
},
|
||||
// 当CGI返回错误时,弹框提示的标题文字
|
||||
errorTitle: function(res) {
|
||||
// 此处例子:当返回数据中的字段errcode等于0x10040730时,错误弹框的标题是“温馨提示”,其他情况下则是“操作失败”
|
||||
return res.errcode == 0x10040730 ? '温馨提示' : '操作失败'
|
||||
},
|
||||
// 当CGI返回错误时,弹框提示的内容文字
|
||||
errorContent: function(res) {
|
||||
// 此处例子:返回数据中的字段msg为错误弹框的提示内容文字
|
||||
return res.msg ? res.msg : '服务可能存在异常,请稍后重试'
|
||||
},
|
||||
// 当出现CGI错误时,统一的回调函数,这里可以做统一的错误上报等处理
|
||||
errorCallback: function(obj, res) {
|
||||
// do some report
|
||||
},
|
||||
// 是否需要调用checkSession,验证小程序的登录态过期,可不传,默认为false
|
||||
doNotCheckSession: true,
|
||||
// 上报耗时的函数,name为上报名称,cost为耗时
|
||||
reportCGI: function(name, startTime, endTime, request) {
|
||||
//wx.reportAnalytics(name, {
|
||||
// time: endTime - startTime
|
||||
//});
|
||||
//request({
|
||||
// url: 'reportCGI',
|
||||
// data: {
|
||||
// name: name,
|
||||
// cost: endTime - startTime
|
||||
// },
|
||||
// fail: function() {
|
||||
//
|
||||
// }
|
||||
//})
|
||||
console.log(name + ":" + (endTime - startTime));
|
||||
},
|
||||
mockJson: require("../../mock.json"),
|
||||
globalData: function() {
|
||||
return {
|
||||
version: getApp().version
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = weRequest;
|
||||
9
package.json
Normal file
9
package.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "weRequest",
|
||||
"version": "1.0.0",
|
||||
"description": "本工具通过拓展小程序的wx.request,让开发者通过简单的配置,实现自动管理登录态等功能",
|
||||
"main": "./src/weRequest.js",
|
||||
"dependencies": {
|
||||
"webpack": "latest"
|
||||
}
|
||||
}
|
||||
17
src/loading.js
Normal file
17
src/loading.js
Normal file
@@ -0,0 +1,17 @@
|
||||
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
|
||||
}
|
||||
555
src/weRequest.js
Normal file
555
src/weRequest.js
Normal file
@@ -0,0 +1,555 @@
|
||||
const loading = require('./loading');
|
||||
|
||||
//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;
|
||||
|
||||
//global data
|
||||
var session = '';
|
||||
var sessionIsFresh = false;
|
||||
// 正在登录中,其他请求轮询稍后,避免重复调用登录接口
|
||||
var logining = false;
|
||||
// 正在查询session有效期中,避免重复调用接口
|
||||
var isCheckingSession = false;
|
||||
|
||||
function checkSession(callback, obj) {
|
||||
if (isCheckingSession) {
|
||||
setTimeout(function () {
|
||||
checkSession(callback, obj)
|
||||
}, 500);
|
||||
} else if (!sessionIsFresh && session) {
|
||||
isCheckingSession = true;
|
||||
obj.count++;
|
||||
// 如果还没检验过session是否有效,则需要检验一次
|
||||
obj._checkSessionStartTime = new Date().getTime();
|
||||
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);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 已经检验过了
|
||||
doLogin(callback, obj);
|
||||
}
|
||||
}
|
||||
|
||||
function doLogin(callback, obj) {
|
||||
if (session || obj.isLogin) {
|
||||
// 缓存中有session,或者是登录接口
|
||||
typeof callback === "function" && callback();
|
||||
} else if (logining) {
|
||||
// 正在登录中,请求轮询稍后,避免重复调用登录接口
|
||||
setTimeout(function () {
|
||||
doLogin(callback, obj);
|
||||
}, 500)
|
||||
} else {
|
||||
// 缓存中无session
|
||||
logining = true;
|
||||
obj.count++;
|
||||
// 记录调用wx.login前的时间戳
|
||||
obj._loginStartTime = new Date().getTime();
|
||||
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;
|
||||
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;
|
||||
},
|
||||
fail: codeToSession.fail || null
|
||||
});
|
||||
} else {
|
||||
fail(obj, res);
|
||||
console.error(res);
|
||||
// 登录失败,解除锁,防止死锁
|
||||
logining = false;
|
||||
}
|
||||
},
|
||||
fail: function (res) {
|
||||
fail(obj, res);
|
||||
console.error(res);
|
||||
// 登录失败,解除锁,防止死锁
|
||||
logining = false;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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 : (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 : (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;
|
||||
|
||||
try {
|
||||
session = wx.getStorageSync(sessionName) || '';
|
||||
} 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 {
|
||||
'urlPerfix': urlPerfix
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: init,
|
||||
request: requestWrapper,
|
||||
uploadFile: uploadFileWrapper,
|
||||
setSession: setSession,
|
||||
login: login,
|
||||
getSession: getSession,
|
||||
getConfig: getConfig
|
||||
};
|
||||
21
webpack.config.js
Normal file
21
webpack.config.js
Normal file
@@ -0,0 +1,21 @@
|
||||
var path = require('path');
|
||||
var webpack = require('webpack');
|
||||
var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
|
||||
|
||||
module.exports = {
|
||||
entry: './src/weRequest.js',
|
||||
output: {
|
||||
path: path.join(__dirname, 'build'),
|
||||
filename: 'weRequest.js',
|
||||
library: "weRequest",
|
||||
libraryTarget: "commonjs-module"
|
||||
},
|
||||
plugins: [
|
||||
new UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: false
|
||||
},
|
||||
except: []
|
||||
})
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user