小程序最近开放了广告组件,但是有一个门槛就是UV需要达到1000,看着我那已经上线半年的小程序自然增长用户只有400多,感觉错失了一个亿,而能最快最有效带来用户增长的渠道就是扫码。作为一个半吊子前端,小程序的数据库和后台程序直接偷懒使用了 LeanCloud ,但是生成小程序码这件事情恐怕就得自己来了。
本文记录一下这个需求的解决过程,帮助小白们快速搭建小程序码的相关后台程序。JS 和 Nginx 写得用得都不多,恳请大神们指正哈~
获取小程序码逻辑和需求
微信文档 已经写得很清楚了,首先需要获取一个有效期 2 小时的 access_token
,使用该 token 再带上小程序码中需要包括的路径、参数向微信接口请求,微信会通过二进制文件流的形式返回小程序码。
比较坑的是出于安全需要,微信不允许直接通过前端请求获取小程序码接口,原因是 app_secret
是敏感字段,放在前端不安全。所以我们现在需要自己搭后端程序,通过服务器向微信请求到小程序码,再返回给前端,相当于一个中间层。从输入输出看我们这个接口,就是接受小程序传过来的参数,返回一个小程序图片或 URL 。
简单考虑,使用 Express (NodeJS的后端框架)作为路由,用来设置接口地址。服务端请求发送使用 request
库,为了减小延迟(省事儿)就直接把文件存在本地。二进制流发来发去估计也会比较慢,所以就直接返回本地的路径就好。PM2
是为了让程序能够持续稳定地运行而选择的一个库,没仔细看过文档,大概就是可以自动重启吧~
编写 Node 程序
整体目录结构如下:
- package.json (npm依赖)
- index.js (入口、路由)
- wechatqr.js (获取、保存小程序码)
- wechattoken.js (获取、持续更新
access_token
) - conf.js (保存小程序 id 和 secret)
使用方式:
下面放代码,一个一个来哈~
//index.js
const express = require('express');
const url = require('url');
const wechatqr = require('./wechatqr');
const app = express();
app.get('接口地址', function(req, res) {
//接收参数,自行换成你的
//接口效果如https://baidu.com/接口地址?objectId=123;
let objectId = url.parse(req.url, true).query.objectId;
if (typeof objectId !== 'string' || objectId.length == 0) {
res.json({
code: 0
})
} else {
wechatqr.getQR(objectId, '扫码后打开的小程序页面路径').then((data) => {
res.json({
qrimg: data,
code: 1
})
}).catch((err) => {
console.log(err)
res.json({
code: 0
})
});
}
});
var server = app.listen(3000, function() {
});
//wechatqr.js
const request = require("request");
const rp = require("request-promise-native");
const fs = require("fs");
const crypto = require('crypto');
const token = require('./wechattoken');
function sha1(message) {
return crypto.createHash('sha1').update(message, 'utf8').digest('hex');
}
function checkFile(filePath) {
return new Promise(function(resolve, reject) {
fs.access(filePath, fs.constants.R_OK, (err) => {
if (err)
reject()
else
resolve()
});
})
}
function getQR(scene, path) {
let fileName = sha1(scene),
filePath = `../webpage/postcard/static/qrimg/${fileName}.png`,
imgPath = `https://static.postcard.daibor.cn/qrimg/${fileName}.png`,
readable;
return new Promise(function(resolve, reject) {
//检查文件是否存在
checkFile(filePath).then((res) => {
//无错误,有文件
console.log('existed');
resolve(imgPath);
}).catch((err) => {
//有错误,无文件
console.log('new user');
var saveQR = request({
url: 'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=' + token.getToken(),
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
scene: scene,
page: path,
is_hyaline: true
})
}).pipe(fs.createWriteStream(filePath));
saveQR.on('finish', function() {
resolve(imgPath);
})
})
})
}
module.exports = {
getQR
}
//wechattoken.js
const cf = require('./conf');
const rp = require("request-promise-native");
var _TOKEN = '';
//请求access_token
function getAccessToken() {
rp('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + cf.appid + '&secret=' + cf.secret).then((res) => {
console.log(res);
_TOKEN = JSON.parse(res).access_token;
})
}
function getToken() {
return _TOKEN;
}
getAccessToken();
//微信规定超时时间7200秒,不能频繁请求,因此设置每7000秒重新请求一次。
const _timer = setInterval(getAccessToken, 7000000);
module.exports = {
getToken
}
//conf.js
module.exports = {
appid: 你的小程序id,
secret: 你的小程序secret
}
配置 Nginx 反向代理
只要加上这么一段在 server 的大括号里就妥了。
location / {
proxy_pass http://localhost:3000;
}
在服务端启动代码
使用 PM2 作为 Node 进程的管理工具。对于这个项目来说不用了解太多 PM2 的用法(好吧其实是我懒)。用 FTP 把代码(4个 JS 和 package.json )传到服务器上,我用的是 FileZilla。呃……写完才发现这种方法一点都不 Geek ,还是过两天搭个仓库,直接 git clone
逼格高(并不)。
首先,全局安装该包
npm install -g pm2
然后进入到存放代码的目录,npm install
一下。国内速度一般比较慢,可以先配置下 CNPM ,然后就欢快的用 PM2 启动吧~
pm2 start index.js --watch -i 2
//这句的意思是:监听当前目录,启用两个实例用于负载均衡
如果没出什么问题的话,接下来就可以用 Postman (或者直接用小程序的 request ) 测试一下接口有没有正常工作。然后就欢快地为你的小程序加上小程序码的功能,收割朋友圈这个最大的流量入口吧~
作为一个后端能力近乎不存在的独立小程序开发者,我在生成带小程序码时直接把微信生成的图片返回到前端,使用 Canvas 搭配 PS 做好的图片模板生成海报,效果如下图,个人感觉还不错,过两天写篇文章分享下我生成图片的代码:
Last modified on 2018-07-29