NodeJS實(shí)現(xiàn)微信公眾號(hào)關(guān)注后自動(dòng)回復(fù)功能
一 實(shí)先自動(dòng)回復(fù)功能的邏輯步驟
1 處理POST類(lèi)型的控制邏輯,接收XML的數(shù)據(jù)包;
2 解析XML數(shù)據(jù)包(獲得數(shù)據(jù)包的消息類(lèi)型或者是事件類(lèi)型);
3 拼裝我們定義好的消息;
4 包裝成XML格式;
5 在5秒內(nèi)返回回去
二 代碼實(shí)操
本節(jié)代碼參照上節(jié)課繼續(xù)修改和完善,目錄結(jié)構(gòu)跟之前相同,新引入的模塊raw-body使用npm install安裝一下即可,app.js啟動(dòng)文件和util.js不做變動(dòng),主要修改一下generator.js文件,以及在generator.js同級(jí)目錄下新建wechat.js文件和tools.js文件。
wechat.js文件是將前一節(jié)generator.js文件中票據(jù)部分的代碼抽離出來(lái)單獨(dú)放在一個(gè)文件中,具體代碼如下:
'use strict'; // 引入模塊 var Promise = require('bluebird'); var request = Promise.promisify(require('request')); //增加url配置項(xiàng) var prefix = 'https://api.weixin.qq.com/cgi-bin/'; var api = { accessToken: prefix + 'token?grant_type=client_credential' }; //利用構(gòu)造函數(shù)生成實(shí)例 完成票據(jù)存儲(chǔ)邏輯 function weChat(opts) { var that = this; this.appID = opts.appID; this.appSecret = opts.appSecret; this.getAccessToken = opts.getAccessToken; this.saveAccessToken = opts.saveAccessToken; //獲取票據(jù)的方法 this.getAccessToken() .then(function(data) { //從靜態(tài)文件獲取票據(jù),JSON化數(shù)據(jù),如果有異常,則嘗試更新票據(jù) try { data = JSON.parse(data); } catch (e) { return that.updateAccessToken(); } //判斷票據(jù)是否在有效期內(nèi),如果合法,向下傳遞票據(jù),如果不合法,更新票據(jù) if (that.isValidAccessToken(data)) { Promise.resolve(data); } else { return that.updateAccessToken(); } }) //將拿到的票據(jù)信息和有效期信息存儲(chǔ)起來(lái) .then(function(data) { //console.log(data); that.access_token = data.access_token; that.expires_in = data.expires_in; that.saveAccessToken(data); }) }; //在weChat的原型鏈上增加驗(yàn)證有效期的方法 weChat.prototype.isValidAccessToken = function(data) { //進(jìn)行判斷,如果票據(jù)不合法,返回false if (!data || !data.access_token || !data.expires_in) { return false; } //拿到票據(jù)和過(guò)期時(shí)間的數(shù)據(jù) var access_token = data.access_token; var expires_in = data.expires_in; //獲取當(dāng)前時(shí)間 var now = (new Date().getTime()); //如果當(dāng)前時(shí)間小于票據(jù)過(guò)期時(shí)間,返回true,否則返回false if (now < expires_in) { return true; } else { return false; }; }; //在weChat的原型鏈上增加更新票據(jù)的方法 weChat.prototype.updateAccessToken = function() { var appID = this.appID; var appSecret = this.appSecret; var url = api.accessToken + '&appid=' + appID + '&secret=' + appSecret; return new Promise(function(resolve, reject) { //使用request發(fā)起請(qǐng)求 request({ url: url, json: true }).then(function(response) { var data = response.body; var now = (new Date().getTime()); var expires_in = now + (data.expires_in - 20) * 1000; //把新票據(jù)的有效時(shí)間賦值給data data.expires_in = expires_in; resolve(data); }) }) }; //向外暴露weChat module.exports = weChat;
generator.js文件進(jìn)行精簡(jiǎn)后,添加判斷對(duì)xml數(shù)據(jù)的格式化方法以及判斷事件,添加關(guān)注事件測(cè)試信息,具體代碼如下:
'use strict'; // 引入模塊 var sha1 = require('sha1'); var getRawBody = require('raw-body'); var weChat = require('./wechat'); var tools = require('./tools'); // 建立中間件函數(shù)并暴露出去 module.exports = function(opts) { //實(shí)例化weChat()函數(shù) //var wechat = new weChat(opts); return function*(next) { //console.log(this.query); var that = this; var token = opts.token; var signature = this.query.signature; var nonce = this.query.nonce; var timestamp = this.query.timestamp; var echostr = this.query.echostr; // 進(jìn)行字典排序 var str = [token, timestamp, nonce].sort().join(''); // 進(jìn)行加密 var sha = sha1(str); //使用this.method對(duì)請(qǐng)求方法進(jìn)行判斷 if (this.method === 'GET') { // 如果是get請(qǐng)求 判斷加密后的值是否等于簽名值 if (sha === signature) { this.body = echostr + ''; } else { this.body = 'wrong'; }; } else if (this.method === 'POST') { //如果是post請(qǐng)求 也是先判斷簽名是否合法 如果不合法 直接返回wrong if (sha !== signature) { this.body = 'wrong'; return false; }; //通過(guò)raw-body模塊 可以把把this上的request對(duì)象 也就是http模塊中的request對(duì)象 去拼裝它的數(shù)據(jù) 最終拿到一個(gè)buffer的xml數(shù)據(jù) //通過(guò)yield關(guān)鍵字 獲取到post過(guò)來(lái)的原始的XML數(shù)據(jù) var data = yield getRawBody(this.req, { length: this.length, limit: '1mb', encoding: this.charset }); //console.log(data.toString());打印XML數(shù)據(jù)(當(dāng)微信公眾號(hào)有操作的時(shí)候 終端可以看到返回的XML數(shù)據(jù)) //tools為處理XML數(shù)據(jù)的工具包 使用tools工具包的parseXMLAsync方法 把XML數(shù)據(jù)轉(zhuǎn)化成數(shù)組對(duì)象 var content = yield tools.parseXMLAsync(data); //console.log(content);打印轉(zhuǎn)化后的數(shù)組對(duì)象 //格式化content數(shù)據(jù)為json對(duì)象 var message = tools.formatMessage(content.xml); console.log(message); //打印message //判斷message的MsgType 如果是event 則是一個(gè)事件 if (message.MsgType === 'event') { //如果是訂閱事件 if (message.Event === 'subscribe') { //獲取當(dāng)前時(shí)間戳 var now = new Date().getTime(); //設(shè)置回復(fù)狀態(tài)是200 that.status = 200; //設(shè)置回復(fù)的類(lèi)型是xml格式 that.type = 'application/xml'; //設(shè)置回復(fù)的主體 that.body = '<xml>' + '<ToUserName><![CDATA[' + message.FromUserName + ']]></ToUserName>' + '<FromUserName><![CDATA[' + message.ToUserName + ']]></FromUserName>' + '<CreateTime>' + now + '</CreateTime>' + '<MsgType><![CDATA[text]]></MsgType>' + '<Content><![CDATA[你好,同學(xué)!]]></Content>' + '</xml>'; return; } } } } };
tools.js是處理XML數(shù)據(jù)的工具文件:
'use strict'; //引入模塊 var xml2js = require('xml2js'); var Promise = require('bluebird'); //導(dǎo)出解析XML的方法 exports.parseXMLAsync = function(xml) { return new Promise(function(resolve, reject) { xml2js.parseString(xml, { trim: true }, function(err, content) { if (err) { reject(err); } else { resolve(content); }; }); }); }; //因?yàn)関alue值可能是嵌套多層的 所以先對(duì)value值進(jìn)行遍歷 function formatMessage(result) { //聲明空對(duì)象message var message = {}; //對(duì)result類(lèi)型進(jìn)行判斷 if (typeof result === 'object') { //如果是object類(lèi)型 通過(guò)Object.keys()方法拿到result所有的key 并存入keys變量中 var keys = Object.keys(result); //對(duì)keys進(jìn)行循環(huán)遍歷 for (var i = 0; i < keys.length; i++) { //拿到每個(gè)key對(duì)應(yīng)的value值 var item = result[keys[i]]; //拿到key var key = keys[i]; //判斷item是否為數(shù)組或者長(zhǎng)度是否為0 if (!(item instanceof Array) || item.length === 0) { //如果item不是數(shù)組或者長(zhǎng)度為0 則跳過(guò)繼續(xù)向下解析 continue; } //如果長(zhǎng)度為1 if (item.length === 1) { //拿到value值存入val變量 var val = item[0]; //判斷val是否為對(duì)象 if (typeof val === 'object') { //如果val為對(duì)象 則進(jìn)一步進(jìn)行遍歷 message[key] = formatMessage(val); } else { //如果不是對(duì)象 就把值賦給當(dāng)前的key放入message里 并去除收尾空格 message[key] = (val || '').trim(); } } //如果item的長(zhǎng)度既不是0也不是1 則說(shuō)明它是一個(gè)數(shù)組 else { //把message的key設(shè)置為空數(shù)組 message[key] = []; //對(duì)數(shù)組進(jìn)行遍歷 for (var j = 0, k = item.length; j < k; j++) { message[key].push(formatMessage(item[j])); } } } } return message; } exports.formatMessage = function(xml) { return new Promise(function(resolve, reject) { xml2js.parseString(xml, { trim: true }, function(err, content) { if (err) { reject(err); } else { resolve(content); }; }); }); }; exports.formatMessage = formatMessage;
完成這節(jié)的代碼后,當(dāng)關(guān)注微信測(cè)試公眾號(hào)的時(shí)候,會(huì)自動(dòng)回復(fù)『你好,同學(xué)!』的提示信息。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
NodeJS?GRPC?多個(gè)?.proto?文件的處理步驟
本文教程詳細(xì)介紹了在NodeJS環(huán)境中如何使用gRPC框架處理多個(gè).proto文件,步驟包括安裝依賴(lài)、定義.proto文件、生成gRPC代碼、實(shí)現(xiàn)服務(wù)器和客戶(hù)端以及運(yùn)行,適用于開(kāi)發(fā)者在構(gòu)建分布式應(yīng)用時(shí)進(jìn)行接口定義和服務(wù)實(shí)現(xiàn)2024-10-10Node.js重新刷新session過(guò)期時(shí)間的方法
在Node.js中,我們通常使用express-session這個(gè)包來(lái)使用和管理session,保存服務(wù)端和客戶(hù)端瀏覽器之間的會(huì)話(huà)狀態(tài)。那如何才能實(shí)現(xiàn)當(dāng)用戶(hù)刷新當(dāng)前頁(yè)面或者點(diǎn)擊頁(yè)面上的按鈕時(shí)重新刷新session的過(guò)期時(shí)間呢,接下來(lái)通過(guò)本文一起學(xué)習(xí)吧2016-02-02node 利用進(jìn)程通信實(shí)現(xiàn)Cluster共享內(nèi)存
本篇文章主要介紹了node 利用進(jìn)程通信實(shí)現(xiàn)Cluster共享內(nèi)存,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10nodejs連接mongodb數(shù)據(jù)庫(kù)實(shí)現(xiàn)增刪改查
本篇文章主要結(jié)合了nodejs操作mongodb數(shù)據(jù)庫(kù)實(shí)現(xiàn)增刪改查,包括對(duì)數(shù)據(jù)庫(kù)的增加,刪除,查找和更新,有興趣的可以了解一下。2016-12-12nodejs簡(jiǎn)單實(shí)現(xiàn)操作arduino
本文給大家分享的是使用nodejs來(lái)驅(qū)動(dòng)arduino,主要是基于cylonjs 和 gort,有需要的小伙伴可以參考下2016-09-09Node.js實(shí)現(xiàn)http請(qǐng)求服務(wù)與Mysql數(shù)據(jù)庫(kù)操作方法詳解
這篇文章主要介紹了Node.js實(shí)現(xiàn)http請(qǐng)求服務(wù)與Mysql數(shù)據(jù)庫(kù)操作方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-10-10Node.js的項(xiàng)目構(gòu)建工具Grunt的安裝與配置教程
Grunt是為Node打造的項(xiàng)目構(gòu)建工具,相當(dāng)于C/C++世界中的makefile,可以執(zhí)行像壓縮、編譯、單元測(cè)試、代碼檢查以及打包發(fā)布的任務(wù),下面我們就來(lái)一起看一下Node.js的項(xiàng)目構(gòu)建工具Grunt的安裝與配置教程:2016-05-05我用的一些Node.js開(kāi)發(fā)工具、開(kāi)發(fā)包、框架等總結(jié)
這篇文章主要介紹了我個(gè)人使用的一些Node.js開(kāi)發(fā)工具、開(kāi)發(fā)包、框架等總結(jié),需要的朋友可以參考下2014-09-09