天天看點

koa2微信公衆号開發及JS-SDK的使用

微信公衆号項目介紹

目前隻對實作思路進行說明,不作基礎介紹!詳細資訊檢視微信官方文檔及git位址!

實作功能

  1. 新關注自動回複;
  2. 被動回複;
  3. 推送消息;
  4. 自定義菜單;
  5. js-sdk使用;
  6. 網頁授權擷取使用者資訊

koa2環境搭建

  1. koa-generator的安裝
    cnpm install -g koa-generator
               
  2. koa2項目建立
    koa2 -e wechat
               

使用飛鴿内網穿透

飛鴿

使用比較簡單就不作詳細介紹了!

接入微信公衆平台開發

編寫reply中間件;建立reply檔案夾,進入檔案夾建立index檔案!

安裝所需依賴:

cnpm i -S sha1
           

在app.js入口檔案中路由配置前引入中間件!

...
const reply = require("./reply")

...

//接收處理所有消息
app.use(reply());

// routes
app.use(index.routes(), index.allowedMethods())

...
           

驗證消息的确來自微信伺服器

微信伺服器會發送兩種類型的消息給開發者伺服器.

開發者通過檢驗signature對請求進行校驗(下面有校驗方式)。若确認此次GET請求來自微信伺服器,請原樣傳回echostr參數内容,則接入生效,成為開發者成功,否則接入失敗。

  1. GET請求

    -驗證伺服器的有效性

    1)将token、timestamp、nonce三個參數進行字典序排序

    2)将三個參數字元串拼接成一個字元串進行sha1加密

    3)開發者獲得加密後的字元串可與signature對比,辨別該請求來源于微信

  2. POST請求

    -微信伺服器會将使用者發送的資料以post請求的方式轉發到開發者伺服器上

const config = require("../config/config")
const sha1 = require("sha1");

module.exports = () => {
    return async(ctx, next) => {
        const { signature, timestamp, nonce, echostr } = ctx.query;
        const token = config.wechat.token;

        let str = [token, timestamp, nonce].sort().join('');
        const sha = sha1(str);

        if (ctx.method === "GET" && sha === signature) {
            //如果一樣說明消息來自于微信伺服器,傳回echostr給微信伺服器
            ctx.body = echostr;

        } else if (ctx.method === "POST" && sha === signature) {

        } else {
            await next();
            // ctx.body = "Failed"
        }
    }
}

           

在測試号中進行配置,若無誤會提示配置成功!

360截圖167401157264113_看圖王.png

繼續處理post請求,并實作回複消息

安裝依賴raw-body和xml2js:

cnpm i -S raw-body xml2js
           

處理POST請求,具體實作方法移步源碼檢視!

...
const getRawBody = require("raw-body");
const { parseXML, formatMessage, tpl2xml } = require("../utils/tool")
const reply = require("./reply")

...

const data = await getRawBody(ctx.req, {
    length: ctx.length,
    limit: "1mb",
    encoding: ctx.charset
})
const content = await parseXML(data);
// console.log(content);
const message = formatMessage(content.xml);
// console.log(message);
let replyBody = await reply(message);
// console.log(replyBody);
//生成xml資料
let xml = tpl2xml(replyBody, message);
// console.log(xml)
ctx.status = 200;
ctx.type = 'application/xml';

ctx.body = xml;

...
           

reply.js

/**
 * 處理使用者發送的消息和内容,傳回不同的内容給使用者
 */
module.exports = async(message) => {
    let replyBody = "您在說什麼,我聽不懂!";

    if (message.MsgType === "text") {
        let content = message.Content;
        if (content === "熱門") {
            replyBody = "熱門";
        } else if (content === "2") {
            replyBody = "落地成盒";
        } else if (content.match("愛")) {
            replyBody = "我愛你~";
        }
    } else if (message.MsgType === "voice") {
        options.msgType = "voice";
        options.mediaId = message.MediaId;
        console.log(message.Recognition);
    } else if (message.MsgType === "event") {
        if (message.Event === "subscribe") {
            replyBody = "歡迎您的關注~\n" +
                "回複 首頁 檢視電影預告片頁面\n" +
                "回複 熱門 檢視最新熱門電影\n" +
                "回複 文本 檢視指定電影資訊\n" +
                "回複 語音 檢視指定電影資訊\n" +
                "也可以點選下面的菜單按鈕來了解其它資訊~";
        } else if (message.Event === "unsubscribe") {
            console.log("使用者取消關注了!")
        } else if (message.Event === "CLICK") {
            replyBody = "您可以按照以下提示來進行操作~\n" +
                "回複 首頁 檢視電影預告片頁面\n" +
                "回複 熱門 檢視最新熱門電影\n" +
                "回複 文本 檢視指定電影資訊\n" +
                "回複 語音 檢視指定電影資訊\n" +
                "也可以點選下面的菜單按鈕來了解其它資訊~";
        }
    }

    return replyBody;
}
           

自定義菜單及微信JS-SDK分享接口執行個體

根目錄建立wechat檔案夾,進入檔案夾建立menu.js和wechat.js檔案。

wechat.js封裝了access_token、jsapi_ticket、建立和删除菜單!

自定義菜單

/*routes/index.js*/

...
// 建立執行個體對象
const Wechat = require("../wechat/wechat")
const wechatApi = new Wechat();

//menu.js檔案重新配置菜單
router.get('/updateMenu', async(ctx, next) => {
    let result = await wechatApi.createMenu(menu);
    ctx.body = result
})

...

           

JSSDK使用步驟

  • 步驟一:綁定域名

    先登入微信公衆平台進入“公衆号設定”的“功能設定”裡填寫“JS接口安全域名”。

    備注:登入後可在“開發者中心”檢視對應的接口權限。

  • 步驟二:引入JS檔案

    在需要調用JS接口的頁面引入如下JS檔案,(支援https):http://res.wx.qq.com/open/js/jweixin-1.6.0.js

    如需進一步提升服務穩定性,當上述資源不可通路時,可改通路:http://res2.wx.qq.com/open/js/jweixin-1.6.0.js (支援https)。

    備注:支援使用 AMD/CMD 标準子產品加載方法加載

  • 步驟三:通過config接口注入權限驗證配置

    所有需要使用JS-SDK的頁面必須先注入配置資訊,否則将無法調用(同一個url僅需調用一次,對于變化url的SPA的web app可在每次url變化時進行調用,目前Android微信用戶端不支援pushState的H5新特性,是以使用pushState來實作web app的頁面會導緻簽名失敗,此問題會在Android6.2中修複)。

    wx.config({
    debug: true, // 開啟調試模式,調用的所有api的傳回值會在用戶端alert出來,若要檢視傳入的參數,可以在pc端打開,參數資訊會通過log打出,僅在pc端時才會列印。
    appId: '', // 必填,公衆号的唯一辨別
    timestamp: , // 必填,生成簽名的時間戳
    nonceStr: '', // 必填,生成簽名的随機串
    signature: '',// 必填,簽名
    jsApiList: [] // 必填,需要使用的JS接口清單
    });
               
  • 步驟四:通過ready接口處理成功驗證
    wx.ready(function(){
    // config資訊驗證後會執行ready方法,所有接口調用都必須在config接口獲得結果之後,config是一個用戶端的異步操作,是以如果需要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確定正确執行。對于使用者觸發時才調用的接口,則可以直接調用,不需要放在ready函數中。
    });
               
  • 步驟五:通過error接口處理失敗驗證
    wx.error(function(res){
    // config資訊驗證失敗會執行error函數,如簽名過期導緻驗證失敗,具體錯誤資訊可以打開config的debug模式檢視,也可以在傳回的res參數中檢視,對于SPA可以在這裡更新簽名。
    });
               

建立jssdk路由權限簽名

/*routes/index.js*/

...
const { appID } = require("../config/config").wechat;
...
//用于JS-SDK使用權限簽名算法
router.get('/jssdk', async(ctx, next) => {
    /* JS-SDK使用權限(簽名算法)
          簽名生成規則如下:參與簽名的字段包括noncestr(随機字元串),
          有效的jsapi_ticket, timestamp(時間戳), url(目前網頁的URL,不包含#及其後面部分) 。
    */
    //擷取傳入的url
    let url = ctx.query.url;
    const { ticket } = await wechatApi.fetchTicket();
    const nonceStr = Math.random().toString().split(".")[1];
    const timestamp = Date.now();
    const arr = [`jsapi_ticket=${ticket}`, `noncestr=${nonceStr}`, `timestamp=${timestamp}`, `url=${url}`];
    const str = arr.sort().join("&");
    const signature = sha1(str);

    ctx.body = {
        appId: appID,
        signature,
        nonceStr,
        timestamp
    }
})
...
           

前端使用

<!-- /*public/test.html*/ -->
<!DOCTYPE html>
<html >

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://libs.cdnjs.net/zepto/1.2.0/zepto.min.js"></script>
    <script src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
</head>

<body>
    <div>123</div>
    <script>
        $(function() {
            let shareUrl = location.href.split('#')[0];
            $.ajax({
                url: "http://caorui.max.svipss.top/jssdk",
                data: {
                    url: shareUrl
                },
                success: function(data) {
                    wx.config({
                        //debug: true, // 開啟調試模式,調用的所有api的傳回值會在用戶端alert出來,若要檢視傳入的參數,可以在pc端打開,參數資訊會通過log打出,僅在pc端時才會列印。
                        appId: data.appId, // 必填,公衆号的唯一辨別
                        timestamp: data.timestamp, // 必填,生成簽名的時間戳
                        nonceStr: data.nonceStr, // 必填,生成簽名的随機串
                        signature: data.signature, // 必填,簽名
                        jsApiList: [ // 必填,需要使用的JS接口清單
                            "updateAppMessageShareData",
                            "updateTimelineShareData"
                        ]
                    });

                    wx.ready(function() {
                        wx.updateAppMessageShareData({
                            title: '分享測試12dsd', // 分享标題
                            desc: '分享描述cgngn', // 分享描述
                            link: shareUrl, // 分享連結
                            imgUrl: '分享圖示', // 分享圖示
                            success: function() {
                                // 使用者确認分享後執行的回調函數
                            }
                        });
                        wx.updateTimelineShareData({
                            title: '分享測試12fsf', // 分享标題
                            link: shareUrl, // 分享連結
                            imgUrl: '分享圖示', // 分享圖示
                            success: function() {
                                // 使用者确認分享後執行的回調函數
                            }
                        });
                        wx.error(function(res) {
                            alert(res.errMsg); // 正式環境記得關閉啊!!!!
                        });
                    })
                }
            })
        })
    </script>
</body>

</html>

           

網頁授權擷取使用者資訊

微信官方文檔

具體而言,網頁授權流程分為四步:

1、引導使用者進入授權頁面同意授權,擷取code

2、通過code換取網頁授權access_token(與基礎支援中的access_token不同)

3、如果需要,開發者可以重新整理網頁授權access_token,避免過期

4、通過網頁授權access_token和openid擷取使用者基本資訊(支援UnionID機制)

在routes/index.js添加路由

//微信網頁授權擷取code
router.get("/oauth", async(ctx, next) => {
    let redirect_uri = `http%3a%2f%2fcaorui.max.svipss.top/oauth.html`;
    ctx.redirect(`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appID}&redirect_uri=${redirect_uri}&response_type=code&scope=snsapi_userinfo&state=STATE&connect_redirect=1#wechat_redirect`)
})

//擷取授權後的使用者資訊
router.get("/getUserInfo", async(ctx, next) => {
    //擷取code值
    let code = ctx.query.code;
    if (!code) {
        ctx.redirect('http://caorui.max.svipss.top/oauth')
    }
    let result = await wechatApi.getOauthAccessToken(code);
    let data = await wechatApi.getOauthUserinfo(result.access_token, result.openid);

    ctx.body = data;
})
           

通過code擷取AccessToken 和 擷取授權後的使用者資料 的方法檢視wechat.js檔案!

在public/oauth.html中使用:

<!DOCTYPE html>
<html >

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://libs.cdnjs.net/zepto/1.2.0/zepto.min.js"></script>
    <script src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
</head>

<body>
    <div>
        <button id="oauth">擷取使用者資訊</button>
        <div id="userInfo"></div>
    </div>
    <script>
        $(function() {
            //擷取url參數
            function getQueryVariable(variable) {
                var query = window.location.search.substring(1);
                var vars = query.split("&");
                for (var i = 0; i < vars.length; i++) {
                    var pair = vars[i].split("=");
                    if (pair[0] == variable) {
                        return pair[1];
                    }
                }
                return (false);
            }

            var userinfo = JSON.parse(localStorage.getItem("userinfo"));

            // console.log(userinfo)

            if (userinfo) {
                $("#oauth").hide();
                var html = `
                            <image src="${userinfo.headimgurl}"/>
                            <h2>你已經登入</h2>
                            `;
                $("#userInfo").html(html);
                return;
            }

            $("#oauth").on("click", function() {
                location.href = "http://caorui.max.svipss.top/oauth";
            })

            var code = getQueryVariable("code");
            if (code) {
                $.ajax({
                    url: "http://caorui.max.svipss.top/getUserInfo",
                    data: {
                        code
                    },
                    success: function(data) {
                        $("#oauth").hide();
                        // console.log(data)
                        localStorage.setItem("userinfo", JSON.stringify(data));
                        var html = `
                                <image src="${data.headimgurl}"/>
                                <p>nickname:${data.nickname}</p>
                                <p>country:${data.country}</p>
                                <p>province:${data.province}</p>
                                <p>city:${data.city}</p>
                                <p>openid:${data.openid}</p>
                            `
                        $("#userInfo").html(html)
                    }
                })
            }


        })
    </script>
</body>

</html>
           

結果如下:

koa2微信公衆号開發及JS-SDK的使用

 轉自https://www.jianshu.com/p/2006cf85b1cf

繼續閱讀