上一篇文章: nodejs微信公衆號開發(1)接入微信公衆號,本篇文章將在此基礎上實現簡單的回覆功能。(項目github地: https://github.com/Panfen/wem... )
以前咱們簡單粗暴的實現了微信公衆號的接入,接入的代碼直接寫在了app.js
文件裏面,從項目開發的角度而言,不便於往後代碼的維護,因此將這部分代碼獨立出來,按照koa
的風格,寫成一箇中間件。
在根目錄下新建wechat
文件夾,新建generator.js
文件,html
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
的內容變成:node
'use strict' var Koa = require('koa'); var generator= require('./wechat/generator'); var config = { wechat:{ appID:'...', appSecret:'...', token:'...' } }; var app = new Koa(); app.use(generator(config.wechat)); app.listen(8080);
console.log('Listening 8080...')git
access_token
是開發程序與wexin公衆平臺交互的一把鑰匙,調用絕大部分接口都須要用到access_token
。
access_token的特色:
解決方案:github
程序中採用構造函數的方式,在生成實例,完成初始化工做的的過程當中,讀取存儲在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
:json
var wechat = new Wechat(opts);
這樣確保了每次程序啓動都會獲取對access_token
的有效性進行檢驗,而且每一個一段時間會自動獲取一個新的access_token
。segmentfault
不管是事件推送仍是消息推送,微信服務器都是以post的方式發送請求,推送的數據類型不是json
而是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);
使用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); }) }); }
從解析的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; }
這裏針對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; } }
注:
這裏只是簡單地實現一下自動回覆功能,這種拼接字符串的方式仍是很不方便的,後面會封裝成接口。
使用手機微信掃描測試帳號的二維碼,便可關注,同時接收到測試公衆號推送的消息!
啦啦,一個簡單的關注回覆就完成了。