Express搭建微信公众号后台

初衷

最近在弄个人公众号,也会发表一些自己的技术实现,同时为了要加粉,所以加了一个回复关键字获取demo的功能,可是每次设置关键字都要在微信公众平台设置,比较繁琐,于是想到直接使用后端控制,然而,受限于自身后端实力,要搭建一个完整的系统实在有些为难,所以才去了折中的办法。

面向对象

  1. 后端不熟悉,但有基本的编程思维;
  2. 稍微了解运营基础(可以参考我的另外一篇文章CentOS下从零开始部署自己的博客);
  3. 功能简单,没有复杂场景,比较适合个人。

开发过程

工具

必须

  • 一台服务器
  • 一个域名

软件推荐

  1. ssh工具:Mobaxterm(windows)、nuoshell(mac) ——都是免费的,linux 就直接用命令行好了
  2. nginx:其它工具也行,不用代理apache也可以
  3. pm2: 便于管理express应用

安装工具

  1. 安装node和nginx

往期文章已详细叙述,不在赘述CentOS下从零开始部署自己的博客

  • npm命令,本人皆使用的是淘宝镜像,用cnpm命令速度将快速很多
  1. 安装express脚手架和pm2
1
2
npm install express-generator -g
npm install pm2 -g
  • 推荐直接安装express脚手架,这样路由什么的都已经配好了,方便拿来即用,当然想自定义的,可以只安装express。
  1. 创建微信工程,并安装依赖
1
2
3
express -e wechat
cd wechat
npm install

申请微信参数

我们可以先在测试环境使用,在微信公众平台接口测试帐号申请页面,创建基本参数,并记住token参数。

代码编写

功能开发

  1. 该步骤需要安装crypto
1
npm install crypto

按照文档描述,将字段排序并加密后与签名比对,代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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");
}
});
  1. 回复功能

因为与微信的交互都是xml格式,所以我们还要安装一个xml解析库

1
npm install xml2js -s

解析接受的消息

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
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) {
// 被关注
}
}
})
})
});

回复文本消息

1
2
3
4
5
6
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);
  1. 菜单功能
  • 菜单功能仅对认证的公众号开放,因本人公众号并未认证,所以在正式号上,没发实现,但测试是可以的。
1
2
3
4
5
6
7
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

1
2
3
4
5
6
/**
* 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端口,则代理配置如下

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
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;
}
}

运行

至此,所有的开发完成了,我们如果想测试效果,可以

1
npm start

如果我们发布,自然不会一直挂在终端上,所以我们用pm2命令

1
pm2 start bin/www

更多命令,可以自行搜索。

最后

如需获取完整demo,可以关注公众号[奶爸程序猿]回复”wxexpress”获取。