详解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框架Express的模板视图机制分析
Jul 19 NodeJs
跟我学Nodejs(一)--- Node.js简介及安装开发环境
May 20 NodeJs
我的NodeJs学习小结(一)
Jul 06 NodeJs
抛弃Nginx使用nodejs做反向代理服务器
Jul 17 NodeJs
nodejs npm package.json中文文档
Sep 04 NodeJs
nodejs实现邮件发送服务实例分享
Mar 29 NodeJs
详解nodejs爬虫程序解决gbk等中文编码问题
Apr 06 NodeJs
详解nodejs微信公众号开发——4.自动回复各种消息
Apr 11 NodeJs
Nodejs进阶之服务端字符编解码和乱码处理
Sep 04 NodeJs
nodejs express配置自签名https服务器的方法
May 22 NodeJs
基于Nodejs的Tcp封包和解包的理解
Sep 19 NodeJs
NodeJs生成sitemap站点地图的方法示例
Jun 11 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遍历某个目录下的所有文件和子文件夹的实现代码
2013/06/28 PHP
PHP+shell实现多线程的方法
2015/07/01 PHP
PHP程序员的技术成长规划
2016/03/25 PHP
详解Yii2.0使用AR联表查询实例
2017/06/16 PHP
ThinkPHP5&amp;5.1框架关联模型分页操作示例
2019/08/03 PHP
PHP pthreads v3下的Volatile简介与使用方法示例
2020/02/21 PHP
PHP生成随机密码4种方法及性能对比
2020/12/11 PHP
javascript 关于# 和 void的区别分析
2009/10/26 Javascript
Jquery中增加参数与Json转换代码
2009/11/20 Javascript
线路分流自动智能跳转代码,自动选择最快镜像网站(js)
2011/10/31 Javascript
轻松创建nodejs服务器(7):阻塞操作的实现
2014/12/18 NodeJs
JavaScript中的原型链prototype介绍
2014/12/30 Javascript
浅谈React Native 中组件的生命周期
2017/09/08 Javascript
ES6学习笔记之map、set与数组、对象的对比
2018/03/01 Javascript
Angular入口组件(entry component)与声明式组件的区别详解
2018/04/09 Javascript
nodejs基础之多进程实例详解
2018/12/27 NodeJs
详解Vue template 如何支持多个根结点
2020/02/10 Javascript
详解react组件通讯方式(多种)
2020/05/06 Javascript
[05:59]2018DOTA2国际邀请赛寻真——只为胜利的Secret
2018/08/13 DOTA
[01:18]PWL开团时刻DAY4——圣剑与抢盾
2020/11/03 DOTA
Python迭代器和生成器定义与用法示例
2018/02/10 Python
使用Python搭建虚拟环境的配置方法
2018/02/28 Python
解决python matplotlib imshow无法显示的问题
2018/05/24 Python
树莓派安装OpenCV3完整过程的实现
2019/10/10 Python
Python实现遗传算法(二进制编码)求函数最优值方式
2020/02/11 Python
Python matplotlib可视化实例解析
2020/06/01 Python
购买限量版收藏品、珠宝和礼品:Bradford Exchange
2016/09/23 全球购物
Banana Republic欧盟:美国都市简约风格的代表品牌
2018/05/09 全球购物
Trip.com澳大利亚:在线旅行社
2019/12/01 全球购物
小学语文业务学习材料
2014/06/02 职场文书
幼儿教师年度个人总结
2015/02/05 职场文书
个人简历自我评价怎么写
2015/03/10 职场文书
汽车4S店销售经理岗位职责
2015/04/02 职场文书
2015年团支部工作总结
2015/04/03 职场文书
煤矿施工安全协议书
2016/03/22 职场文书
python调试工具Birdseye的使用教程
2021/05/25 Python