美文网首页前端课外拓展
koa2微信公众号开发及JS-SDK的使用

koa2微信公众号开发及JS-SDK的使用

作者: 快乐小码仔 | 来源:发表于2020-05-26 09:34 被阅读0次

    微信公众号项目介绍

    当前只对实现思路进行说明,不作基础介绍!详细信息查看微信官方文档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接口安全域名”。

      备注:登录后可在“开发者中心”查看对应的接口权限。

    • 步骤三:通过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 lang="en">
    
    <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 lang="en">
    
    <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>
    

    结果如下:

    360截图17321128447023.png

    相关文章

      网友评论

        本文标题:koa2微信公众号开发及JS-SDK的使用

        本文链接:https://www.haomeiwen.com/subject/lfvyahtx.html