Node.js+Koa開發微信公衆號我的筆記(二)響應事件

微信公衆號中的事件有訂閱事件/掃碼事件/點擊事件/跳轉連接事件等等,具體能夠查閱文檔。json

這裏來實現一下訂閱事件,其餘的事件的實現過程也都相似。服務器

當有人訂閱了公衆號後,微信服務器會向咱們的服務器推送一個事件,這個事件是XML格式的數據包。微信

1、咱們在index路由下實現post事件的響應。

能夠理解爲當微信向咱們的服務器推送消息時,消息就會先走到這裏。app

/routes/index.js加入:異步

router.post('/', index_middleware.post(config.wechat));

2、實現index_middleware中的post方法:

就是咱們該如何處理微信服務器給咱們的服務器的post請求。ide

大體過程是先把微信服務器發來的數據包接收到,而後解析這個XML數據包,而後根據接收來的數據,在咱們服務器上進行邏輯處理後造成一個XML格式的回覆消息。函數

/wechat/index_middleware.js加入:工具

exports.post = 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);

        if (sha !== signature) {
            this.body = 'wrong';
            return false;
        }

        var data = yield getRawBody(this.req, {
            length: this.length,
            limit: '1mb',
            encoding: this.charset
        });

        var message = yield util.parseXMLAsync(data);
        var xml = yield autoReply(message.xml, wechat);
        console.log(message);
        console.log(xml);

        this.status = 200;
        this.type = 'application/xml';
        this.body = xml;
    };
};

這裏的data就是咱們接收到的數據包,使用了raw-body這個組件。同時使用了util這個工具函數和autoReply這個自定義的函數。post

所以在/wechat/index_middleware.js中須要加入如下代碼:ui

var getRawBody = require('raw-body');
var util = require('./util');
var autoReply = require('./autoReply');

3、實現util工具函數中的parseXMLAsync方法,它的做用是異步地將xml格式化爲json數據。

/wechat/util.js:

var fs = require('fs');
var xml2js = require('xml2js');

exports.parseXMLAsync = function(xml) {
    return new Promise(function(resolve, reject) {
        xml2js.parseString(xml, {
            trim: true,
            explicitArray: false
        }, function(err, content) {
            if (err) {
                reject(err);
            }
            resolve(content);
        });
    });
};

這裏用到了xml2js這個組件。

4、實現autoReply函數,它的做用是將微信服務器發來的數據包進行邏輯處理,造成咱們須要發送給微信的XML格式數據。

/wechat/autoReply.js:

var createXML = require('./createXML');

function autoReply(message, wechat) {
    if (message.MsgType === 'event') {
        if (message.Event === 'subscribe') {
            if (message.EventKey) {
                console.log('掃碼進入');
            }
            var now = new Date().getTime();
            return Promise.resolve(createXML({
                ToUserName: message.FromUserName,
                FromUserName: message.ToUserName,
                MsgType: 'text',
                Content: 'Hello!!'
            }));
        }else if (message.Event === 'unsubscribe') {
            console.log('取關');
            return Promise.resolve('');
        }
    }
}

這裏只實現了關注和取關的事件,同時用到了createXML函數,它的做用是將數據封裝成xml格式:

/wechat/createXML.js:

function createXML(messageObj) {
    var { ToUserName, FromUserName, MsgType = 'text'} = messageObj;
    var CreateTime = new Date().getTime();
    var header = `<xml>
                    <ToUserName><![CDATA[${ToUserName}]]></ToUserName>
                    <FromUserName><![CDATA[${FromUserName}]]></FromUserName>
                    <CreateTime>${CreateTime}</CreateTime>
                    <MsgType><![CDATA[${MsgType}]]></MsgType>`;
    var content = '';
    switch(MsgType) {
        case 'text':
            var { Content } = messageObj;
            content = `<Content><![CDATA[${Content}]]></Content>
                     </xml>`;
            break;
        case 'image':
            var { MediaId }  = messageObj;
            content = `<Image>
                         <MediaId><![CDATA[${MediaId}]]></MediaId>
                       </Image>
                     </xml>`;
            break;
        case 'voice':
            var { MediaId } = messageObj;
            content = `<Voice>
                         <MediaId><![CDATA[${MediaId}]]></MediaId>
                       </Voice>
                     </xml>`;
            break;
        case 'video':
            var { MediaId, Title, Description } = messageObj;
            content = `<Video>
                         <MediaId><![CDATA[${MediaId}]]></MediaId>
                         <Title><![CDATA[${Title}]]></Title>
                         <Description><![CDATA[${Description}]]></Description>
                       </Video> 
                     </xml>`;
            break;
        case 'music':
            var { Title, Description, MusicUrl, HQMusicUrl, ThumbMediaId } = messageObj;
            content = `<Music>
                         <Title><![CDATA[${Title}]]></Title>
                         <Description><![CDATA[${Description}]]></Description>
                         <MusicUrl><![CDATA[${MusicUrl}]]></MusicUrl>
                         <HQMusicUrl><![CDATA[${HQMusicUrl}]]></HQMusicUrl>
                         <ThumbMediaId><![CDATA[${ThumbMediaId}]]></ThumbMediaId>
                       </Music>
                     </xml>`;
            break;
        case 'news':
            var { Articles } = messageObj;
            var ArticleCount = Articles.length;
            content = `<ArticleCount>${ArticleCount}</ArticleCount><Articles>`;
            for (var i = 0; i < ArticleCount; i++) {
                content += `<item>
                                <Title><![CDATA[${Articles[i].Title}]]></Title>
                                <Description><![CDATA[${Articles[i].Description}]]></Description>
                                <PicUrl><![CDATA[${Articles[i].PicUrl}]]></PicUrl>
                                <Url><![CDATA[${Articles[i].Url}]]></Url>
                              </item>`;
            }
            content += '</Articles></xml>';
            break;
        default:
            content = `<Content><![CDATA[Error]]></Content>
                     </xml>`;
    }
    
    var xml = header + content;
    return xml;
}

module.exports = createXML;

完成以上工做之後,啓動咱們的服務器,再次關注公衆號,應該能收到一條Hello!!的信息。

這樣就實現了對關注事件的處理。

相關文章
相關標籤/搜索