最近在整理我的第一个小程序项目,发现了一些Bug。在这里记录解决过程,一方面梳理知识,另一方面帮大家排排雷。
在小程序开发时,我们常常会调用wx.login
和wx.getUserProfile
来让用户授权并收集用户的openid、用户名、头像等信息。
官方文档对这两个接口的描述如下
wx.login(Object object)
(不支持以 Promise 风格 调用)调用接口获取登录凭证(code)。通过凭证进而换取用户登录态信息,包括用户在当前小程序的唯一标识(openid)、微信开放平台帐号下的唯一标识(unionid,若当前小程序已绑定到微信开放平台帐号)及本次登录的会话密钥(session_key)等。用户数据的加解密通讯需要依赖会话密钥完成。
wx.getUserProfile(Object object)
(支持以 Promise 风格 调用)获取用户信息。页面产生点击事件(例如 button 上 bindtap 的回调中)后才可调用,每次请求都会弹出授权窗口,用户同意后返回 userInfo。
这样看是不是感觉两者没有什么联系,先后顺序不影响,我最开始也是这样觉得的,代码如下
getInfo(){
wx.getUserProfile({
desc: '登录',
success: (res) => {
console.log('getUserProfile', res)
let changeData = {}, // 需要setData的数据, 一次setData的操作
userInfo = {
username: res.userInfo.nickName,
userphoto: res.userInfo.avatarUrl
}
changeData.hasUserInfo = true
wx.login({
//成功放回
success: (res) => {
let code = res.code
wx.request({
url: `https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&${this.data.my.lock}=${this.data.my.key}&js_code=${code}&grant_type=authorization_code`,
success: (res) => {
userInfo.openid = res.data.openid
changeData.userInfo = userInfo
this.setData(changeData)
app.globalData.openid = res.data.openid;
wx.setStorageSync('userInfo', userInfo)
db.collection('users')
.where({
openid:res.data.openid
})
.get().then((res)=>{
if(res.data.length===0){
db.collection('users').add({
data: {
...this.data.userInfo
}
})
db.collection('userData').add({
data:{
openid:res.data.openid,
late:0,
notCome:0,
cancel:0,
leaveEarly:0,
signIn:0
}
})
}
})
}
})
}
})
}
})
},
在微信开发者工具和真机调试时可以成功,但是预览
就无法授权成功。
最开始以为是微信小程序的Bug,用朋友手机试了一下体验版,还是无法授权成功。,那应该是我的问题,尝试使用手机调试,发现调试的时候又可以授权成功了,在日志里也可以打印并获取到的userInfo
。
我不明白,但是我一整个震惊住了!又看了一遍代码,感觉是wx.login 和 wx.getUserProfile的问题,上网查了一下,发现还真是!!!
也就是说,我们必须先调用wx.login拿到最新的code,否则容易解密失败。即保证wx.login是比wx.getUserProfile先完成。直接把wx.getUserProfile放在wx.login的回调里不就好了嘛?
NO!!! wx.getUserProfile
在页面产生点击事件(例如 button 上 bindtap 的回调中)后才可调用,不可放回调里。
网上的promise.all的方式处理,我没试但是我感觉是不行的。all只是把login和getinfo同时运行罢了,但是这两个实际都是异步,到底哪个先触发实际是没法确定的。
找了很久的资料没有答案,最后问了两个专门做微信小程序开发的前辈,发现大家都是这样解决的!!!
因为code有时效,所以一般都是进入页面的时候直接调用wx.login
获取code,然后用code换取openid,存储openid。当用户点击授权按钮时,我们可以直接调用wx.getUserProfile。
代码修改如下:
var openid=''; // 不渲染到页面所以不要存在data中
onLoad(options) {
var t = this
var userInfo = wx.getStorageSync('userInfo')
if(userInfo){
this.setData({
userInfo:userInfo,
hasUserInfo:true,
})
}else{
db.collection('importantKey')
.get()
.then((res)=>{
var my = {...res.data[0]} // 密钥不能以明文形式写,所以存在数据库中获取
wx.login({
//成功放回
success: (res) => {
let code = res.code
wx.request({
url: `https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&${my.lock}=${my.key}&js_code=${code}&grant_type=authorization_code`,
success: (res) => {
openid = res.data.openid
}
})
}
})
})
}
},
getInfo(){
var changeData = {}// 需要setData的数据, 一次setData的操作,优化性能
wx.getUserProfile({
desc: '登录',
success: (res) => {
var userInfo = {
username: res.userInfo.nickName,
userphoto: res.userInfo.avatarUrl,
openid:openid
}
changeData.hasUserInfo = true
changeData.userInfo = userInfo
this.setData(changeData) // 一次性setData
wx.setStorageSync('userInfo', userInfo)
db.collection('users')
.where({
openid:userInfo.openid
})
.get().then((res)=>{
console.log('openid',openid);
if(res.data.length===0){
db.collection('users').add({
data: {
...userInfo
}
})
db.collection('userData').add({
data:{
openid:userInfo.openid,
late:0,
notCome:0,
cancel:0,
leaveEarly:0,
signIn:0
}
})
}
})
}
})
},
你以为这样就对了吗?错!!!
如果直接这样使用,你就会发现微信小程序的另外一个新坑。
就是在小程序开发者工具直接测试,使用真机调试,都没有任何问题,但是一旦上传代码,使用小程序的体验版测试的话,就拿不到openid,奇怪的是,如果在体验版开启调试模式,又可以拿到。也就是说开发环境和生产环境在代码相同的情况下,体现的效果不一样。
经过很长时间的查找资料,获取openid不能直接在微信客户端来获取,因为api.weixin.qq.com
写到域名配置里面是无效的,必须后端调用。应该改用后端来获取openid然后再返回给前端。
如果不是云开发,在onLoad
的时候直接调用wx.login
获取code,然后传给后端,让后端获取openid
返回给前端
如果是云开发的话,可以直接创建一个云函数getopenid
。
index.js
代码如下
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
return {
event,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
}
}
在授权页面中onLoad
函数函数调用
var openid=''; // 不渲染到页面所以不要存在data中
onLoad(options) {
var t = this
var userInfo = wx.getStorageSync('userInfo')
if(userInfo){
this.setData({
userInfo:userInfo,
hasUserInfo:true,
})
}else{
wx.cloud.callFunction({ // 调用云函数获取openid
name: 'getopenid',
complete: res => {
openid = res.result.openid;
}
})
}
},
如果觉得本篇文章对你有帮助,记得点赞、收藏~