[原创] 微信小程序和actioncable实现通信

2018-11-09 1293

参考资料

    actioncable

    微信小程序


在做微信小程序开发的时候需要用到websocket通信,由于小程序没有对actioncable.js的封装,因此需要根据小程序的websocket接口文档进行传递参数的处理。
首先看一下使用actioncable.js模块时的网络数据传输情况

频道订阅

频道订阅


订阅之后返回消息


发送消息

通过以上三张截图可以很明确的看到actioncable.js所做的事情,把rails服务器端需要的数据进行封装,按照固定的格式进行数据传输。
so!为了在小程序中实现同样的功能,只需要按照这个格式对传递的参数进行相应的封装即可。

创建服务器端代码

rails g channel qagame # 创建channel

为了让小程序可以访问到服务器端的数据,需要修改actioncable的配置
config/application.rb

# 添加action_cable配置,关闭跨域检测
config.action_cable.disable_request_forgery_protection = true



channels/application_cable/connection.rb

# frozen_string_literal: true

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user
    def connect
      openid = request.headers["Authorization"]
      self.current_user = openid
    end
  end
end


channels/qagame_channel.rb

class QagameChannel < ApplicationCable::Channel
  def subscribed
    stream_from "qagame_channel"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def join_challenge(data)
    p "获取客户端数据:#{data}"
    ActionCable.server.broadcast("qagame_channel", "登录成功") # 返回数据到客户端
  end
end



客户端

const app = getApp()

Page({
  ...
  onLoad: function () {
    // test websocket
    wx.connectSocket({
      url: 'ws://localhost:3000/cable',
      header: {
        'content-type': 'application/json',
        'Authorization': '授权信息'
      },
      method: "GET"
    });
    const id = JSON.stringify({ channel: "QagameChannel", id: '这个是订阅参数' });
    wx.onSocketOpen(function () {
      wx.sendSocketMessage({
        data: JSON.stringify({ command: "subscribe", identifier: id}),
      })
    })
    // 延时发送命令,等待客户端和服务器连接成功 
    setTimeout(function(){
      wx.sendSocketMessage({
        data: JSON.stringify({
          command: 'message',
          data: JSON.stringify({ 
            openid: 123,
            action: 'join_challenge'
            }),
          identifier: id
        }),
      })
    }, 5000)
    wx.onSocketMessage(function(res){
      console.log(res);
    })
    wx.onSocketClose(function(res){
      console.log("连接已关闭")
      console.log(res)
    })
    wx.onSocketError(function(err){
      console.log("打开连接失败")
      console.log(err)
    })
  },
  ...
})



附上自己定义的Actioncable

const formatTime = date => {
  const year = date.getFullYear()
  const month = date.getMonth() + 1
  const day = date.getDate()
  const hour = date.getHours()
  const minute = date.getMinutes()
  const second = date.getSeconds()

  return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}

const formatNumber = n => {
  n = n.toString()
  return n[1] ? n : '0' + n
}

// 
class Actioncable {
  /**
   * url ws服务器地址 http://localhost:3000/cable
   * channel  服务端channel名字如:GameChannel
   * id       可选参数,
   * cb       接受返回数据的回调函数
   */
  constructor(url, channel, id, cb) {
    wx.connectSocket({
      url,
      header: {
        'content-type': 'application/json',
        'Authorization': '授权信息'
      },
      method: "GET"
    })
    this.identifier = JSON.stringify({ channel, id });
    wx.onSocketOpen(() => {
      wx.sendSocketMessage({
        data: JSON.stringify({ command: "subscribe", identifier: this.identifier }),
      })
    })
    wx.onSocketMessage((res) => {
      console.log(res) // 服务器端返回数据
      res = JSON.parse(res.data);
      if (JSON.stringify(res.identifier) == JSON.stringify(this.identifier)){
        if(res.message) {
          cb(res)
        }
      }
    })
    wx.onSocketClose(function (res) {
      console.log("连接已关闭")
      console.log(res)
    })
    wx.onSocketError(function (err) {
      console.log("打开连接失败")
      console.log(err)
    })
  }

  /**
   * 发送消息
   *  action  channel中定义的action名字
   *  data    传递到action的数据
   */
  sendMessage(action, data) {
    var info = {}
    data.action = action
    info.command = 'message'
    info.identifier = this.identifier
    info.data = JSON.stringify(data)
    wx.sendSocketMessage({
      data: JSON.stringify(info),
    })
  }
}

module.exports = {
  formatTime: formatTime,
  Actioncable
}



调用例子

const ws = new Actioncable('ws://localhost:3000/cable', 'QagameChannel', 1211, function(res){
      console.log(res);
    })
setTimeout(() => ws.sendMessage('join_challenge', { openid: 123, nick_name: 'Timer' }), 5000);