企业微信公众号开发笔记
了解公众号开发
参考:
首先要申请通过一个微信公众平台接口测试帐号,可免费调用接口等操作:
需搭建一个80/443端口的公网服务器,服务器中代码包含Token(随便自定义),公网服务器连接和Token需填入登录后的微信公众平台接口测试帐号,微信后台会发送一个请求携带4个数据,公网服务器需处理微信后台Get方式请求数据,处理请求数据,并响应echostr
完成验证,详情见微信官网文档:入门指引
了解公众号实现“你问我答”机制,理解被动消息的含义、理解收\发消息机制
- FromUserName是粉丝的ID
- ToUserName结尾的是我们公众号ID(可理解为我们提供公众号服务的主机)
- MsgId: 是公众平台为记录识别该消息的一个标记数值, 微信后台系统自动产生
- 接收消息和发送的前5个xml标签是一样的,此图FunFlag(星标字段),但微信官网文档:入门指引中,此标签是没有的
首先接收消息的服务器是微信的服务器,转发粉丝的消息,相当于一个代理,而我们自己的服务器提供回复消息处理、网页等服务。
基本架构
网站 H5页面
可简单理解为,分别代表web后端和前端项目
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。
微信开放社区-潇~:h5微信公众号,能授权获取用户手机号码?
规则
从开放社区了解到,微信公众号网页是不能像小程序那样注册后直接获取用户手机号的
开发者在网页中在不规范使用发起 snsapi_userinfo 网页授权时,将会进入网页快照页模式
- 获取的头像、昵称、openId、unionId 均为虚拟账号数据
- 快照页不与正常页面共享缓存,离开将清理
- 快照页内也无法使用微信其它 JS-SDK 的能力
可获取用户信息
公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑(来自微信官方文档-公众号:网页授权)
中控服务器
建议公众号开发者使用中控服务器统一获取和刷新access_token,一般情况下放于后端即可,除业务繁重的公众号,需单独分离中控服务器
非微信官方提供,专用于访问微信服务器获取access_token的API,Token用于访问微信公众号所有接口的唯一凭证
中控服务器需要 AppID 和 AppSecret 调用本接口来获取access_token
access_token有效时间2小时,需定时刷新,重复获取将导致上次获取的access_token失效
中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新access_token的接口
API-Proxy
微信官方提供的API访问代理,目的是为了隐藏内部真实接口,保障真实API服务器安全
官方理论
服务器地址有效性
按照官方文档的方式建立公众号过程中,发现 微信官方文档-公众号 中不仅代码有问题居然还有错别字,也不知为何,由于要写公众号的后端是部署在服务器上,根据官方要求还得做一些开发认证啥的,比如验证服务器地址的有效性,不得不边写代码边测试,出问题还得调试代码,无奈选择内网穿透服务frp,并成功搭建,详见本站文章 「内网穿透」,配置见本节 网页授权。
内网穿透为了可以通过一台阿里云主机(公众号主机)代理我的电脑,比如我的电脑开启博客网站服务,直接就可以从这台阿里云访问到我的电脑上的博客网站,这样一来,我电脑上的博客项目源码就可利用各种工具方便写,方便调试,方便测试。
注意:若使用WeRobot框架做好内网穿透,开发直接填入Token即可,无序官方的繁琐步骤,以下内容目的仅为了理清微信公众号理论。
理论
按照官方文档,验证服务器地址的有效性,填入url和token(只用于验证开发者服务器),官方先用数组排序对[token, timestamp, nonce]
按照数字和字母正序进行排序,排序后再进行解码成二进制,因为sha算法需要对二进制数据进行提取的,sha1生成后,像这样:
此时生成的sha1字符串就是微信官方传来的signature,我们只需写代码验证一次这个算法再得出一遍sha1,对比自己生成的sha和signature相等,返回nonce即可验证成功:
消息处理
完成服务器地址有效性验证,开始写消息处理,web.py接收微信服务器POST请求,但微信服务器只知道/wx
这个接口,写入POST方式到/wx
关注测试号或者通过公众平台接口测试工具发送hellowx,http协议传输二进制格式的信息,打印出的字符串前带b
通过关注者微信发送
1 | <!--格式化后的xml--> |
通过公众平台测试工具发送
分别以xml格式和json格式进行
1 | { |
1 | <xml> |
access_token
微信官方文档中所述的access_token,一共有两种,一种是只能用来获取用户信息的网页授权access_token,另一种是基础支持access_token,超时时间均为7200(2小时),两种都需要开发使其开发者者服务器妥善保管。
参考:
网页授权access_token
- 用来获取用户信息
- 面向单用户,网页授权access_token是一次性的(来自博客园-TBHacker:网页授权access_token,基础支持access_token,jsapi_ticket待验证)
- 如果需要可进行刷新,刷新后有效期为30天
- 没有次数限制
基础支持access_toke
- 调用微信接口,weixin-js-sdk所需
- 面向多用户
- 需刷新
- 可采用GET方式请求获得jsapi_ticket有效期7200秒(生成 JS-SDK 权限验证的signature)
- 测试号2000次/天
网页授权
关于网页授权access_token和普通access_token的区别
- 微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证(网页授权access_token),通过网页授权access_token可以进行授权后接口调用,如获取用户基本信息;
- 其他微信接口,需要通过基础支持中的“获取access_token”接口来获取到的普通access_token调用。
网页授权过程中从微信服务器不只是acess_token(有效时长2小时)授权,还要有一个code,这个code是换取用户信息的关键,每个用户获取的code不一样的,只有5分钟,此acess_token是为了允许我们的服务器向微信获取code下用户的信息。
参考:
获取用户信息
两种scope,不同的信息获取权限
详细用户信息
- snsapi_userinfo,用来获取用户的基本信息
未关注
- 弹出页面授权
- 需用户同意授权
- 无需关注公众号
已关注
- 从公众号的会话/自定义菜单进入本公众号的网页授权页
- 静默授权
参数 | 描述 |
---|---|
openid | 用户的唯一标识 |
nickname | 用户昵称 |
sex | 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知 |
province | 用户个人资料填写的省份 |
city | 普通用户个人资料填写的城市 |
country | 国家,如中国为CN |
headimgurl | 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像 URL 将失效。 |
privilege | 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom) |
unionid | 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。 |
简要用户信息
- snsapi_base,用来获取进入页面的用户的 openid 和 access_token,后期可通过 用户管理接口(需关注且需产生消息交互)获取用户基本信息
- 静默授权
- 自动跳转回调页(用户无感知,不弹出授权窗口)
- 服务器通过code请求获取access_token & openID
- 拿到用户OpenID/UnionID,可通过 用户管理-获取用户基本信息(UnionID机制),获取用户基本信息
- 关注者与公众号产生消息交互后(比如网页snsapi_base授权),公众号可获得关注者的OpenID
- 可批量获取简要用户信息
参数 | 说明 |
---|---|
subscribe | 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。 |
openid | 用户的标识,对当前公众号唯一 |
language | 用户的语言,简体中文为zh_CN |
subscribe_time | 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间 |
unionid | 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。 |
remark | 公众号运营者对粉丝的备注,公众号运营者可在微信公众平台用户管理界面对粉丝添加备注 |
groupid | 用户所在的分组ID(兼容旧的用户分组接口) |
tagid_list | 用户被打上的标签 ID 列表 |
subscribe_scene | 返回用户关注的渠道来源,ADD_SCENE_SEARCH 公众号搜索,ADD_SCENE_ACCOUNT_MIGRATION 公众号迁移,ADD_SCENE_PROFILE_CARD 名片分享,ADD_SCENE_QR_CODE 扫描二维码,ADD_SCENE_PROFILE_LINK 图文页内名称点击,ADD_SCENE_PROFILE_ITEM 图文页右上角菜单,ADD_SCENE_PAID 支付后关注,ADD_SCENE_WECHAT_ADVERTISEMENT 微信广告,ADD_SCENE_REPRINT 他人转载 ,ADD_SCENE_LIVESTREAM 视频号直播,ADD_SCENE_CHANNELS 视频号 , ADD_SCENE_OTHERS 其他 |
qr_scene | 二维码扫码场景(开发者自定义) |
qr_scene_str | 二维码扫码场景描述(开发者自定义) |
网页授权
本节独立拿出来是因为网页授权需要两个端之间的联动,涉及到两个项目的编码,结合官方理论进行实践。
网页授权需要JS接口安全域名主机下的网页,于是此操作也需要内网穿透,再结合Nginx反向代理,将H5前端和公众号后端进行了80端口反向代理,访问80请求按照接口前缀不同分流至各自内网穿透转发到公网服务器的各自端口。
准备工作
配置
JS接口安全域名
填入公众号测试号中JS接口安全域名,此步骤不需要验证即可填入,IP地址、域名均可,只是IP地址作为网页应该会有如下提示
Frp 内网穿透
详细内容见本博客 「内网穿透」文章
客户端
1 | [common] |
服务端
1 | [common] |
Nginx 反向代理
详细内容见本博客 「Nginx」文章
1 | ··· |
公众号后端
参考:
依赖
- WeRoBot,是一个微信公众号开发框架
- Django
WeRoBot
由于微信官网的文档所提及的技术实在是太老了,开发时十分的费力,况且目前的项目基本上都流行Json格式传递数据了,这家伙微信公众号开发还只能是xml,选用了一个专门做微信公众号的框架WeRobot,而且它可以集成Django,详见:与其他 Web 框架集成,开发效率大大提升。
功能清单
-
消息处理
WeRoBot 会解析微信服务器发来的消息,将消息转换成成 Message 或者是 Event,在Dango中全都需处理成views,需werobot.contrib.django
中的make_view()
函数处理,。
Session
WeRobot官方:你可以通过 Session 实现用户状态的记录。
引入WeRobot的项目会默认使用Sqlite作为默认数据库,其中Session也是存在于Sqlite的,准备将Sqlite替换为Redis(待后期决定)
参考:
参考WeRoBot:API=>Session 对象 发现可以将Session的存储方式进行改变,由于项目会使用Redis,但由于Session会存储用户状态,比如服务器重启,如果使用Redis用户状态会消失,将后期决定是否将Sqlite替换为Redis。
H5前端
参考:
开发工具
VsCode
插件
前端依赖
1 | { |
vue3-h5-template
前端将基于Vant3 + Vue3 + H5的一个模板项目作为前端项目开发
参考:
技术分析
参考:
GitHub-yulimchen:vue3-h5-template => README
Axiso 文档
掘金-狮吼土拨鼠:一个好用的svg加载器svg-sprite-loaderCSDN-巴山却话:详解vue 配置 cdn 加载依赖的方法
掘金-远航_:Vue移动端 / PC端适配解决方案:postcss-px-to-viewport
掘金-藤原托漆:尤大推荐的神器unplugin-vue-components,解放双手!以后再也不用呆呆的手动引入(组件,ui(Element-ui)库,vue hooks等)
基于 Vue3 全家桶、Vant3,vw 视口适配,开箱即用的移动端项目基础模板
- Vue3✨
- Vant3(升级Vant4)✨
- 支持 SVG 图标自动注册组件✨
- vw 视口适配,postcss-px-to-viewport它能完美解决上面的三个痛点,高效的将代码中px单位转为rm、rem、vw等视口单位,一份配置文件完美解决适配问题
- Axios (升级1.2.1) 封装:Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests
- 生产环境 CDN 依赖:将项目依赖的一些第三方包替换成通过 cdn 方式外部加载,而不是打包到 vender,对于提升应用的加载、响应速度很有意义,同时减少打包的体积
- 打包资源 gzip 压缩
- ESLint:是一个用来识别 ECMAScript/JavaScript 并且按照规则给出报告的代码检测工具,比如编译的时候可以编译通过,但会出现好多代码格式不对的提示,不过挺烦人的
- 首屏加载动画
- 项目资源路径 alias 别名
- 开发环境调试面板
- Vuex 集成:Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新
- Vue-router 集成:Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。
- 开发环境 Mock 数据
- normalize.css 它是css样式初始化的插件,详见
- unplugin-vue-components 它可以自动引入组件,并按需引入组件的样式
- eruda H5移动端开发者模式,类似于腾讯的VConsole
/src/main.js
此文件为Vue入口文件,任何全局函数变量、插件挂载都在此文件中表达。
- 注册App.vue到createApp
- 注册Vant组件、svg-icon组件
- 导入less样式
依赖升级
开发小程序使用的是vant-weapp v1.10.2 微信小程序版,遇到了非常多的Bug,打开Vant官网其实可以看到目前的最新版也只不过是Vant2只是升级到了vant-weapp v1.10.10,看样子之前版本遗留的问题依旧没有得到解决,毕竟小程序企业开发已经很少使用微信原生的方式进行开发了,使用量相比Vue版本的Vant来说还是小巫见大巫。
决定使用最新版的Vant4作为本项目的组件库,以达到没有常见Bug的目的。
参考:
segmentfault-Ethan小木:如何检测及升级项目中的 Node 依赖
掘金-UU:vue.config.js 的完整配置(超详细)!
升级Vant 4
依赖常规操作详见Vant 4 官方文档:从 v3 升级到 v4
1 | 通过 yarn 安装 |
移除babel.config.js代码
1 | module.exports = { |
按照官网操作后升级会有报错
1 | Can't import the named export XXXX.mjs from non EcmaScript module (only default export is available) |
看样子就是没加载到文件,结合查找的一些资料得知,需要修改vue.config.js,正好从Stack Overflow:Can’t import the named export XXXX from non EcmaScript module (only default export is available)找到了解决方案,文件修改以下内容,即可正常运行
1 | module.exports = { |
更改这个就是为了更改webpage打包的配置,之前的vue2项目可能是改的webpack.config.js
,现在换成Vue3只能更改vue.config.js
以达到相同的目的,原理待后期研究,先放着。
调整按需引入样式方式
通过以上配置后所有组件的样式已经全部消失,分析代码,此模板所使用了src/plugins/registerVant.js
按需方式引入(GitHub文档有说明),每次引入组件需要加入引入组件的相关代码:
1 | import { |
安装官方升级v4所述插件
1 | 通过 yarn 安装 -D 依赖安装在package.json的devDependencies中 |
基于 vue-cli
的项目,在 vue.config.js
文件中配置插件:
1 | const { VantResolver } = require('unplugin-vue-components/resolvers'); |
使用 ConfigProvider 全局配置 组件进行替换
由于官网声称目前 Vant 已经支持了基于 CSS 变量的主题定制能力,相较于 Less 定制更加灵活。因此,Vant 4 将不再提供基于 Less 的主题定制方式。
这意味着 Vant 的 npm 包中将不再会包含 .less
样式源文件,只会提供编译后的 .css
样式文件。
如果你的项目正在使用旧版的 Less 主题定制,请使用 ConfigProvider 全局配置 组件进行替换。
通俗讲意思是使用了ConfigProvider
组件后可以自定义主题的样式,在 Vant4 官方文档中每个组件的文档最后的部分都有主题定制,提供相比于less更加方便快捷的组件自定义主题样式。
引入
1 | import { createApp } from 'vue'; |
升级Axiso
1 | 在项目目录下执行,就会列出可供更新的依赖 |
升级svg-sprite-loader
1 | ncu svg-sprite-loader -u |
网页授权
网页授权
H5后端
开源项目
报错
公众号菜单栏
微信公众号官方规定:一级菜单最多4个汉字,二级菜单最多8个汉字
参考:
报错
1 | werobot.client.ClientException: 40018: invalid button name size rid: 64ce638f-40af05da-2da1d84a |