解决微信小程序无法建立持久化连接的两种方案

问题出现的场景 因为最近在做一个小程序的项目,在建立前后端连接的过程中,发现了一个非常让人奇怪的现象:本身小程序是通过调用wx.https()方法来发起http请求的,但是你会发现,如果你在后端将值保存到了request或者session中,这个值你再次调用的时候就不见了!取值的时候会出现NullPointerException,或者你在使用了Spring Security、Shiro这样的权限校验框架以后,会发现登录后出现了权限丢失的问题。
这到底是为什么呢?根据我的经验,我怀疑是session发生了变化,为了证明这一点,我通过观察两次请求的session是否为同一个得到了最终的结论。
小程序发起请求的代码是这样的:

wx.request({ url: serverUrl + '/login', data: { username: username, password: password }, header: { 'content-type': 'application/json' // 默认值 }, success:function(){ console.log("登录成功"); } })

通过观察小程序的调试器的network,发现果然两次请求的session发生了变化。我猜想是因为小程序没有保存连接的Cookie,果然,通过查阅资料,发现小程序是无法建立持久化的连接的,所以就不会主动保存Cookie。其实这也在一定程度上体现了小程序用完即走的特性。

那么解决的办法也就很清楚了:将首次请求返回的response中的Cookie保存下来,然后下次发送请求时将此值放到请求头中。
解决问题的方案 实际上,我们在使用浏览器访问某个网站的时候,浏览器会主动的将Cookie中的JSESSIONID存入浏览器的Cookie缓存中,这样下次再请求的时候,就会自动将这个JESSIONID加到请求头中,这样对方的服务器就能够识别我们,从而达到建立一种“持久化”连接的状态。实际上我们知道,这种连接并不是真正持久的,都是需要数据的时候再次建立连接,然后断开。整个过程如图所示:

方案一:将JSESSIONID放入全局变量
其实最简单的办法就是,在第一次请求完成后,就将JSESSIONID放到全局的SESSIONID中,然后每次请求的时候在header中这样写:
header: { 'content-type': 'application/json', // 默认 'Cookie': app.globalData.JSESSIONID },

【解决微信小程序无法建立持久化连接的两种方案】这就能够做到SESSION不丢失,但是这样很麻烦,如果后期代码编写不当,这数据很容易丢失,所以我们并不推荐使用这种方法。
方案二:将JESSIONID放入缓存
微信为我们提供了几个方法,这几个方法类似于Java中的session.setAttrabiute()方法,都是
wx.request({ url: serverUrl + '/login', data: { }, header: { 'content-type': 'application/json' // 默认值 }, success:function(res){ //取出session var cookie = res.header["Set-Cookie"]; if (undefined != cookie) { var sessionPos; var rememberMe; if ((sessionPos = cookie.indexOf("JSESSIONID=")) != -1) { //每次请求成功都将sessionId放入缓存 wx.setStorageSync("JSESSIONID", cookie.substring(sessionPos, 48)); } if ((sessionPos = cookie.indexOf("rememberMe=")) != -1) { //设置rememberme wx.setStorageSync("rememberMe", cookie.substring(sessionPos + 78, 712)); } } } })

这段代码做的就是从Cookie的字符串中取到SessionId和RememberMe,可能因为版本不同等问题造成长度不一致,这个自己计算一下就行了。
然后就可以在小程序中建立“持久化的连接”了。
建个Util 其实像上面那样做可以,但是如果遇到session失效的情况,原有的写入的session就没了,还有就是代码复用的问题,像上面那样做,可能你每次请求的时候都要自己写Cookie之类的代码,这就很麻烦了,最好的办法就是建一个工具类,其中包括持久化连接的代码。
/** * 公共微信https请求封装 * @param url * @param type * @param data * @param callBack 回调函数 */ function https(url, type, data, callBack, header) { if (!data.isHideLoad) { wx.showLoading({ title: '加载中', }) } wx.showNavigationBarLoading(); wx.request({ url: url, method: type, data: data, header: header ? header : ({ "Content-Type": "application/json", "Cookie": wx.getStorageSync('JSESSIONID') + wx.getStorageSync('rememberMe') }), success: function(res) { //取出session var cookie = res.header["Set-Cookie"]; if (undefined != cookie) { var sessionPos; var rememberMe; if ((sessionPos = cookie.indexOf("JSESSIONID=")) != -1) { //每次请求成功都将sessionId放入缓存 wx.setStorageSync("JSESSIONID", cookie.substring(sessionPos, 48)); } if ((sessionPos = cookie.indexOf("rememberMe=")) != -1) { //设置rememberme wx.setStorageSync("rememberMe", cookie.substring(sessionPos + 78, 712)); } } callBack(res.data); }, fail: function(error) { // showToast("登录过期,请重新登录", "none"); wx.reLaunch({ url: '../../account/login', success: function() { wx.showToast({ title: '登录过期,请重新登录', icon: 'none' }) } }) }, complete: function(res) { if (res.status === 400) { showToast("家校通请求未授权"); } wx.hideLoading(); wx.stopPullDownRefresh(); wx.hideNavigationBarLoading(); } }) }

需要用到请求的时候,就先导入util,然后调用util.https(params…)。
如果你发现无法调用这个方法,那么问题是你没有将https这个方法暴露出去,在util方法的最后,写上这样一段代码:
module.exports = { https: https }

结语 文章到这里就结束了,如果你喜欢我的文章,请多多点赞、转发。
如果您想要了解JAVA、JAVAWEB、小程序、数据库、干货……等深度文章以及学习资源(后台回复java可见,无套路),欢迎关注我的微信公众号:最高权限比特流。

    推荐阅读