Node.js:微信消息回复、推送的实现
适应场景:Sails框架、微信公众号数据配置在数据库中,支持多个公众号。
1、Model定义(Waterline)
文件路径:/api/models/wx/Wx_config.js
/** * Created by root on 10/25/15. */ var moment = require('moment'); module.exports = { schema: true, autoCreatedAt: false, autoUpdatedAt: false, attributes: { id: { type: 'integer', autoIncrement: true, primaryKey: true }, appname: { type: 'string', required: true }, ghid: {//原始ID type: 'string', unique: true, required: true }, appid: { type: 'string', unique: true, required: true }, appsecret: { type: 'string', unique: true, required: true }, encodingAESKey:{ type: 'string', unique: true, required: true }, token: { type: 'string', unique: true, required: true }, access_token: { type: 'string' }, expire_time: { type:'integer', defaultsTo:function(){ return 0; } }, createdBy:{ type: 'integer' }, createdAt:{ type:'integer', defaultsTo:function(){ return moment().format('X'); } } } };
2、Sails默认不支持微信xml post,改造之
文件路径:/config/http.js
rawParser: function(req, res, next) { switch(req.headers['content-type']) { case 'text/xml': require('body-parser').raw({ type: 'text/xml' })(req, res, next); break; default : next(); } }, order: [ 'startRequestTimer', 'cookieParser', 'session', 'myRequestLogger', 'rawParser', 'bodyParser', 'handleBodyParserError', 'compress', 'methodOverride', 'poweredBy', '$custom', 'router', 'www', 'favicon', '404', '500' ],
3、控制类
文件路径:/api/controllers/open/WeixinController.js
微信公众号后台配置路径:http://127.0.0.1/open/weixin/api?wxid=1
/** * Created by root on 10/25/15. */ module.exports = { api: function (req, res) { var id = req.query.wxid; Wx_config.findOne(id).exec(function (err, conf) { if (err)return res.send(200, 'fail'); if (req.body) { WeixinService.loop(req, function (data) { if (data.type = 'text') {//用户发送纯文本 var msg = {toUserName: data.openid, fromUserName: conf.ghid, content: 'Node.js Test...'}; WeixinService.sendTextMsg(res, msg);//向用户回复消息 } }); } //签名 if (WeixinService.checkSignature(req, conf.token)) { res.send(200, req.query.echostr); } else { res.send(200, 'fail'); } }); } };
4、微信服务工具类
文件路径:/api/services/WeixinService.js
/** * Created by root on 10/26/15. */ var sha1 = require('sha1'), xml2js = require('xml2js'); module.exports = { /** * 签名验证 * @param req * @param token * @returns {boolean} */ checkSignature: function (req, token) { var signature = req.query.signature, timestamp = req.query.timestamp, nonce = req.query.nonce, echostr = req.query.echostr; // 按照字典排序 var array = [token, timestamp, nonce]; array.sort(); // 连接 var str = sha1(array.join("")); // 对比签名 return str == signature; }, /** * 监听用户消息 * @param req * @param callback */ loop: function (req, callback) { var body = req.body.toString('utf-8'); var data = {}; xml2js.parseString(body, function (err, json) { if (err) { } else { console.log('json::' + JSON.stringify(json)); data.type = json.xml.MsgType; data.openid = json.xml.FromUserName; if (data.type == 'text') { data.txt = json.xml.Content; } else if (data.type == 'image') { data.pic = json.xml.PicUrl; } return callback(data); } }); }, /** * 发送文本消息 * @param res * @param msg */ sendTextMsg: function (res, msg) { var time = Math.round(new Date().getTime() / 1000); var funcFlag = msg.funcFlag ? msg.funcFlag : 0; var output = "" + "<xml>" + "<ToUserName><![CDATA[" + msg.toUserName + "]]></ToUserName>" + "<FromUserName><![CDATA[" + msg.fromUserName + "]]></FromUserName>" + "<CreateTime>" + time + "</CreateTime>" + "<MsgType><![CDATA[text]]></MsgType>" + "<Content><![CDATA[" + msg.content + "]]></Content>" + "<FuncFlag>" + funcFlag + "</FuncFlag>" + "</xml>"; res.type('xml'); res.send(output); }, /** * 发送图文消息 * @param res * @param msg */ sendNewsMsg: function (res, msg) { var time = Math.round(new Date().getTime() / 1000); var articlesStr = ""; for (var i = 0; i < msg.articles.length; i++) { articlesStr += "<item>" + "<Title><![CDATA[" + msg.articles[i].title + "]]></Title>" + "<Description><![CDATA[" + msg.articles[i].description + "]]></Description>" + "<PicUrl><![CDATA[" + msg.articles[i].picUrl + "]]></PicUrl>" + "<Url><![CDATA[" + msg.articles[i].url + "]]></Url>" + "</item>"; } var funcFlag = msg.funcFlag ? msg.funcFlag : 0; var output = "" + "<xml>" + "<ToUserName><![CDATA[" + msg.toUserName + "]]></ToUserName>" + "<FromUserName><![CDATA[" + msg.fromUserName + "]]></FromUserName>" + "<CreateTime>" + time + "</CreateTime>" + "<MsgType><![CDATA[news]]></MsgType>" + "<ArticleCount>" + msg.articles.length + "</ArticleCount>" + "<Articles>" + articlesStr + "</Articles>" + "<FuncFlag>" + funcFlag + "</FuncFlag>" + "</xml>"; res.type('xml'); res.send(output); } };
Nodejs版本:0.12.7
Sails版本:0.11.0
require相关组件,记得安装。
参考项目1:https://github.com/node-webot/wechat-api
参考项目2:https://github.com/JeremyWei/weixin_api