学习分享|Vue3项目中使用微信SDK开发微信网页

grtsinry43
7/3/2024(更新于 1/28/2025
161 views
预计阅读时长 15 分钟

最近在开发一个微信 H5 的项目,采用的是 Vue3+FastAPI,正好学习下微信用户登录,api 配置和调用,以及前后端关于这部分的解决方案

<!--more-->

对微信网页开发的探索起源于最近的一个项目,需要一方面调用微信登录获取用户信息和将 token 作为区分用户的方式,另一方面要调用微信网页的 API 实现定位的操作

于是我查询微信官方开发文档,还有一些相关资料,按照自己理解研究了相关的解决方案

比较蛋疼的是,由于微信开发调用 API 时都会校验当前域名,因此被迫开发时进行线上测试(!!),目前个人没有解决方案,如果有了解的大佬恳请在评论处告知,不胜感激!!

安装及引入

首先微信的 js-sdk 是可以通过 npm 获取的:weixin-js-sdk

在项目中直接通过以下命令安装:

SHELL
1npm install weixin-js-sdk

之后就可以在项目中直接引用

JS
1import wx from 'weixin-js-sdk';

image-20240703141306717

此时你可以发现已经能正常调用并且补全了(好耶)

项目背景

首先前端我使用的 Vite+Vue3,后端是 FastAPI,为了在页面打开时就载入相关配置,我们需要在 main.js 执行相关逻辑

首先规定相关的接口逻辑

url定义
/api/wxconfig/get?url =获取相关配置
/api/wxconfig/flush?url =强制刷新配置

前端配置

这部分比较简单,无脑写就行,当测试完成之后,可以添加逻辑,让 wx.error 时候调用强制刷新的 API,这个具体自己设计就好

新建 util/wx-api-config.js

JS
1import wx from 'weixin-js-sdk';
2import {getWxConfig} from "@/api/wxConfig.js";
3
4console.log("微信 JS-SDK 配置开始...")
5
6// 微信 API 全局配置
7getWxConfig(location.href.split('#')[0]).then(res => {
8    console.log(res)
9    const {appId, timestamp, nonceStr, signature} = res;
10    wx.config({
11        debug: process.env.NODE_ENV === 'development',
12        appId,
13        timestamp,
14        nonceStr,
15        signature,
16        jsApiList: []
17    });
18});
19
20wx.ready(function(){
21    // config 信息验证后会执行 ready 方法,所有接口调用都必须在 config 接口获得结果之后,
22    // config 是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,
23    // 则须把相关接口放在 ready 函数中调用来确保正确执行。
24    // 对于用户触发时才调用的接口,则可以直接调用,不需要放在 ready 函数中。
25    console.log("微信 JS-SDK 配置成功!")
26});
27
28wx.error(function(err){
29    console.log("微信 JS-SDK 配置失败:", err);
30});
31
32
33

以及 axios 的配置,注意我这里使用了响应拦截器进行了统一处理,返回时已经直接是 data 的内容

api/wxConfig.js

JS
1import ins from "@/api/request.js";
2
3export function getWxConfig(url) {
4    return ins.get("/wxconfig/get",{
5        params: {
6            url
7        }
8    })
9}
10

最后只需要在 main.js 中调用即可:

JS
1// 配置微信 API
2import "@/util/wx-api-config.js";

后端配置

重头戏肯定在这里哇,首先呢,我们先了解一下 config 都有哪些部分:

让我们把目光再转向刚才的配置

JS
1wx.config({
2        debug: true,
3        appId,
4        timestamp,
5        nonceStr,
6        signature,
7        jsApiList: []
8    });

其中有:是否开发模式,公众号唯一 id,生成签名的时间戳,生成签名的随机字符串,签名,要调用的 api 列表

准备好你的信息~,就像这样:

PYTHON
1wx_app_cfg = {
2    'appid': 'xxxxxxxxxxxxxxxxxx',
3    'secret': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
4}

对于这些内容,我们继续了解微信这个签名逻辑

首先,要带上 appidappscrect 请求微信接口拿到 access_token,有效时长 7200

PYTHON
1def get_access_token(appid: str, appsecret: str) -> str:
2    """调用微信API获取access_token"""
3    url = f"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={appsecret}"
4    response = requests.get(url)
5    data = response.json()
6    return data.get("access_token")

之后获取 jsapi_ticket

PYTHON
1def get_jsapi_ticket(access_token: str) -> str:
2    """调用微信API使用access_token获取jsapi_ticket"""
3    url = f"https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={access_token}&type=jsapi"
4    response = requests.get(url)
5    data = response.json()
6    return data.get("ticket")

有了这两个东西,就可以开始签名啦~

PYTHON
1def generate_nonce_str(length: int = 16) -> str:
2    """生成随机字符串,用于签名"""
3    characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
4    return ''.join(random.choice(characters) for _ in range(length))
5
6
7def generate_signature(ticket: str, noncestr: str, timestamp: int, url: str) -> str:
8    """使用sha1生成签名"""
9    string1 = f"jsapi_ticket={ticket}&noncestr={noncestr}&timestamp={timestamp}&url={url}"
10    return hashlib.sha1(string1.encode('utf-8')).hexdigest()

这样我们就得到了签名,但是这还没有结束,根据微信文档的介绍:

access_token 是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用 access_token。开发者需要进行妥善保存。access_token 的存储至少要保留 512 个字符空间。access_token 的有效期目前为 2 个小时,需定时刷新,重复获取将导致上次获取的 access_token 失效。

公众平台的 API 调用所需的 access_token 的使用及生成方式说明:

1、建议公众号开发者使用中控服务器统一获取和刷新 access_token,其他业务逻辑服务器所使用的 access_token 均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突,导致 access_token 覆盖而影响业务;

2、目前 access_token 的有效期通过返回的 expires_in 来传达,目前是 7200 秒之内的值。中控服务器需要根据这个有效时间提前去刷新新 access_token。在刷新过程中,中控服务器可对外继续输出的老 access_token,此时公众平台后台会保证在 5 分钟内,新老 access_token 都可用,这保证了第三方业务的平滑过渡;

3、access_token 的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新 access_token 的接口,这样便于业务服务器在 API 调用获知 access_token 已超时的情况下,可以触发 access_token 的刷新流程。

因此我们需要提供缓存的能力,这里使用 mongoengine 处理相关逻辑

PYTHON
1def save_and_expire_handle(force=False):
2    """
3    每次调用的时候,首先检查mongo中是否有缓存(mongoengine),如果有,检查是否过期,如果过期,重新获取并且存储,如果没有,直接获取
4    """
5    current_time = int(time.time())
6    config = WXConfig.objects().first()  # 因为只会有一条记录,所以直接获取第一条应该就可以了
7
8    if config and config.expires_at > current_time and not force:
9        # 如果配置存在且未过期,直接返回
10        return {
11            "access_token": config.access_token,
12            "jsapi_ticket": config.jsapi_ticket
13        }
14    else:
15        # 如果配置不存在或已过期,重新获取并存储
16        appid = wx_app_cfg['appid']
17        appsecret = wx_app_cfg['secret']
18        print("==重新调用微信API获取access_token和jsapi_ticket==")
19        access_token = get_access_token(appid, appsecret)
20        jsapi_ticket = get_jsapi_ticket(access_token)
21        expires_at = current_time + 7000  # 这里过期时间应该是 7200 秒,但是为了保险起见,提前 200 秒过期并重新获取
22
23        if config:
24            # 更新现有记录
25            config.update(access_token=access_token, jsapi_ticket=jsapi_ticket, expires_at=expires_at)
26        else:
27            # 创建新记录
28            WXConfig(access_token=access_token, jsapi_ticket=jsapi_ticket, expires_at=expires_at).save()
29
30        return {
31            "access_token": access_token,
32            "jsapi_ticket": jsapi_ticket
33        }

这样,我们就可以实现缓存 access_tokenjsapi_ticket,并且在每次都检查是否过期减少请求次数了。

诶?为什么要留一个 force 参数呢?

在实际开发中,如果存在多端测试,可能在未过期时候就会刷新 access_tokenjsapi_ticket,这样之前的存储就会过期,因此需要提供一个强制刷新的接口

按照前端需要的内容封装好函数,就有:

PYTHON
1def get_wx_config(appid: str, appsecret: str, url: str, force=False) -> Dict[str, str]:
2    """获取微信JS-SDK配置"""
3    # 首先从数据库或者新获取 access_token 和 jsapi_ticket
4    # 这里就一个需要 force 即可,因为 access_token 和 jsapi_ticket 是一起获取的
5    access_token = save_and_expire_handle(force)['access_token']
6    jsapi_ticket = save_and_expire_handle()['jsapi_ticket']
7    # 生成签名所需的参数
8    noncestr = generate_nonce_str()
9    timestamp = int(time.time())
10    # 生成签名
11    signature = generate_signature(jsapi_ticket, noncestr, timestamp, url)
12
13    return {
14        "appId": appid,
15        "timestamp": timestamp,
16        "nonceStr": noncestr,
17        "signature": signature
18    }

最后注册路由接口,FastAPI,启动!!

PYTHON
1@wxconfig_route.get('/get')
2async def return_wx_config(url: str):
3    """
4    获取微信JS-SDK配置
5    """
6    # 生成微信 JS-SDK 配置
7    wx_config = get_wx_config(wx_app_cfg['appid'], wx_app_cfg['secret'], url)
8
9    return trueReturn(wx_config)
10
11
12@wxconfig_route.get('/flush')
13async def flush_wx_config(url):
14    """
15    这里保留了一个接口用于特殊情况,强制刷新微信JS-SDK配置
16    """
17    # 生成微信 JS-SDK 配置
18    wx_config = get_wx_config(wx_app_cfg['appid'], wx_app_cfg['secret'], url, force=True)
19
20    return trueReturn(wx_config)

测试

按照之前我说的,测试过程比较蛋疼,必须是正式域名的线上环境,可以用微信开发者工具测试一下,测试时候可以将 debug 写死成 true 便于发现问题


这里留个坑(?!又来),之后详细讲一下测试过程

相关推荐

学习分享|跨域解决、安卓开发探索、油猴脚本探索

最近学习的一些内容,包括跨域问题及其解决方案,安卓开发的简单探索,OpenAI的api做了个小插...

grtsinry43
6/10/2024
35
0
0

折腾记录|使用 Nuxt.js 重写个人主页,使用 SSR 优化 SEO ,实现一些期待已久的效果

在 22 年刚创建个人主页的时候,由于我的技术水平不够,只能用一些 wordpress type...

grtsinry43
9/19/2024
24
0
0

用一个月的时间写一个自己的博客系统——Grtblog的技术介绍

终于,历时一个多月的开发 ~~bug~~ 和测试,这个目前问题很多很不成熟很难用的系统终于上线了.....

grtsinry43
12/14/2024
117
4
0

使用 BeautifulSoup 配合请求库实现简单的爬虫程序

事情起源于课内的课程实验作业...因为要求要用爬虫,~~不必说课内讲的一言难尽,更不必说就算讲了我也...

grtsinry43
1/7/2025
67
0
0

B 站直播弹幕的 WebSocket 获取尝试与抽奖程序实现

网上对于 B 站的直播 ws 协议研究已经很多了,但是一是相互 copy 未形成完整的解决方案,二是...

grtsinry43
1/17/2025
99
0
0
COMMENT 7273217769506738176

发表评论

登录之后评论体验更好哦 ~
支持 Markdown 语法 0 / 3000

在风雨飘摇之中

本站已运行了

一路走来,感谢陪伴与支持

愿我们不负热爱,继续前行

全站通知
更新通知