本节思路
进入小程序的前端开发,验证前面构建的websocket服务能否和小程序联通。确认联通后再同步进行前后端的开发。

小程序的申请,开发者工具下载等准备工作就不具体阐述了。

打开开发者工具-选择小程序开发,
首先面临的问题就是用户登录状态的维护


小程序登录态维护实现流程

1.通过wx.login获取登录态。
2.通过登录返回的code发送给服务器,服务器用code换取session_key和openid后。
3.获取session_key后一般使用缓存框架保存登录态,服务端随机生成一串唯一字符串3rdSessionId为key,session_key为value组成键值对并存到缓存当中,缓存时间视情况自行决定。
4.将3rdSessionId返回给客户端
5.客户端将3rdSessionId缓存到localStorage中,后续接口从缓存中读取3rdSessionId,传递给服务器;服务器根据3rdSessionId来判断用户身份。
6.如果服务器根据3rdSessionId在缓存中查找是否存在session_key,如果存在正常执行;如果不存在小程序未登录,重新从第一步流程走。

以上流程是官方推荐使用的流程,并不是维护登录态的唯一途径。

另外,你也可以在小程序中使用wx.checkSession()检查登录态是否过期。如果过期重新调用wx.login接口。
session_key在微信服务器有效期是30天,建议服务端缓存session_key不超过30天。

登录时序图
login.png

而根据以上流程,将陷入多层嵌套回调,于是用promise优化流程

官方在 2017.03.28 更新日志 中 支持绝大部分的 ES6 API
其中包括了Promise

于是可以在微信小程序中使用Promise来处理登录流程

代码如下:
app.js

//app.js
App({
  onLaunch: function () {
    console.log("App生命周期函数——onLaunch函数");
  },
  checkSession:function(mysessionid) {
    return new Promise(function(resolve, reject) {
      wx.request({
        url: 'https://xxx.xxxxx.com/check.php',
        header: {
          sessionid:mysessionid
        },
        success: function(res) {
          console.log("检查sessionid是否有效")
          resolve(res.data)
        },
        fail: function(e) {
          reject(e)
        }
      })
    })
  },
  login:function() {
    return new Promise(function(resolve, reject) {
      wx.login({
        success: function (res0) {
          if (res0.code) {
            wx.request({
              url: 'https://xxx.xxxxx.com/login.php',
              data: {
                code: res0.code
              },
              success: function(res) {
                console.log("取得新的sessionid")
                var mysessionid = res.data
                wx.setStorageSync("mysessionid",mysessionid)
                resolve(mysessionid)
              },
              fail: function(e) {
                reject(e)
              }
            })
          }
        }
      })
    })
  },
  getWxUserInfo:function() {
    return new Promise(function(resolve, reject) {
      wx.getUserInfo({
        withCredentials: false,
        success: function(res) {
          console.log("取得新的userInfo")
          var userInfo = res.userInfo
          wx.setStorageSync("userInfo",userInfo)
          console.log("setUserInfo")
          resolve(userInfo)
        }
      })
    })
  },
  getUserInfo:function() {
    var that = this
    return new Promise(function(resolve, reject) {
      var mysessionid = wx.getStorageSync('mysessionid')
      if(mysessionid) {
        console.log("sessionid存在")
        that.checkSession(mysessionid).then(function(sessionFlag){
          if(sessionFlag == 1) {
            console.log("sessionid有效-直接取本地userInfo")
            var userInfo = wx.getStorageSync("userInfo")
            resolve(userInfo)
          } else {
            console.log("sessionid无效-取userInfo存到本地")
            that.login().then(function(){
              that.getWxUserInfo().then(function(userInfo){
                resolve(userInfo)
              })
            })
          }
        })
        
      } else {
        console.log("sessionid不存在,重新走登录流程")
        that.login().then(function(){
          that.getWxUserInfo().then(function(userInfo){
            resolve(userInfo)
          })
        })
      }
    })
  },
  globalData:{
    userInfo:null
  }
})

index.js

//index.js
//获取应用实例
var app = getApp()
Page({
  data: {
    userInfo: {}
  },
  onLoad: function () {
    console.log("Page onLoad函数");
    var that = this
    app.getUserInfo().then(function(userInfo){
      that.setData({
        userInfo:userInfo
      })
    })
  }
})

根据console的输出内容可以看到:
第一次登录时:

QQ图片20170414115524.png

刷新页面:

QQ图片20170414115618.png

当服务器上的缓存的sessionid失效时:
QQ图片20170414115739.png

check.php(在服务器查找sessionid,可以在redis里得到session_key,本例只判断了sessionid是否存在)

<?php

$post_data = $_POST;
$header = get_all_headers();

$sessionid = $header['sessionid'];

$host = '127.0.0.1';
$port = '6379';
$timeout = 0;

$redis = new Redis();
$redis->connect($host, $port, $timeout);
$session_content = $redis->get("miniappsession:".$sessionid);
if($session_content)
{
    echo 1;
} else {
    echo 0;
}

/**
 * 获取自定义的header数据
 */
function get_all_headers(){

    // 忽略获取的header数据
    $ignore = array('host','accept','content-length','content-type');

    $headers = array();

    foreach($_SERVER as $key=>$value){
        if(substr($key, 0, 5)==='HTTP_'){
            $key = substr($key, 5);
            $key = str_replace('_', ' ', $key);
            $key = str_replace(' ', '-', $key);
            $key = strtolower($key);

            if(!in_array($key, $ignore)){
                $headers[$key] = $value;
            }
        }
    }

    return $headers;

}

login.php(通过code,获取session_key和openid,并生成唯一sessionid,以sessionid为key,session_key和openid为value,存入redis,并返回sessionid给小程序)

<?php
$code = $_GET['code'];
define("APPID",'yourappid');
define("SECRET",'yoursecret');
$url = "https://api.weixin.qq.com/sns/jscode2session?appid=".APPID."&secret=".SECRET."&js_code=".$code."&grant_type=authorization_code";
$rs = curlGet($url);

$arr = json_decode($rs);

$str = randomFromDev(32);

$host = '127.0.0.1';
$port = '6379';
$timeout = 0;

$redis = new Redis();
$redis->connect($host, $port, $timeout);
$expires_time = 10*24*60*60;
$redis->setex("miniappsession:".$str,$expires_time,$arr->openid."|".$arr->session_key);

echo $str;


function randomFromDev($len)
{
    $fp = @fopen('/dev/urandom','rb');
    $result = '';
    if ($fp !== FALSE) {
        $result .= @fread($fp, $len);
        @fclose($fp);
    }
    else
    {
        trigger_error('Can not open /dev/urandom.');
    }
    $result = md5($result);
    return $result;
}

function curlGet($url, $method = 'get', $data = '')
{
    $ch = curl_init();
    $header = 'Accept-Charset: utf-8';
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $temp = curl_exec($ch);
    return $temp;
}

测试websocket联通

登录后取得的用户昵称、用户头像,根据本系列第二节内容
我们在小程序内构造一个room=1,username=用户昵称的websocket请求,看能否连接websocket服务器

index.js

//index.js
//获取应用实例
var app = getApp()
Page({
  data: {
    userInfo: {}
  },
  onLoad: function () {
    console.log("Page onLoad函数");
    var that = this
    app.getUserInfo().then(function(userInfo){
      that.setData({
        userInfo:userInfo
      })
      //websocket
      wx.connectSocket({
        url: 'wss://xx.xxxxx.com/echo'
      })
      wx.onSocketOpen(function(res) {
        console.log('WebSocket连接已打开!')
        var msg = new Object();
        msg.Room = '1';
        msg.Cmd = 'login';
        msg.User = userInfo.nickName;
        msg.Uuid = userInfo.nickName;
        var str = JSON.stringify(msg)
        wx.sendSocketMessage({
          data:str
        })
      })

    })
  }
})

查看Network里的请求,可以看到
QQ图片20170414145616.png

证明小程序与websocket服务已经连通了