• tornado开发的页面跳转到微信小程序


    官方也有一些资料,网上也有不少资料,但是抄起来,不少坑,而且我这边还是python写的,阻力更大,还是我自己总结一下,能给他人带来遍历,那就算欣慰的了。
    使用tornado的问题,要注意文件上传到centos上直接覆盖,重启不一定会生效,这个问题花了我2小时。
    1 微信公众号-功能设置
    注意这里的业务、JS接口安全、网页授权域名,如果加了www.test.cn/mp路径之类,会提示invalid domain,所以最好还是根目录
    2
    于是,配置路由的时候

    (r'/MP_verify_2ge90RVBsxxxx.txt',WxJsCheckHandler),
    
    • 1

    代码比较简单,也就是将微信授权的文件,返回就可以。

    from tornado import web
    class WxJsCheckHandler(web.RequestHandler):
    
        def get(self):
            self.write("2ge90Rxxxx")
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2 微信签名
    这个页面上会用到,通过h5页面跳转到小程序需要微信公众号授权

    import redis
    import string
    import random
    import time
    import json
    import requests
    import hashlib
    from settings import ENV,REDIS_CONFIG,WX_CONFIG
    
    pool = redis.ConnectionPool(host=REDIS_CONFIG[ENV]['host'], port=REDIS_CONFIG[ENV]['port'], db=REDIS_CONFIG[ENV]['db'])
    r = redis.StrictRedis(connection_pool=pool)
    
    class WxService():
    
        def __init__(self):
            self.wx_url = 'https://api.weixin.qq.com/cgi-bin'
    
        def _create_nonce_str(self):
            return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(15))
    
        def _create_timestamp(self):
            return int(time.time())
    
        def get_token(self):
            ACCESS_TOKEN = r.get('wx:ACCESS_TOKEN')
            if ACCESS_TOKEN:
                return ACCESS_TOKEN
            else:
                token_url = self.wx_url + '/token?grant_type=client_credential&appid={}&secret={}' \
                    .format(WX_CONFIG['appid'],WX_CONFIG['secret'])
                res = requests.get(token_url)
                print('请求微信的token is {}'.format(res.text))
                token = json.loads(res.text)
                ACCESS_TOKEN = token.get("access_token")
                r.set('wx:ACCESS_TOKEN', ACCESS_TOKEN, 7200)
                return ACCESS_TOKEN
    
        def get_ticket(self):
            ticket = r.get('wx:ticket')
            if ticket:
                tic = str(ticket, encoding='utf-8')
                return tic
            else:
                token = self.get_token()
                ticket_url = self.wx_url + "/ticket/getticket?access_token={}&type=jsapi".format(token)
                res = requests.get(ticket_url)
                js_ticket = json.loads(res.text)
                ticket = js_ticket.get('ticket')
                r.set('wx:ticket', ticket, 7200)
                return ticket
    
        def sign(self,url):
            jsapi_ticket = self.get_ticket()
            ret = {
                'nonceStr':self._create_nonce_str(),
                'jsapi_ticket': jsapi_ticket,
                'timestamp':self._create_timestamp(),
                'url':url
            }
            string = '&'.join(['%s=%s' % (key.lower(), ret[key]) for key in sorted(ret)]) # 根据字符的ASCII值进行排序,拼接
            ret['signature'] = hashlib.sha1(string.encode('utf-8')).hexdigest() # 对字符串进行sha1加密
            return ret
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    配置路由

    (r"/wx/sign",WxSignHandler),
    
    • 1

    3 前端页面

     # web
        settings = {
            'debug': True,
            'static_path':os.path.join(os.path.dirname(__file__), 'static'),
            'template_path':os.path.join(os.path.dirname(__file__), "views")
        }
        # app
        app = web.Application([
            (r'/MP_verify_2xxxxsxxxx.txt',WxJsCheckHandler),
            (r'/js/(.*)',web.StaticFileHandler,{'path':'assets/js'}),
            (r"/test/xcx",ToTestXcxHandler),
            (r"/test/order/add",OrderHandler),
            (r"/wx/sign",WxSignHandler),
            web.url(provider.authorize_path,TokenHandler,dict(provider=provider)),
            web.url(provider.token_path,TokenHandler,dict(provider=provider)),
            ],
            **settings
        )
       
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    h5页面,signature,timestamp,nonceStr并不是随便填的,signature是会被微信公众号验签的,这一点特别要注意。
    参考了公众号h5网页跳转小程序,但也被误导了,随机数、时间戳与签名是由关系的

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>欢迎使用title>
        <style>
            .home {
                position: relative;
                width: 100%;
                height: 100vh;
            }
            .logo-wrap {
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;
                width: 100%;
                height: 400px;
            }
            .content {
                position: absolute;
                bottom: 123px;
                width: 100%;
                display: flex;
                justify-content: center;
            }
            .tip {
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;
                position: absolute;
                bottom: 32px;
                width: 100%;
                height: 14px;
                color: #666;
                line-height: 14px;
                font-size: 10px;
            }
        style>
        <script src="/js/jquery-3.6.0.min.js">script>
        <script src="/js/jweixin-1.6.0.js">script>
    head>
    <body>
        <div class="home">
            <div class="logo-wrap" id="logoUrl">
                <img src="/static/logo.jpg" style="width:120px;height: 120px"/>
                <div>欢迎使用div>
            div>
            <div class="content" id="content">
            div>
            <div class="tip">
                <div>微信版本要求为:7.0.12及以上div>
                <div>系统版本要求为:iOS 10.3及以上、Android 5.0及以上div>
            div>
        div>
        <script>
            getUrlParam = function (name) {
                var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
                var r = window.location.search.substr(1).match(reg);
                if (r != null) return unescape(r[2]); return null;
            }
            href = window.location.href.split('#')[0]
            console.log(href)
            console.log(window.location.href)
            nsrsbh = getUrlParam('nsrsbh')
            // alert('nsrsbh is '+ nsrsbh)
            console.log('nsrsbh is '+ nsrsbh)
            $.ajax({
                url: '/wx/sign',
                type: 'POST',
                data: JSON.stringify({
                    appId: '微信公众号的appid',
                    url: href
                }),
                async: false,
                dataType: "json",
                contentType : "application/json;charset=UTF-8",
                success: function (res) {
                    console.log(JSON.stringify(res.data))
                    if (res) {
                        wx.config({
                            debug: false,
                            appId: '微信公众号的appid',
                            timestamp: res.data.timestamp,
                            nonceStr: res.data.nonceStr,
                            signature: res.data.signature,
                            jsApiList: ['scanQRCode'], // 必填,随意一个接口即可
                            openTagList:['wx-open-launch-weapp'], // 填入打开小程序的开放标签名
                        })
                        wx.ready(function (){
                            console.log("微信环境准备成功");
                            getContent();
                        })
                        wx.error(function (res) {
                            console.log("微信环境准备失败,原因:"+ res);
                        });
                    }
                },
                error: function(err){
                    console.log("后台接口获取签名异常,原因:", error);
                }
            })
            getContent = function (){
                var color = "#d02329";
                var buttonStyle = "width: 343px; height: 200px; color: #FFFFFF; border-radius: 27px; border: 1px solid " + color +"; "
                    +"background-color: #07ba22; font-size:32px";
      
                var content = '';
                var weappurl = "/pages/index/test?nsrsbh="+nsrsbh;
                var id = "launch-btn";
                content += '+id+'" username="'+"微信小程序的原始ID gh_xxx"+'" path="'+weappurl+'">';
                content += '';
                content += '';
                $("#content").html(content);
                var btn = document.getElementById( 'launch-btn');
                btn.addEventListener( 'launch', function(e) {
                    console.log("调起成功", e.detail);
                });
                btn.addEventListener( 'error', function(e) {
                    console.log("调起失败:", e.detail);
                });
            }
        script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    class ToTestXcxHandler(BaseHandler):
    
        def get(self):
            self.render("test.html")
    
    • 1
    • 2
    • 3
    • 4

    4 微信公众号关注自动回复
    欢迎使用
    1

  • 相关阅读:
    使用 AHK 在 VS Code 中根据上下文自动切换输入法状态
    Python之函数详解
    继承的构造函数
    js数据类型判断的五种方法及归纳。
    初刷leetcode题目(3)——数据结构与算法
    RPC框架性能优化思路和具体实现
    【PID优化】基于蝗虫算法PID控制器优化设计含Matlab源码
    当开发人员无法解决问题时,测试人员应该如何与他们沟通?
    Yii 知识点总结
    一文看懂MySQL的行锁
  • 原文地址:https://blog.csdn.net/warrah/article/details/126617097