node.js 微信開發2-消息回覆、token獲取、自定義菜單

項目結構html

>config/wechat.json 微信公衆號的配置文件node

>controllers/oauth.js 微信網頁受權接口(下一篇再細講講)git

>controllers/wechat.js 微信公衆號接口(包括接入接口和其餘調用微信api的接口)github

>wechat/access_token.json 請求微信api接口以前都須要使用的access_token數據庫

>wechat/crytoGraphy.js 加密解密文件(這裏使用的是明文方式,未用到)json

>wechat/menus.json 微信公衆號的自定義菜單api

>wechat/wechat.js 調用微信api的方法緩存

項目的架構說明和使用步驟能夠參考前面一篇的‘node.js 接口調用示例’:http://www.javashuo.com/article/p-speryjjv-ba.html服務器

 

一些說明點微信

一、公衆號的接入接口和接收消息接口

兩個接口的地址是一致的,區別是:

> 接入接口是GET請求,須要的是對get的接口參數進行解析驗證,而後按需返回就能夠了

> 接收消息接口是POST請求,須要先解析微信發送過來的消息,而後根據狀況決定是否返回消息

WeChat.prototype.handleMsg = async function (ctx) {
            return new Promise((resolve, reject) => {

                // let req = ctx.request;
                // let res = ctx.response;
                let req = ctx.req;
                var buffer = [],
                    that = this;

                //實例微信消息加解密
                // var cryptoGraphy = new CryptoGraphy(that.config,ctx.request);
                //監聽 data 事件 用於接收數據
                req.on('data', function (data) {
                    // logger.info("on data", data);
                    buffer.push(data);
                });
                req.on('end', function () {
                    // logger.info("on end");
                    var msgXml = Buffer.concat(buffer).toString('utf-8');

                    parseString(msgXml, {
                        explicitArray: false
                    }, function (err, result) {
                        // logger.info("on result", result);
                        result = result.xml;
                        resolve(result)
                    })
                });
            })

        },
接收微信消息

* 接收消息使用的是Promise函數封裝,目的是將微信的收發消息兩塊作隔離

* 涉及到node.js 接收post參數的方式,須要ctx.req.on('data',function(){})方法接受參數,並在ctx.req.on(‘end’,function(){})函數中接受最終的獲取參數

* 由於微信發過來的消息是xml格式,因此在node.js 中須要xml2js模塊(非node.js內置模塊,須要先安裝依賴)將xml文件解析

/**
 * 微信消息回覆
 * @param {ctx} context 對象
 * @param {result} 微信消息
 */
WeChat.prototype.responseMsg = function (ctx, result) {
        var toUser = result.ToUserName; //接收方微信
        var fromUser = result.FromUserName; //發送仿微信
        var reportMsg = ""; //聲明回覆消息的變量   
        if (result.MsgType.toLowerCase() === "event") {
            //判斷事件類型
            switch (result.Event.toLowerCase()) {
                case 'subscribe':
                    //回覆消息
                    var content = "歡迎關注 線上隨訪 公衆號\n";
                    content += "咱們致力於幫助出院康復病人與醫生創建便捷的溝通渠道~\n";
                    reportMsg = msg.txtMsg(fromUser, toUser, content);
                    break;
                case 'click':
                    var contentArr = [{
                            Title: "Node.js 微信自定義菜單",
                            Description: "使用Node.js實現自定義微信菜單",
                            PicUrl: "http://img.blog.csdn.net/20170605162832842?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
                            Url: "http://blog.csdn.net/hvkcoder/article/details/72868520"
                        },
                        {
                            Title: "Node.js access_token的獲取、存儲及更新",
                            Description: "Node.js access_token的獲取、存儲及更新",
                            PicUrl: "http://img.blog.csdn.net/20170528151333883?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
                            Url: "http://blog.csdn.net/hvkcoder/article/details/72783631"
                        },
                        {
                            Title: "Node.js 接入微信公衆平臺開發",
                            Description: "Node.js 接入微信公衆平臺開發",
                            PicUrl: "http://img.blog.csdn.net/20170605162832842?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
                            Url: "http://blog.csdn.net/hvkcoder/article/details/72765279"
                        }
                    ];
                    //回覆圖文消息
                    reportMsg = msg.graphicMsg(fromUser, toUser, contentArr);
                    break;
            }
        } else {
            //判斷消息類型爲 文本消息
            if (result.MsgType.toLowerCase() === "text") {
                switch (result.Content) {
                    case '1':
                        reportMsg = msg.txtMsg(fromUser, toUser, 'Hello ,線上隨訪公衆號開通了,快來使用吧……');
                        break;
                    case '2':
                        reportMsg = msg.txtMsg(fromUser, toUser, 'Ha Ha,我還有更多的功能待發掘呢,敬請期待吧……');
                        break;
                    case '文章':
                        var contentArr = [{
                                Title: "Node.js 微信自定義菜單",
                                Description: "使用Node.js實現自定義微信菜單",
                                PicUrl: "http://img.blog.csdn.net/20170605162832842?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
                                Url: "http://blog.csdn.net/hvkcoder/article/details/72868520"
                            },
                            {
                                Title: "Node.js access_token的獲取、存儲及更新",
                                Description: "Node.js access_token的獲取、存儲及更新",
                                PicUrl: "http://img.blog.csdn.net/20170528151333883?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
                                Url: "http://blog.csdn.net/hvkcoder/article/details/72783631"
                            },
                            {
                                Title: "Node.js 接入微信公衆平臺開發",
                                Description: "Node.js 接入微信公衆平臺開發",
                                PicUrl: "http://img.blog.csdn.net/20170605162832842?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
                                Url: "http://blog.csdn.net/hvkcoder/article/details/72765279"
                            }
                        ];
                        //回覆圖文消息
                        reportMsg = msg.graphicMsg(fromUser, toUser, contentArr);
                        break;
                    default:
                        reportMsg = msg.txtMsg(fromUser, toUser, '沒有這個選項哦');
                        break;
                }
                // logger.info("on reportMsg", reportMsg);
            }
        }
        //返回給微信服務器
        ctx.body = reportMsg
    },
回覆微信消息

# 主要任務是根據微信消息的類型‘MsgType’,來執行不一樣的動做

#  事件推送消息(MsgType==‘event’)

* 關注取消事件 subscribe,unsubscribe

  此時能夠作數據庫接入錄入訂閱者信息或者更新訂閱者信息

* 掃描帶參數二維碼事件

  用戶未關注時:qrscene_ +二維碼參數值

  用戶已關注時:scan

* 上報地理位置事件 LOCATION

* 自定義菜單事件 CLICK

# 普通消息推送 (MsgType=='text')

  可分爲 文本消息、圖片消息、語音消息、視頻消息、小視頻消息、地理位置消息、連接消息

 

二、有參考的node.js 發送get、post請求的兩個方法

/**
     * 用於處理 https Get請求方法
     * @param {String} url 請求地址 
     */
    this.requestGet = function (url) {
        return new Promise(function (resolve, reject) {
            https.get(url, function (res) {
                var buffer = [],
                    result = "";
                //監聽 data 事件
                res.on('data', function (data) {
                    buffer.push(data);
                });
                //監聽 數據傳輸完成事件
                res.on('end', function () {
                    result = Buffer.concat(buffer).toString('utf-8');
                    //將最後結果返回
                    resolve(result);
                });
            }).on('error', function (err) {
                reject(err);
            });
        });
    }
node.js 發送get請求
    /**
     * 用於處理 https Post請求方法
     * @param {String} url  請求地址
     * @param {JSON} data 提交的數據
     */
    this.requestPost = function (url, data) {
        return new Promise(function (resolve, reject) {
            //解析 url 地址
            var urlData = urltil.parse(url);
            //設置 https.request  options 傳入的參數對象
            var options = {
                //目標主機地址
                hostname: urlData.hostname,
                //目標地址 
                path: urlData.path,
                //請求方法
                method: 'POST',
                //頭部協議
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'Content-Length': Buffer.byteLength(data, 'utf-8')
                }
            };
            var req = https.request(options, function (res) {
                    var buffer = [],
                        result = '';
                    //用於監聽 data 事件 接收數據
                    res.on('data', function (data) {
                        buffer.push(data);
                    });
                    //用於監聽 end 事件 完成數據的接收
                    res.on('end', function () {
                        result = Buffer.concat(buffer).toString('utf-8');
                        resolve(result);
                    })
                })
                //監聽錯誤事件
                .on('error', function (err) {
                    console.log(err);
                    reject(err);
                });
            //傳入數據
            req.write(data);
            req.end();
        });
    }
}
node.js 發送post請求

  注:使用前須要引用內置的https中間件

 

三、關於access_token

  調用微信接口的時候都須要發送微信接口憑證access_token(除微信網頁受權中獲取的access_token並非這個access_token),所以請求微信接口的第一步都是要先獲取這個access_oken

/**
 * 獲取微信 access_token
 */
WeChat.prototype.getAccessToken = function () {
    var that = this;
    return new Promise(function (resolve, reject) {
        //獲取當前時間 
        var currentTime = new Date().getTime();
        //格式化請求地址
        var url = util.format(that.apiURL.accessTokenApi, that.apiDomain, that.appID, that.appScrect);
        //判斷 本地存儲的 access_token 是否有效
        if (accessTokenJson.access_token === "" || accessTokenJson.expires_time < currentTime) {
            that.requestGet(url).then(function (data) {
                var result = JSON.parse(data);
                if (data.indexOf("errcode") < 0) {
                    accessTokenJson.access_token = result.access_token;
                    accessTokenJson.expires_time = new Date().getTime() + (parseInt(result.expires_in) - 200) * 1000;
                    //更新本地存儲的
                    fs.writeFile('./wechat/access_token.json', JSON.stringify(accessTokenJson));
                    //將獲取後的 access_token 返回
                    resolve(accessTokenJson.access_token);
                } else {
                    //將錯誤返回
                    resolve(result);
                }
            });
        } else {
            //將本地存儲的 access_token 返回
            resolve(accessTokenJson.access_token);
        }
    });
}
獲取access_token
var url = util.format(that.apiURL.accessTokenApi, that.apiDomain, that.appID, that.appScrect);

url參數依次爲:

that.apiURL.accessTokenApi:"%scgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"
that.apiDomain:"https://api.weixin.qq.com/"
that.appID:公衆號的appID
that.appScrect:公衆號的密匙

請求後獲取的accee_token 有效時間爲7200s,在此時間內,再次請求微信接口均可以使用已經緩存的accee_token

 

四、關於菜單

  最好是在請求接口的時候加入請求密匙(如oauth2認證),尤爲是更新和刪除

 

git:https://github.com/wuyongxian20/wechat-api

相關文章
相關標籤/搜索