詳解nodejs微信公眾號(hào)開(kāi)發(fā)——2.自動(dòng)回復(fù)
上一篇文章:nodejs微信公眾號(hào)開(kāi)發(fā)(1)接入微信公眾號(hào),本篇文章將在此基礎(chǔ)上實(shí)現(xiàn)簡(jiǎn)單的回復(fù)功能。
1. 接入代碼的優(yōu)化
之前我們簡(jiǎn)單粗暴的實(shí)現(xiàn)了微信公眾號(hào)的接入,接入的代碼直接寫(xiě)在了app.js
文件里面,從項(xiàng)目開(kāi)發(fā)的角度而言,不便于日后代碼的維護(hù),所以將這部分代碼獨(dú)立出來(lái),按照koa
的風(fēng)格,寫(xiě)成一個(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
是開(kāi)發(fā)程序與wexin公眾平臺(tái)交互的一把鑰匙,調(diào)用絕大部分接口都需要用到access_token
。
access_token
的特點(diǎn):
- 有效期為2小時(shí)(7200s),過(guò)期自動(dòng)失效,需要重新獲??;
- 只要更新了access_token,之前的access_token自動(dòng)失效;
解決方案:
- 系統(tǒng)每隔2小時(shí)自動(dòng)去獲取一下access_token的值,確保access_token始終是有效的;
- 為了方便頻繁調(diào)用,將access_token存儲(chǔ)在唯一的一個(gè)地方(數(shù)據(jù)庫(kù)、文件等),所有子系統(tǒng)都能訪(fǎng)問(wèn)。
程序中采用構(gòu)造函數(shù)的方式,在生成實(shí)例,完成初始化工作的的過(guò)程中,讀取存儲(chǔ)在config/wechat.txt
文件中的票據(jù),判斷是否為空且是否過(guò)期,選擇性的重新獲取數(shù)字并且保存在原文件里面,關(guān)于獲取access_token的官方文檔介紹可見(jiàn):獲取access_token。
function Wechat(opts){ //構(gòu)造函數(shù),用以生成實(shí)例,完成初始化工作,讀寫(xiě)票據(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)); }); }
我們?cè)?code>moudle.exports中實(shí)例化一個(gè)Wechat
:
var wechat = new Wechat(opts);
這樣確保了每次程序啟動(dòng)都會(huì)獲取對(duì)access_token的有效性進(jìn)行檢驗(yàn),并且每個(gè)一段時(shí)間會(huì)自動(dòng)獲取一個(gè)新的access_token。
3. 處理微信消息的步驟
無(wú)論是事件推送還是消息推送,微信服務(wù)器都是以post的方式發(fā)送請(qǐng)求,推送的數(shù)據(jù)類(lèi)型不是json
而是xml
,處理推送消息一般分為五個(gè)步驟:
- 處理POST類(lèi)型的控制邏輯,接收xml數(shù)據(jù)包;
- 解析數(shù)據(jù)包,獲取數(shù)據(jù)包的消息類(lèi)型或數(shù)據(jù)類(lèi)型;
- 拼裝自定義的消息;
- 包裝成xml格式;
- 在5秒鐘內(nèi)返回消息。
3.1 接收xml數(shù)據(jù)
通過(guò)raw-body模塊可以獲取http模塊中的request對(duì)象,并且可以對(duì)數(shù)據(jù)進(jìn)行拼裝,從而拿到一個(gè)buffer的xml對(duì)象
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ù)解析成對(duì)象格式
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ù)來(lái)看,數(shù)據(jù)雖然已經(jīng)呈現(xiàn)鍵值對(duì)的形式,但是其值是數(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 判斷消息類(lèi)型并回復(fù)
這里針對(duì)subscribe
事件,新關(guān)注后自動(dòng)回復(fù)文本消息終于等到你,還好我沒(méi)放棄
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[終于等到你,還好我沒(méi)放棄]]></Content>'+ '</xml>' return; } }
注:這里只是簡(jiǎn)單地實(shí)現(xiàn)一下自動(dòng)回復(fù)功能,這種拼接字符串的方式還是很不方便的,后面會(huì)封裝成接口。
使用手機(jī)微信掃描測(cè)試賬號(hào)的二維碼,即可關(guān)注,同時(shí)接收到測(cè)試公眾號(hào)推送的消息!
啦啦,一個(gè)簡(jiǎn)單的關(guān)注回復(fù)就完成了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Nest.js使用multer實(shí)現(xiàn)文件上傳功能
這篇文章主要為大家詳細(xì)介紹了Nest.js鵝湖使用multer實(shí)現(xiàn)文件上傳功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03node操作mysql數(shù)據(jù)庫(kù)實(shí)例詳解
這篇文章主要介紹了node操作mysql數(shù)據(jù)庫(kù),結(jié)合實(shí)例形式較為詳細(xì)的分析了node操作數(shù)據(jù)庫(kù)的連接、增刪改查、事務(wù)處理及錯(cuò)誤處理相關(guān)操作技巧,需要的朋友可以參考下2017-03-03nvm管理node無(wú)法正常切換node版本問(wèn)題的解決方法
相信一定會(huì)有存在一些小伙伴 明明都已經(jīng)按著操作卸載node 和安裝nvm 了但是 依舊無(wú)法正常通過(guò)nvm管理node,本文將給大家介紹nvm管理node無(wú)法正常切換node版本問(wèn)題的解決方法,需要的朋友可以參考下2024-01-01150行Node.js實(shí)現(xiàn)的dns代理工具
這篇文章主要介紹了150行Node.js實(shí)現(xiàn)的dns代理工具,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08node快速搭建后臺(tái)的實(shí)現(xiàn)步驟
本文主要介紹了node快速搭建后臺(tái),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12Windows系統(tǒng)下Node.js的簡(jiǎn)單入門(mén)教程
這篇文章主要介紹了Windows系統(tǒng)下Node.js的簡(jiǎn)單入門(mén)教程,Node.js是用于后端編程的JavaScript框架,需要的朋友可以參考下2015-06-06