nodejs微信公衆號開發——2.自動回覆

上一篇文章: nodejs微信公衆號開發(1)接入微信公衆號,本篇文章將在此基礎上實現簡單的回覆功能。(項目github地: https://github.com/Panfen/wem... )

1. 接入代碼的優化

以前咱們簡單粗暴的實現了微信公衆號的接入,接入的代碼直接寫在了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

2. 獲取access_token

access_token是開發程序與wexin公衆平臺交互的一把鑰匙,調用絕大部分接口都須要用到 access_token
access_token的特色:
  • 有效期爲2小時(7200s),過時自動失效,須要從新獲取;
  • 只要更新了access_token,以前的access_token自動失效;

解決方案:github

  • 系統每隔2小時自動去獲取一下access_token的值,確保access_token始終是有效的;
  • 爲了方便頻繁調用,將access_token存儲在惟一的一個地方(數據庫、文件等),全部子系統都能訪問。

程序中採用構造函數的方式,在生成實例,完成初始化工做的的過程當中,讀取存儲在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中實例化一個Wechatjson

var wechat = new Wechat(opts);

這樣確保了每次程序啓動都會獲取對access_token的有效性進行檢驗,而且每一個一段時間會自動獲取一個新的access_tokensegmentfault

3. 處理微信消息的步驟

不管是事件推送仍是消息推送,微信服務器都是以post的方式發送請求,推送的數據類型不是json而是xml,處理推送消息通常分爲五個步驟:數組

  • 處理POST類型的控制邏輯,接收xml數據包;
  • 解析數據包,獲取數據包的消息類型或數據類型;
  • 拼裝自定義的消息;
  • 包裝成xml格式;
  • 在5秒鐘內返回消息。

3.1 接收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);

圖片描述

3.2 解析xml數據

使用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);
        })
    });
}

圖片描述

3.3 格式化xml數據

從解析的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;
}

圖片描述

3.4 判斷消息類型並回復

這裏針對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;
    }
}

注:這裏只是簡單地實現一下自動回覆功能,這種拼接字符串的方式仍是很不方便的,後面會封裝成接口。
使用手機微信掃描測試帳號的二維碼,便可關注,同時接收到測試公衆號推送的消息!

圖片描述

啦啦,一個簡單的關注回覆就完成了。

相關文章
相關標籤/搜索