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

问题出现的场景

因为最近在做一个小程序的项目,在建立前后端连接的过程中,发现了一个非常让人奇怪的现象:本身小程序是通过调用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()方法,都是<key,value>的存储形式,调用的时候使用get…就可以了。
我们首先要做的就是将第一次请求成功的Cookie中的JSESSIONID写入缓存,如果用的是Shiro一类的框架,还要写入rememberMe到缓存中。
方法如下:

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可见,无套路),欢迎关注我的微信公众号:最高权限比特流

发表评论

电子邮件地址不会被公开。 必填项已用*标注