微信小程序连接阿里云物联网平台操控设备(IOT)二

微信小程序连接阿里云物联网平台操控设备(IOT)二


上一篇我们成功将设备上云,这一篇我们来了解如何通过阿里云物联网平台的云端API来控制设备。阿里云物联网平台提供云端管理产品、设备、分组、Topic、规则、设备影子等API接口,和从云端发布消息的API接口。使用云端SDK,向API的服务端地址发送HTTPS/HTTP GET或POST请求,并按照API接口说明,在请求中加入相应请求参数来调用API。物联网平台根据请求的处理情况,返回处理结果。

微信小程序开发(一)

请求API公共参数

首先我们需要先了解一下访问阿里云物联网平台云端API,需要哪些公共参数,我们需要解决哪些问题。
1.公共参数

其中大部分很好生成,需要注意的只有2个。一个是需要生成唯一随机数,有很多uuid的生成方法。另一个就是签名。我们需要具体了解一下阿里的签名机制。

2.签名机制
签名时,需在控制台 AccessKey 管理页面查看您的阿里云账号的AccessKeyId和AccessKeySecret,这个之前在上一篇已经让大家准备好了,然后进行对称加密。其中,AccessKeyId用于标识访问者身份;AccessKeySecret是用于加密签名字符串和服务器端验证签名字符串的密钥,必须严格保密。
签名机制篇幅较大,具体签名方法请大家看阿里云官方文档
其中计算签名需要计算HMAC值还需要SHA1算法,最后需要Base64编码。我选择使用cryptoJs。但是找到的一些版本对微信小程序的支持都不尽人意。后来发现有一版对微信小程序支持比较好。但是有一些问题存在,比如不支持Base64编码返回结果,我稍微做了一些改动。如需下载此版本cryptojs资源,请点击此链接

uuid帮助工具类

用于生成SignatureNonce唯一随机数,这里提供一个uuid生成工具模块。

var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');

const uuid = (len, radix) => {
  var chars = CHARS,
    uuid = [],
    i;
  radix = radix || chars.length;

  if (len) {
    for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
  } else {
    var r;

    uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
    uuid[14] = '4';

    for (i = 0; i < 36; i++) {
      if (!uuid[i]) {
        r = 0 | Math.random() * 16;
        uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
      }
    }
  }

  return uuid.join('');
};

const uuidFast = () => {
  var chars = CHARS,
    uuid = new Array(36),
    rnd = 0,
    r;
  for (var i = 0; i < 36; i++) {
    if (i == 8 || i == 13 || i == 18 || i == 23) {
      uuid[i] = '-';
    } else if (i == 14) {
      uuid[i] = '4';
    } else {
      if (rnd <= 0x02) rnd = 0x2000000 + (Math.random() * 0x1000000) | 0;
      r = rnd & 0xf;
      rnd = rnd >> 4;
      uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
    }
  }
  return uuid.join('');
};

const uuidCompact = () => {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random() * 16 | 0,
      v = c == 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
};

module.exports = {
  uuid: uuid,
  uuidFast: uuidFast,
  uuidCompact: uuidCompact
}

创建项目

1.创建项目,导入所需工具模块

2.因为是通过访问云端API控制设备,登录微信公众平台小程序配置request合法域名。目前阿里云物联网平台服务器地域国内只有上海站,所以暂时Region只写cn-shanghai。

编写sdk

除了研究官方给的签名机制,还参考了官方给的nodejs-sdk。
1.引用模块,因为我将一些信息放在app的全局变量globalData中,所以除了刚才的uuid和cryptojs还要引用app。

var crypto = require("/cryptojs-master/cryptojs.js").Crypto
var uuid = require("/uuid.js")
const app = getApp()


2.按照公共参数的描述和签名机制编写相关方法。

//格式化数字
const formatNumber = n => {
  n = n.toString()
  return n[1] ? n : '0' + n
}

//首字母大写
const firstLetterUpper = str => {
  return str.slice(0, 1).toUpperCase() + str.slice(1);
}

//格式化参数
const formatParams = params => {
  var keys = Object.keys(params)
  var newParams = {}
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i]
    newParams[firstLetterUpper(key)] = params[key]
  }
  return newParams;
}

//参数排序
const sortParams = params => {
  var keys = Object.keys(params).sort()
  var newParams = {}
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i]
    newParams[key] = params[key]
  }
  return newParams;
}

//生成规定时间格式
const timestamp = () => {
  var date = new Date();
  var YYYY = date.getUTCFullYear();
  var MM = formatNumber(date.getUTCMonth() + 1);
  var DD = formatNumber(date.getUTCDate());
  var HH = formatNumber(date.getUTCHours());
  var mm = formatNumber(date.getUTCMinutes());
  var ss = formatNumber(date.getUTCSeconds());
  // 删除掉毫秒部分
  return `${YYYY}-${MM}-${DD}T${HH}:${mm}:${ss}Z`;
}

//url编码
const encode = (str) => {
  var result = encodeURIComponent(str);

  return result.replace(/\!/g, '%21')
    .replace(/\'/g, '%27')
    .replace(/\(/g, '%28')
    .replace(/\)/g, '%29')
    .replace(/\*/g, '%2A');
}

//将数组参数格式化成url传参方式
const replaceRepeatList = (target, key, repeat) => {
  for (var i = 0; i < repeat.length; i++) {
    var item = repeat[i];

    if (item && typeof item === 'object') {
      const keys = Object.keys(item);
      for (var j = 0; j < keys.length; j++) {
        target[`${key}.${i + 1}.${keys[j]}`] = item[keys[j]];
      }
    } else {
      target[`${key}.${i + 1}`] = item;
    }
  }
}

//将所有数组参数展开
const flatParams = (params) => {
  var target = {};
  var keys = Object.keys(params);
  for (let i = 0; i < keys.length; i++) {
    var key = keys[i];
    var value = params[key];
    if (Array.isArray(value)) {
      replaceRepeatList(target, key, value);
    } else {
      target[key] = value;
    }
  }
  return target;
}

//按传参名首字母顺序将所有参数以特定格式放进数组中
const normalize = (params) => {
  var list = [];
  var flated = flatParams(params);
  var keys = Object.keys(flated).sort();
  for (let i = 0; i < keys.length; i++) {
    var key = keys[i];
    var value = flated[key];
    list.push([encode(key), encode(value)]); 
  }
  return list;
}

//将数组格式的参数转化为url传参格式
const canonicalize = (normalized) => {
  var fields = [];
  for (var i = 0; i < normalized.length; i++) {
    var [key, value] = normalized[i];
    fields.push(key + '=' + value);
  }
  return fields.join('&');
}

//构建公共参数
const _buildParams = () => {
  var defaultParams = {
    Format: 'JSON',
    SignatureMethod: 'HMAC-SHA1',
    SignatureNonce: uuid.uuid(),
    SignatureVersion: '1.0',
    Timestamp: timestamp(),
    AccessKeyId: app.globalData.ai,
    Version: app.globalData.apiVersion,
    RegionId: "cn-shanghai"
  };
  return defaultParams;
}

3.封装小程序request,请求时加入公共参数

const request = (params, opts, success, fail, complete) => {
  params = formatParams(params)
  params.Action = firstLetterUpper(params.Action)
  var defaultParams = _buildParams()
  params = Object.assign(defaultParams, params)

  var method = (opts.method || 'GET').toUpperCase()
  var normalized = normalize(params)
  var canonicalized = canonicalize(normalized)

  var stringToSign = `${method}&${encode('/')}&${encode(canonicalized)}`

  const key = app.globalData.as + '&'

  var signature = crypto.HMAC(crypto.SHA1, stringToSign, key, {
    asBase64: true
  })

  normalized.push(['Signature', encode(signature)])

  const url = method === 'POST' ? `${app.globalData.endpoint}/` : `${app.globalData.endpoint}/?${canonicalize(normalized)}`
  if (method === 'POST') {
    opts.headers = opts.headers || {};
    opts.headers['content-type'] = 'application/x-www-form-urlencoded'
    opts.data = canonicalize(normalized)
  }

  wx.request({
    url: url,
    data: opts.data ? opts.data : {},
    header: opts.headers,
    method: method,
    dataType: 'json',
    responseType: 'text',
    success: function(res) {
      if (typeof success === 'function')
        success(res)
      else
        console.log("success is not a function")
    },
    fail: function(res) {
      if (typeof fail === 'function')
        fail(res)
      else
        console.log("fail is not a function")
    },
    complete: function(res) {
      if (typeof complete === 'function')
        complete()
      else
        console.log("complete is not a function")
    }
  })
}

4.最后模块向外暴露封装好的request

module.exports = {
  request: request
}

这一篇我们了解了请求阿里云物联网平台云端API所需要的公共参数和签名机制,并进行了sdk编写。下一篇就将编写小程序使用我们编写好的sdk调用云端API,实现获取已经上云的设备数据,并给设备下发指令。

发布了4 篇原创文章 · 获赞 4 · 访问量 1346

猜你喜欢

转载自blog.csdn.net/weixin_42852371/article/details/105062549