上一篇:nodejs微信公众号开发(一)微信接入公众号,本文将在此基础上实现一个简单的回复功能。(项目github地址:https://github.com/Panfen/wem...)1.在接入码优化之前,我们简单粗暴的实现了微信公众号的接入,直接写了接入码在app.js文件中,从项目开发的角度来看,不方便以后的代码维护,所以将这部分代码分离出来,按照koa风格写成中间件。在根目录新建wechat文件夹,新建generator.js文件,varsha1=require('sha1');module.exports=function(opts){returnfunction*(next){vartoken=opts.token;varsignature=this.query.signature;varnonce=this.query.nonce;vartimestamp=this.query.timestamp;varechostr=this.query.echostr;varstr=[token,timestamp,nonce].sort().join('');varsha=sha1(str);this.body=(sha===signature)?echostr+'':'失败';};}此时app.js的内容变为:'usestrict'varKoa=require('koa');vargenerator=require('./wechat/generator');varconfig={wechat:{appID:'...',appSecret:'...',token:'...'}};varapp=newKoa();app.use(generator(config.wechat));app.listen(8080);console.log('监听8080...')2.获取access_tokenaccess_token是开发程序与微信公众平台交互的关键,大部分接口的调用都需要access_token。access_token的特点:有效期为2小时(7200s),过期会自动失效,需要重新获取;只要更新access_token,之前的access_token就会自动失效;解决方案:系统会每隔2小时自动获取access_token的值,保证access_token始终有效;为了方便频繁调用,将access_token只存放在一个地方(数据库、文件等),所有子系统都可以访问。程序采用constructor的方式,在生成实例并完成初始化工作的过程中,读取config/wechat.txt文件中保存的ticket,判断是否为空和过期,有选择地重新获取号码并保存在原文档,关于获取access_token的官方文档可见:Obtainingaccess_token。functionWechat(opts){//构造函数,用于生成实例,完成初始化,读写票据varthat=this;this.appID=opts.appID;this.appSecret=opts.appSecret;this.getAccessToken=选择。获取访问令牌;this.saveAccessToken=opts.saveAccessToken;this.getAccessToken().then(function(data){try{data=JSON.parse(data);}catch(e){returnthat.updateAccessToken();}if(that.isvalidAccessToken(data)){承诺。resolve(data);}else{returnthat.updateAccessToken();}}).then(function(data){that.access_token=data.access_token;that.expires_in=data.expires_in;that.saveAccessToken(JSON.stringify(data));});}我们在moudle.exports中实例化一个微信:varwechat=newWechat(opts);这样可以保证每次程序启动时都会获取access_token进行有效性检查,每隔一段时间会自动获取一个新的access_token。3、处理微信消息的步骤无论是事件推送还是消息推送,微信服务器都是以post的形式发送请求。推送的数据类型不是json而是xml。处理推送消息一般分为五个步骤:处理POST类型的控制逻辑,接收xml数据包;解析数据包,获取数据包的报文类型或数据类型;组装自定义消息;将它们打包成xml格式;5秒内返回消息。3.1接收xml数据通过raw-body模块可以获取http模块中的request对象,将数据拼装得到一个bufferxml对象vardata=yieldrawBody(this.req,{length:this.length,limit:'1mb',encoding:this.charset});console.log('data:'+data);3.2解析xml数据使用xml2js模块将xml数据解析成对象格式varcontent=yieldutil.parseXMLAsync(data);util中的parseXMLAsync方法:exports.parseXMLAsync=function(xml){returnnewPromise(function(resolve,reject){xml2js.parseString(xml,{trim:true},function(err,content){err?reject(err):resolve(content);})});}3.3格式化xml数据从解析出来的xml数据来看,虽然数据已经是键值对的形式,但是它的值是数组的形式,需要扁平化处理:varmessage=util.formatMessage(content.xml);它的本质是遍历数组中的值,因为多文消息存在嵌套情况:functionformatMessage(result){varmessage={};if(typeofresult==='object'){varkeys=Object.keys(result);for(vari=0;i
