记录在微信公众号开发过程中遇到的一些问题以及解决方法
一、开发环境
- Vue 2.5.2
- weixin-js-sdk 1.4.0-test
二、问题与解决方法
1. 微信接口报错63002:invalid signature,即签名错误
1.1 问题分析
微信提供了专门的签名算法用于生成签名,一般由后端调用微信的接口生成签名,签名错误的原因一般有两大类:一是传入签名算法的参数错误;二是传入wx.config(微信提供的用于注入权限验证配置的接口)接口的参数错误。
1.2 解决方法
1.2.1 排查是否是传入签名算法的参数错误
1)参数一:noncestr(随机字符串);
2)参数二:有效的jsapi_ticket,这个参数微信也提供了专门的接口生成,正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,因此必须在自己的服务全局缓存jsapi_ticket;
3)参数三:timestamp(时间戳),一定要确保服务器的时间是正确的,不然生成出来的签名是无效的;
4)参数四:当前网页的url,一定要注意传入接口的url不包含#及其后面部分,且必须是动态获取的。
1.2.2 排查是否是传入wx.config接口的参数错误
1)参数一:appId(公众号的唯一标识),确保传入的appId是当前微信公众号的appId,在开发过程中,会经常弄混淆;
2)参数二:timestamp(生成签名的时间戳),一般直接由后端接口返回,确保与传入签名算法中的timestamp一致;
3)参数三:nonceStr(生成签名的随机串),一般直接由后端接口返回,注意这个字段是驼峰式的,不要写错,与传入签名算法中的字段名称大小写有区分;
4)参数四:signature(签名),后端生成的签名,确保签名正确,可以使用微信提供的页面工具来校验。
1.2.3 其他可能引起错误的原因
1)确保JS接口安全域名配置正确,JS接口安全域名不需要添加http(s)://,直接放域名或者ip地址;
2)确保网页账号与JS接口安全域名一样
2. 多图片上传时调用wx.getLocalImgData报错script error
2.1 背景
在项目中使用微信的接口完成上传图片的功能,大致的实现流程如下:
- 调用wx.chooseImage拍照或者从手机相册选择图片,该接口会返回选定照片的本地ID列表localIds,安卓可以使用localId作为img标签的src属性显示图片,但是IOS无法显示,因此不建议直接使用localId来显示图片;
- 使用wx.getLocalImgData将选定的照片转换为base64数据,由于图片允许多选,因此需要多次调用该接口;
- 调用项目接口,将base64数据上传到服务器中,利用接口返回的图片在服务器上的地址,来回显图片。
2.2 存在的问题
- 问题一:wx.getLocalImgData接口单次调用成功,循环调用报错script error;
- 问题二:利用本地接口将wx.getLocalImgData生成的base64上传至服务器并返回图片在服务器的地址,从而回显图片,安卓可以成功回显,但是IOS上不能。
代码如下:
// 选择图片
chooseImage() {
let that = this
wx.chooseImage({
count: that.imgMaxNum, // 允许上传的图片个数
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: function (res) {
// localIds是一个数组
const localIds = res.localIds;
// 循环数组,将每个图片都转换为base64
localIds.forEach(localId => {
that.changeImgToBase64(localId);
});
}
});
},
// 将图片转换为base64
changeImgToBase64(localId) {
let that = this;
wx.getLocalImgData({
localId: localId,
success: function (res) {
// localData是图片的base64数据
let localData = res.localData;
// 将base64上传到服务器
that.uploadImage(localData);
}
});
},
// 将图片上传到服务器
uploadImage(base64Data) {
// 其余代码省略
// photos保存的是图片在服务器上的地址
this.photos.push(path);
}
2.3 问题分析
猜测:类似于这种文件的读写操作,应该是串行的,各个任务按顺序执行,完成一个之后才能进行下一个。循环调用wx.getLocalImgData,类似于在循环里添加了一个异步请求,因此可以通过在回调函数中递归wx.getLocalImgData来达到同步执行。
wx.getLocalImgData接口可以将图片转换为base64,但是转换后的base64在安卓和IOS上返回的是不一样的,
IOS中返回的base64会加上"data:image/jpg;base64,"头部,而安卓不会,我所在的项目后端提供的接口的入参不需要这个头部,因此IOS需要将头部截取掉。
2.4 解决方法
// 选择图片
chooseImage() {
let that = this
wx.chooseImage({
count: that.imgMaxNum, // 允许上传的图片个数
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: function (res) {
// 将返回的localIds全局保存
that.localIds = res.localIds
that.changeImgToBase64(that.localIds.shift());
}
});
},
// 将图片转换为base64
changeImgToBase64(localId) {
let that = this;
wx.getLocalImgData({
localId: localId,
success: function (res) {
let localData = res.localData;
const base = 'data:image/jpg;base64,';
// IOS下截取头部
if (localData.indexOf(base) === 0) {
localData = localData.substring(base.length, localData.length);
}
that.uploadImage(localData);
// 递归调用
if (that.localIds.length > 0) {
that.changeImgToBase64(that.localIds.shift());
}
}
});
},
// 将图片上传到服务器
uploadImage(base64Data) {
// 其余代码省略
// photos保存的是图片在服务器上的地址
this.photos.push(path);
}
3. SPA项目:从微信菜单跳转到子页面时,页面无法正确跳转到子页面,而是跳转到首页
3.1 问题分析
Vue Router默认使用hash模式,因此URL是这样的:http://yoursite.com/#/hello。由于签名算法中的url参数的值不包含#及其后面部分,因此在获取url的时候使用了location.href.split("#")[0],猜测微信的菜单跳转也是一样的,因此每次跳转都只能跳转到首页。
3.2 解决方法
3.2.1 修改Vue Router为history模式
使用history模式后,链接中不再包含#,而是变成了http://yoursite.com/hello。
但是使用这种方法,build后页面变成了空白的,看了官方文档后才知道history模式需要后台配置支持,由于之前没有用过这种模式,所以需要花更多的时间去调研,时间成本较大,因此就没有考虑这个方案了。
3.2.2 将链接中的#编码
这个方法比较巧妙,既然#以及#后面的内容会被忽略,那就把#编码一下,链接中没有直接出现#,而且浏览器也能解析。
解析后的地址为:http://yoursite.com%2F%23%2Fhello