初衷
最近在弄个人公众号,也会发表一些自己的技术实现,同时为了要加粉,所以加了一个回复关键字获取demo的功能,可是每次设置关键字都要在微信公众平台设置,比较繁琐,于是想到直接使用后端控制,然而,受限于自身后端实力,要搭建一个完整的系统实在有些为难,所以才去了折中的办法。
面向对象
- 后端不熟悉,但有基本的编程思维;
- 稍微了解运营基础(可以参考我的另外一篇文章CentOS下从零开始部署自己的博客);
- 功能简单,没有复杂场景,比较适合个人。
开发过程
工具
必须
- 一台服务器
- 一个域名
软件推荐
- ssh工具:Mobaxterm(windows)、nuoshell(mac) ——都是免费的,linux 就直接用命令行好了
- nginx:其它工具也行,不用代理apache也可以
- pm2: 便于管理express应用
安装工具
- 安装node和nginx
往期文章已详细叙述,不在赘述CentOS下从零开始部署自己的博客
- npm命令,本人皆使用的是淘宝镜像,用cnpm命令速度将快速很多
- 安装express脚手架和pm2
npm install express-generator -g
npm install pm2 -g
- 推荐直接安装express脚手架,这样路由什么的都已经配好了,方便拿来即用,当然想自定义的,可以只安装express。
- 创建微信工程,并安装依赖
express -e wechat
cd wechat
npm install
申请微信参数
我们可以先在测试环境使用,在微信公众平台接口测试帐号申请页面,创建基本参数,并记住token参数。
代码编写
功能开发
- 该步骤需要安装crypto
npm install crypto
按照文档描述,将字段排序并加密后与签名比对,代码
const crypto = require('crypto');
router.get('/', function (req, res, next) {
const signature = req.query.signature;
const timestamp = req.query.timestamp;
const nonce = req.query.nonce;
const echostr = req.query.echostr;
// 1. 将token、timestamp、nonce三个参数进行字典序排序
let array = new Array('微信后台设置的token', timestamp, nonce);
array.sort();
let str = array.toString().replace(/,/g, "");
// 2. 将排序后的字符串进行sha1加密
let sha1Code = crypto.createHash("sha1");
let code = sha1Code.update(str, 'utf-8').digest("hex");
// 3. 与signature对比,如果匹配表示数据来自微信
if (code === signature) {
res.send(echostr)
} else {
res.send("error");
}
});
- 回复功能
因为与微信的交互都是xml格式,所以我们还要安装一个xml解析库
npm install xml2js -s
解析接受的消息
const parseString = require('xml2js').parseString;
router.post("/", (req, res) => {
var buffer = [];
req.on('data', function (data) {
buffer.push(data);
});
req.on('end', function () {
var msgXml = Buffer.concat(buffer).toString('utf-8');
parseString(msgXml, { explicitArray: false }, (err, result) => {
if (err) {
throw err;
}
result = result.xml;
var toUser = result.ToUserName;
var fromUser = result.FromUserName;
if (result.MsgType === "text") {
// 文本消息
} else if (result.MsgType === "image") {
// 图片消息
} else if ('event' === result.MsgType) {
if ('subscribe' === result.Event) {
// 被关注
}
}
})
})
});
回复文本消息
let str = `<xml><ToUserName><![CDATA[${fromUser}]]>
</ToUserName><FromUserName><![CDATA[${toUser}]]></FromUserName>
<CreateTime>${new Date().getTime()}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你回复的文本内容]]></Content></xml>`;
res.send(str);
- 菜单功能
- 菜单功能仅对认证的公众号开放,因本人公众号并未认证,所以在正式号上,没发实现,但测试是可以的。
axios.post(`https://api.weixin.qq.com/cgi-bin/menu/create?access_token=获取到的access_token`, '菜单内容').then((response) => {
if (response.data.errcode == 0) {
// 成功
}
}).catch((error) => {
// 失败
});
不用数据库实现各功能的动态更新
画重点,这是最主要的地方
不用数据库,我们直接使用json配置文件,模拟数据库,该怎么做呢?
fs库给我们提供了一个api
/**
* Watch for changes on `filename`. The callback `listener` will be called each time the file is accessed.
* @param filename A path to a file or directory. If a URL is provided, it must use the `file:` protocol.
* URL support is _experimental_.
*/
export function watchFile(filename: PathLike, listener: (curr: Stats, prev: Stats) => void): void;
所以,我们在node启动时,注册一个监听,就可以实时更新数据了,都不需要重启服务器,是不是很简单。
代理
因为微信仅支持80/443端口,所以我们需要使用nginx代理一下,当然端口没有占用,你直接将node运行在这个端口上也没问题。
比如本人是开启的443端口,则代理配置如下
server {
listen 443 ssl;
server_name 代理域名;
ssl_certificate 签名文件;
ssl_certificate_key 签名文件;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Nginx-Proxy true;
proxy_set_header Connection "";
// 这里填写实际的地址和端口
proxy_pass http://127.0.0.1:3000;
proxy_redirect default;
#root html;
#index index.html index.htm;
}
}
运行
至此,所有的开发完成了,我们如果想测试效果,可以
npm start
如果我们发布,自然不会一直挂在终端上,所以我们用pm2命令
pm2 start bin/www
更多命令,可以自行搜索。
最后
所有代码,我都整理成了一个demo,并且将需要配置的地方已经提取出来了,只需要简单配置,即可运行。
如需获取完整demo,可以关注公众号[奶爸程序猿]回复"wxexpress"获取。