-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
小程序 Page 获取登录态,异步满天飞? #11
Comments
大致明白了,但在 session 过期时候的并发 request 会不会导致多次请求 比如某个 Page 里有两个 request 请求,由于 session 过期都调用了 |
@kala888 ,我想问一下wepy中怎么使用saga,我在saga中的函数都没有生效但是也不会报错 |
@kala888 这样子并不能解决问题,正如@ryancui- 的问题一样,说下我的解决方案: import wx from './wx.js'
import { hideWxLoading, showModal, getStorageSyncLoginResult } from './index'
const makeOptions = (url, options) => {
const defaultoptions = {
url: undefined,
method: 'GET',
qs: undefined,
body: undefined,
headers: undefined,
type: 'json',
contentType: 'application/json',
crossOrigin: true,
credentials: undefined,
customToken: false,
showFailMsg: true,
}
let thisoptions = {}
if (!options) {
thisoptions = { url }
} else {
thisoptions = options
if (url) {
thisoptions.url = url
}
}
thisoptions = Object.assign({}, defaultoptions, thisoptions)
return thisoptions
}
const addQs = (url, qs) => {
let queryString = ''
let newUrl = url
if (qs && typeof qs === 'object') {
/* eslint no-restricted-syntax: 0 */
for (const k of Object.keys(qs)) {
queryString += `&${k}=${qs[k]}`
}
if (queryString.length > 0) {
if (url.split('?').length < 2) {
queryString = queryString.substring(1)
} else if (url.split('?')[1].length === 0) {
queryString = queryString.substring(1)
}
}
if (url.indexOf('?') === -1) {
newUrl = `${url}?${queryString}`
} else {
newUrl = `${url}${queryString}`
}
}
return newUrl
}
let isRefreshing = false
/*存储请求的数组*/
let refreshSubscribers = []
/*将所有的请求都push到数组中,其实数组是[function(token){}, function(token){},...]*/
function subscribeTokenRefresh(cb) {
refreshSubscribers.push(cb);
}
/*数组中的请求得到新的token之后自执行,用新的token去请求数据*/
function onRrefreshed() {
console.log(refreshSubscribers)
refreshSubscribers.map(cb => cb());
}
const request = (url, options) => {
const opts = makeOptions(url, options)
const { method, body, headers, qs, type, contentType } = opts
let requestUrl = opts.url
if (qs) requestUrl = addQs(requestUrl, qs)
let header = headers
if ((!headers || !headers['content-type']) && contentType) {
header = Object.assign({}, headers, { 'content-type': contentType })
}
if (opts.customToken) {
const res = getStorageSyncLoginResult()
header = {
...header,
'X-Custom-Token': res && res.token
}
}
return new Promise((resolve, reject) => {
wx.request({
url: requestUrl,
method,
data: body,
header,
dataType: type
})
.then(response => {
// getApp().log(JSON.stringify(response));
// 业务数据异常
if (response.statusCode < 200 || response.statusCode >= 300 || (response.data.code !== 0 && (response.data.code < 200 || response.data.code >= 300))) {
let errors = {
error: -1,
request: url,
errorMessage: '系统异常,请查看response',
response
}
if (response.data && typeof response.data === 'object') {
errors = Object.assign({}, errors, response.data)
}
if (response.data.code === 401) {
subscribeTokenRefresh(() => {
resolve(request(url, options))
})
if (!isRefreshing) {
isRefreshing = true
//showModal('登录已过期,请重新尝试')
wx.app.login().then(async userInfo => {
if (userInfo) {
try {
isRefreshing = false
onRrefreshed()
} catch (e) {
reject(e)
}
} else {
reject()
}
}).catch(e => {
console.log(222, e)
reject(e)
})
}
} else {
opts.showFailMsg && showModal(errors.detail || '数据加载失败')
hideWxLoading()
reject(errors)
}
} else {
// 正确返回
resolve(response.data)
}
})
.catch(err => {
// getApp().log(JSON.stringify(err));
reject({
error: -1,
message: '系统异常,请查看response',
err,
request: url
})
})
})
}
export default request |
@ldwonday 想请问下用这个保存请求的方法, 页面 page 中有个 A 方法,请求后,服务器响应未登录。保存 A 请求,重新登录后 A 也重新执行了。 但是无法在 page 中获取到 A 重新执行返回的数据。这个要怎么解决 ? |
@jingchaocheng 多输出些日志看看吧! |
登录逻辑与登录态
根据小程序文档与官方 Demo 的例子,微信登录逻辑在
app.js
的App.onLaunch
中实现。另外还需要自行与第三方服务器独立进行一套授权机制,这里我使用了 JWT,在换取openid
的接口中返回。整个
app.js
的登录逻辑大致如下,进入小程序后检查微信登录态是否过期,过期了重新调起微信登录并换取新的 JWT 作为与第三方服务器通信的凭证;未过期则从localStorage
中拉取 token 与用户信息。业务页面获取数据
在 Page 中,不可避免地需要调用第三方服务器的接口获取数据,此时需要使用 JWT 进行授权。当进入页面时就需要拉数据时,请求放在了
Page.onLoad
方法中。测试时却发现有时候页面有数据,有时候页面无数据。进一步查看请求便发现有时
token
有值,有时token
拿不到值。全特么是异步
仔细想想就能够明白,原因在于
App.globalData.token
的设置是异步,虽然App.onLaunch
与Page.onLoad
有明确的时序性(文档上没有说明,通过测试可发现Page.onLoad
总在App.onLaunch
后执行),但「设置 token」与「请求业务接口」两个步骤不能保证其时序性,因此会出现偶尔请求失败的情况。既然无法保证时序,第一个想法就是 EventBus 了,这种很 free 的东西,用起来功能强大但也很危险。
给
App
添加了一个自己实现的简单全局EventBus
,并在checkSession.success
和重新登录设置好token
后都广播一个登陆成功事件。这时候 Page 就变成这样了。由于不能保证时序性,如果在
onLoad
方法只订阅事件就会出现:Page.onLoad
方法,订阅事件结果没有调用业务接口,因此需要处理两种情况。
每个页面都要这样?
如果每个业务 Page 都需要在初始化时调用业务接口,都需要写一套这样的逻辑?有没有什么更好的解决办法?
更甚之,就算是一些事件绑定,理论上也不能保证事件调用时已经完成登录,难道每次调用业务接口都要写一套这样的判断?
这个问题的出现根本在于登录态的获取、token 的设置是异步的,而这个异步与业务接口调用需要同步(必须先有 token 才能调用),能否把这个过程通过小程序框架固定成同步?如
Page.onLoad
方法的调用必须在 App 的某个钩子之后?如果有更好的解决方法,请不吝赐教,谢谢。
The text was updated successfully, but these errors were encountered: