详解nodejs微信公众号开发——2.自动回复


Posted in NodeJs onApril 10, 2017

上一篇文章:nodejs微信公众号开发(1)接入微信公众号,本篇文章将在此基础上实现简单的回复功能。

1. 接入代码的优化

之前我们简单粗暴的实现了微信公众号的接入,接入的代码直接写在了app.js文件里面,从项目开发的角度而言,不便于日后代码的维护,所以将这部分代码独立出来,按照koa的风格,写成一个中间件。

在根目录下新建wechat文件夹,新建generator.js文件,

var sha1 = require('sha1');

module.exports = function(opts){
  return function *(next){
    var token = opts.token;
    var signature = this.query.signature;
    var nonce = this.query.nonce;
    var timestamp = this.query.timestamp;
    var echostr = this.query.echostr;
    var str = [token,timestamp,nonce].sort().join('');
    var sha = sha1(str);
    this.body = (sha === signature) ? echostr + '' : 'failed';
  };
}

此时app.js的内容变成:

'use strict'

var Koa = require('koa');
var wechat = require('./wechat/generator');

var config = {
  wechat:{
    appID:'...',
    appSecret:'...',
    token:'...'
  }
};

var app = new Koa();
app.use(wechat(config.wechat));
app.listen(8080);
console.log('Listening 8080...')

2. 获取access_token

access_token是开发程序与wexin公众平台交互的一把钥匙,调用绝大部分接口都需要用到access_token

access_token的特点:

  1. 有效期为2小时(7200s),过期自动失效,需要重新获取;
  2. 只要更新了access_token,之前的access_token自动失效;

解决方案:

  1. 系统每隔2小时自动去获取一下access_token的值,确保access_token始终是有效的;
  2. 为了方便频繁调用,将access_token存储在唯一的一个地方(数据库、文件等),所有子系统都能访问。

程序中采用构造函数的方式,在生成实例,完成初始化工作的的过程中,读取存储在config/wechat.txt文件中的票据,判断是否为空且是否过期,选择性的重新获取数字并且保存在原文件里面,关于获取access_token的官方文档介绍可见:获取access_token。

function Wechat(opts){   //构造函数,用以生成实例,完成初始化工作,读写票据
  var that = this;
  this.appID = opts.appID;
  this.appSecret = opts.appSecret;
  this.getAccessToken = opts.getAccessToken;
  this.saveAccessToken = opts.saveAccessToken;

  this.getAccessToken().then(function(data){
    try{
      data = JSON.parse(data);
    }catch(e){
      return that.updateAccessToken();
    }
    if(that.isvalidAccessToken(data)){
      Promise.resolve(data);
    }else{
      return that.updateAccessToken();
    }
  }).then(function(data){
    that.access_token = data.access_token;
    that.expires_in = data.expires_in;
    that.saveAccessToken(JSON.stringify(data));
  });
}

我们在moudle.exports中实例化一个Wechat

var wechat = new Wechat(opts);

这样确保了每次程序启动都会获取对access_token的有效性进行检验,并且每个一段时间会自动获取一个新的access_token。

3. 处理微信消息的步骤

无论是事件推送还是消息推送,微信服务器都是以post的方式发送请求,推送的数据类型不是json而是xml,处理推送消息一般分为五个步骤:

  1. 处理POST类型的控制逻辑,接收xml数据包;
  2. 解析数据包,获取数据包的消息类型或数据类型;
  3. 拼装自定义的消息;
  4. 包装成xml格式;
  5. 在5秒钟内返回消息。

3.1 接收xml数据

通过raw-body模块可以获取http模块中的request对象,并且可以对数据进行拼装,从而拿到一个buffer的xml对象

var data = yield rawBody(this.req,{
        length:this.length,
        limit:'1mb',
        encoding:this.charset
      });
console.log('data:'+data);

详解nodejs微信公众号开发——2.自动回复

3.2 解析xml数据

使用xml2js模块,将xml数据解析成对象格式

var content = yield util.parseXMLAsync(data);
util中的parseXMLAsync方法:

exports.parseXMLAsync = function(xml){
  return new Promise(function(resolve,reject){
    xml2js.parseString(xml,{trim:true},function(err,content){
      err ? reject(err) : resolve(content);
    })
  });
}

详解nodejs微信公众号开发——2.自动回复

3.3 格式化xml数据

从解析的xml数据来看,数据虽然已经呈现键值对的形式,但是其值是数组的形式,需要进行扁平化处理:

var message = util.formatMessage(content.xml);

其本质就是遍历数组中的值,因为在多图文的消息中存在嵌套的情况:

function formatMessage(result){
  var message = {};
  if(typeof result === 'object'){
    var keys = Object.keys(result);
    for(var i=0;i<keys.length;i++){
      var key = keys[i];
      var item = result[key];
      if(!(item instanceof Array) || item.length === 0) continue;
      if (item.length === 1){
        var val = item[0];
        if (typeof val === 'object') message[key] = formatMessage(val);
        else message[key] = (val || '').trim();
      }else{
        message[key] = [];
        for(var j=0,k=item.length;j<k;j++) message[key].push(formatMessage(item[j]));
      }
    }
  }
  return message;
}

详解nodejs微信公众号开发——2.自动回复

3.4 判断消息类型并回复

这里针对subscribe事件,新关注后自动回复文本消息终于等到你,还好我没放弃

if(message.MsgType === 'event'){
  if(message.Event === 'subscribe'){
    var createTime = new Date().getTime();
    that.status = 200;
    that.type = 'application/xml';
    that.body = '<xml>'+
          '<ToUserName><![CDATA['+ message.FromUserName +']]></ToUserName>'+
          '<FromUserName><![CDATA['+ message.ToUserName +']]></FromUserName>'+
          '<CreateTime>'+createTime+'</CreateTime>'+
          '<MsgType><![CDATA[text]]></MsgType>'+
          '<Content><![CDATA[终于等到你,还好我没放弃]]></Content>'+
          '</xml>'
    return;
  }
}

注:这里只是简单地实现一下自动回复功能,这种拼接字符串的方式还是很不方便的,后面会封装成接口。

使用手机微信扫描测试账号的二维码,即可关注,同时接收到测试公众号推送的消息!

详解nodejs微信公众号开发——2.自动回复

啦啦,一个简单的关注回复就完成了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

NodeJs 相关文章推荐
NodeJS的模块写法入门(实例代码)
Mar 07 NodeJs
NodeJS创建基础应用并应用模板引擎
Apr 12 NodeJs
用nodejs搭建websocket服务器
Jan 23 NodeJs
angular2+nodejs实现图片上传功能
Mar 27 NodeJs
NodeJs安装npm包一直失败的解决方法
Apr 28 NodeJs
Nodejs回调加超时限制两种实现方法
Jun 09 NodeJs
Nodejs连接mysql并实现增、删、改、查操作的方法详解
Jan 04 NodeJs
NodeJS安装图文教程
Apr 19 NodeJs
nodejs npm错误Error:UNKNOWN:unknown error,mkdir 'D:\Develop\nodejs\node_global'at Error
Mar 02 NodeJs
nodejs对项目下所有空文件夹创建gitkeep的方法
Aug 02 NodeJs
Nodejs 数组的队列以及forEach的应用详解
Feb 25 NodeJs
nodejs利用readline提示输入内容实例代码
Jul 15 NodeJs
详解nodejs微信公众号开发——1.接入微信公众号
Apr 10 #NodeJs
使用 NodeJS+Express 开发服务端的简单介绍
Apr 07 #NodeJs
初识NodeJS服务端开发入门(Express+MySQL)
Apr 07 #NodeJs
nodejs服务搭建教程 nodejs访问本地站点文件
Apr 07 #NodeJs
nodejs爬虫遇到的乱码问题汇总
Apr 07 #NodeJs
详解nodejs爬虫程序解决gbk等中文编码问题
Apr 06 #NodeJs
NodeJS基础API搭建服务器详细过程记录
Apr 01 #NodeJs
You might like
php遍历文件夹下的所有文件和子文件夹示例
2014/03/20 PHP
ThinkPHP做文字水印时提示call an undefined function exif_imagetype()解决方法
2014/10/30 PHP
PHP实现递归无限级分类
2015/10/22 PHP
CodeIgniter框架数据库基本操作示例
2018/05/24 PHP
thinkphp5引入公共部分header、footer的方法详解
2018/09/14 PHP
laravel实现登录时监听事件,添加登录用户的记录方法
2019/09/30 PHP
js创建对象的几种常用方式小结(推荐)
2010/10/24 Javascript
颜色选择器 Color Picker,IE,Firefox,Opera,Safar
2010/11/25 Javascript
JavaScript焦点事件、鼠标事件和滚轮事件使用详解
2016/01/15 Javascript
vue组件间通信解析
2017/03/01 Javascript
基于js中document.cookie全面解析
2017/09/14 Javascript
Webpack打包字体font-awesome的方法示例
2018/04/26 Javascript
vue2.x集成百度UEditor富文本编辑器的方法
2018/09/21 Javascript
JS面试题大坑之隐式类型转换实例代码
2018/10/14 Javascript
JS实现小星星特效
2019/12/24 Javascript
JS typeof fn === 'function' &amp;&amp; fn()详解
2020/08/22 Javascript
[42:32]完美世界DOTA2联赛循环赛 Magma vs PXG BO2第二场 10.28
2020/10/28 DOTA
python使用正则搜索字符串或文件中的浮点数代码实例
2014/07/11 Python
Python类的多重继承问题深入分析
2014/11/09 Python
在Python中操作字符串之startswith()方法的使用
2015/05/20 Python
Python实现的最近最少使用算法
2015/07/10 Python
python中format()函数的简单使用教程
2018/03/14 Python
[原创]Python入门教程5. 字典基本操作【定义、运算、常用函数】
2018/11/01 Python
Python之虚拟环境virtualenv,pipreqs生成项目依赖第三方包的方法
2019/07/23 Python
python使用yield压平嵌套字典的超简单方法
2019/11/02 Python
使用Python-OpenCV消除图像中孤立的小区域操作
2020/07/05 Python
Monnier Frères美国官网:法国知名奢侈品网站
2016/11/22 全球购物
匈牙利墨盒和碳粉购买网站:CDRmarket
2018/04/14 全球购物
Steiff台湾官网:德国金耳釦泰迪熊
2019/12/26 全球购物
数学专业推荐信范文
2013/11/21 职场文书
中专毕业生自荐信范文
2013/11/28 职场文书
幼儿园植树节活动总结
2014/07/04 职场文书
高三英语复习计划
2015/01/19 职场文书
比赛主持人开场白
2015/05/29 职场文书
安全主题班会教案
2015/08/12 职场文书
Android移动应用开发指南之六种布局详解
2022/09/23 Java/Android