詳解nodejs微信公眾號開發(fā)——2.自動(dòng)回復(fù)
上一篇文章:nodejs微信公眾號開發(fā)(1)接入微信公眾號,本篇文章將在此基礎(chǔ)上實(shí)現(xiàn)簡單的回復(fù)功能。
1. 接入代碼的優(yōu)化
之前我們簡單粗暴的實(shí)現(xiàn)了微信公眾號的接入,接入的代碼直接寫在了app.js文件里面,從項(xiàng)目開發(fā)的角度而言,不便于日后代碼的維護(hù),所以將這部分代碼獨(dú)立出來,按照koa的風(fēng)格,寫成一個(gè)中間件。
在根目錄下新建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';
};
}
此時(shí)app.js的內(nèi)容變成:
'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是開發(fā)程序與wexin公眾平臺交互的一把鑰匙,調(diào)用絕大部分接口都需要用到access_token。
access_token的特點(diǎn):
- 有效期為2小時(shí)(7200s),過期自動(dòng)失效,需要重新獲??;
- 只要更新了access_token,之前的access_token自動(dòng)失效;
解決方案:
- 系統(tǒng)每隔2小時(shí)自動(dòng)去獲取一下access_token的值,確保access_token始終是有效的;
- 為了方便頻繁調(diào)用,將access_token存儲在唯一的一個(gè)地方(數(shù)據(jù)庫、文件等),所有子系統(tǒng)都能訪問。
程序中采用構(gòu)造函數(shù)的方式,在生成實(shí)例,完成初始化工作的的過程中,讀取存儲在config/wechat.txt文件中的票據(jù),判斷是否為空且是否過期,選擇性的重新獲取數(shù)字并且保存在原文件里面,關(guān)于獲取access_token的官方文檔介紹可見:獲取access_token。
function Wechat(opts){ //構(gòu)造函數(shù),用以生成實(shí)例,完成初始化工作,讀寫票據(jù)
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中實(shí)例化一個(gè)Wechat:
var wechat = new Wechat(opts);
這樣確保了每次程序啟動(dòng)都會獲取對access_token的有效性進(jìn)行檢驗(yàn),并且每個(gè)一段時(shí)間會自動(dòng)獲取一個(gè)新的access_token。
3. 處理微信消息的步驟
無論是事件推送還是消息推送,微信服務(wù)器都是以post的方式發(fā)送請求,推送的數(shù)據(jù)類型不是json而是xml,處理推送消息一般分為五個(gè)步驟:
- 處理POST類型的控制邏輯,接收xml數(shù)據(jù)包;
- 解析數(shù)據(jù)包,獲取數(shù)據(jù)包的消息類型或數(shù)據(jù)類型;
- 拼裝自定義的消息;
- 包裝成xml格式;
- 在5秒鐘內(nèi)返回消息。
3.1 接收xml數(shù)據(jù)
通過raw-body模塊可以獲取http模塊中的request對象,并且可以對數(shù)據(jù)進(jìn)行拼裝,從而拿到一個(gè)buffer的xml對象
var data = yield rawBody(this.req,{
length:this.length,
limit:'1mb',
encoding:this.charset
});
console.log('data:'+data);

3.2 解析xml數(shù)據(jù)
使用xml2js模塊,將xml數(shù)據(jù)解析成對象格式
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);
})
});
}

3.3 格式化xml數(shù)據(jù)
從解析的xml數(shù)據(jù)來看,數(shù)據(jù)雖然已經(jīng)呈現(xiàn)鍵值對的形式,但是其值是數(shù)組的形式,需要進(jìn)行扁平化處理:
var message = util.formatMessage(content.xml);
其本質(zhì)就是遍歷數(shù)組中的值,因?yàn)樵诙鄨D文的消息中存在嵌套的情況:
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;
}

3.4 判斷消息類型并回復(fù)
這里針對subscribe事件,新關(guān)注后自動(dòng)回復(fù)文本消息終于等到你,還好我沒放棄
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;
}
}
注:這里只是簡單地實(shí)現(xiàn)一下自動(dòng)回復(fù)功能,這種拼接字符串的方式還是很不方便的,后面會封裝成接口。
使用手機(jī)微信掃描測試賬號的二維碼,即可關(guān)注,同時(shí)接收到測試公眾號推送的消息!

啦啦,一個(gè)簡單的關(guān)注回復(fù)就完成了。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
node.js基于socket.io快速實(shí)現(xiàn)一個(gè)實(shí)時(shí)通訊應(yīng)用
這篇文章主要介紹了node.js基于socket.io快速實(shí)現(xiàn)一個(gè)實(shí)時(shí)通訊應(yīng)用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
nodejs做個(gè)爬蟲爬取騰訊動(dòng)漫內(nèi)容簡單實(shí)現(xiàn)
這篇文章主要為大家介紹了nodejs做個(gè)爬蟲爬取騰訊動(dòng)漫內(nèi)容簡單實(shí)現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
使用 Node.js 模擬滑動(dòng)拼圖驗(yàn)證碼操作的示例代碼
本篇文章主要介紹了使用 Node.js 模擬滑動(dòng)驗(yàn)證碼操作的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11
Node.js使用Streams來處理文件讀寫操作的示例代碼
在Node.js中,Streams 提供了一種高效的方式來處理文件的讀寫操作,特別是對于大文件或數(shù)據(jù)流,Streams 允許你以流的方式讀寫數(shù)據(jù),這意味著數(shù)據(jù)可以分塊處理,本文介紹了在Node.js中如何使用Streams來處理文件讀寫操作,需要的朋友可以參考下2024-09-09
Nodejs進(jìn)階之服務(wù)端字符編解碼和亂碼處理
這篇文章主要介紹了Nodejs進(jìn)階之服務(wù)端字符編解碼和亂碼處理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09
node省市區(qū)三級數(shù)據(jù)性能測評實(shí)例分析
這篇文章主要介紹了node省市區(qū)三級數(shù)據(jù)性能,結(jié)合具體實(shí)例形式評測分析了node省市區(qū)三級數(shù)據(jù)的實(shí)現(xiàn)、改進(jìn)方法與運(yùn)行效率,需要的朋友可以參考下2019-11-11
Nodejs Express 通過log4js寫日志到Logstash(ELK)
這篇文章主要介紹了Nodejs Express 通過log4js寫日志到Logstash(ELK),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08
Node.js中的require.resolve方法使用簡介
在Node.js中,可以使用require.resolve函數(shù)來查詢某個(gè)模塊文件的帶有完整絕對路徑的文件名,下面這篇文章主要介紹了Node.js中require.resolve方法使用的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-04-04
詳解如何在Node.js中實(shí)現(xiàn)HTTP/2推送信息
HTTP/2 是一種現(xiàn)代化的網(wǎng)絡(luò)協(xié)議,它引入了多路復(fù)用、頭部壓縮和服務(wù)器推送等特性,其中,服務(wù)器推送允許服務(wù)器在客戶端請求資源時(shí),主動(dòng)推送額外的資源,提升頁面加載速度,下面將介紹如何在 Node.js 中實(shí)現(xiàn) HTTP/2 推送信息,需要的朋友可以參考下2024-12-12

