我的微信程序不知道为什么总是不能正常回发到用户手机上,从收到微信POST信息到服务器res.send发送完数据,时间基本在100毫秒内,请老师们帮帮我,检查一下我的问题 //这是调用过程 var weixin = require(’./wechat’); weixin.on(‘TextMsg’, function (aweixin) { //收到text消息 var msg = aweixin.makeMsg({MsgType: ‘text’, Content: “这是测试信息”}); 在这里把msg保存起来 aweixin.res.send(msg); //这次发送手机基本上收不到,极少数能收到 }) app.post(’/weixin’, function (req, res) { // 这里判断如果是重试的请求,就把上次保存的消息直接发出去,基本上都能收到 if (重复) return res.send(保存的msg); var aweixin = new weixin({req: req, res: res, token: token, secret: encodingaeskey, appid: corpid}) aweixin.exec(); });
// 以下是微信模块 // wechat.js var sha1 = require(‘sha1’); var xml2js = require(‘xml2js’); var Events = require(‘events’); var emitter = new Events.EventEmitter(); var request = require(‘request’); var crypto = require(‘crypto’);
var XMLParser = require(‘xml2js’); // thunkify = require(‘thunkify’); var buildXML = new XMLParser.Builder({rootName:‘xml’,cdata:true,headless:true,renderOpts :{indent:’ ',pretty:‘true’}});
var msglist = [];
var Weixin = function (options) { this.req = options.req; this.res = options.res; this.res.setCharacterEncoding(“UTF8”); if (this.cf()) return; //如果重复直接退出 this.req.query.msg_signature = this.req.query.msg_signature || this.req.query.signature; this.token = options.token || ‘’; this.appID = options.appid || ‘’; this.secret = options.secret || ‘’; this.aesKey = new Buffer(this.secret + ‘=’, ‘base64’); this.iv = this.aesKey.slice(0, 16); this.accessToken = ‘’;
};
Weixin.prototype.indexofsig = function (sig) { //按签名查找已存在消息 if (sig==undefined) sig = this.req.query.msg_signature; for (var i=0; i < msglist.length; i++) { while (true) { if (parseInt(new Date().valueOf() / 1000) - msglist[i].attime > 15) { //删除超过10秒的信息 msglist.splice(i, 1); if (i<msglist.length) continue; else break; } if (msglist[i].sig == sig) { return i; } break; } } };
Weixin.prototype.cf = function (sig) { //查消息是否重复 return (this.indexofsig(sig) >= 0); };
Weixin.prototype.msgofsig = function (sig) { //返回重复的消息 return msglist[this.indexofsig(sig)]; };
Weixin.prototype.removesig = function (sig) { //按签名删除超时的消息 msglist.splice(this.indexofsig(sig),1); };
/**
- 执行
- @param fb 回调
- @returns {Weixin} */ Weixin.prototype.exec = function (fb) { var _this = this; msglist.push({sig:_this.req.query.msg_signature,attime:parseInt(new Date().valueOf() / 1000)});//保存到消息列表中 _this.req.postdata(function(data){ _this.parseXmltoJson(data, function (err, data) { if ((!_this.req.query.nosig) && (false == _this.verifysignature(_this.req.query.timestamp, _this.req.query.nonce, data.Encrypt, _this.req.query.msg_signature))) { if (fb) fb(‘验签未通过’); } else { var execmsg = function (data) { _this.msg = data; _this.fromUser = _this.msg.FromUserName; _this.toUser = _this.msg.ToUserName; _this.analysisMsg(); }; if (!_this.req.query.nosig) { _this.parseXmltoJson(_this.decrypt(data.Encrypt), function(err,data) { execmsg(data) }); } else execmsg(data); } }); }); };
/**
- 回复消息
- @param msg */ Weixin.prototype.makeMsg = function (msg) { var MsgType = msg.MsgType; var weixinTmpl = weixinMsgXml[MsgType]; var _timestamp = parseInt(this.req.query.timestamp);//用的微信发来的timestamp、nonce和createtime var _nonce = parseInt(this.req.query.nonce); var _createtime = parseInt(this.msg.CreateTime); weixinTmpl = weixinTmpl.replace((new RegExp(’{ToUserName}’,‘g’)),this.msg.FromUserName); weixinTmpl = weixinTmpl.replace((new RegExp(’{FromUserName}’,‘g’)),this.msg.ToUserName); for (var k in msg) { var reg = new RegExp(’{’ + k + ‘}’, ‘g’); weixinTmpl = weixinTmpl.replace(reg, msg[k]); } weixinTmpl = weixinTmpl.replace((new RegExp(’{CreateTime}’,‘g’)),_createtime); if (MsgType == ‘news’) { var items = ‘’; for (var i = 0; i < msg.Articles.length; i++) { var tmp = weixinMsgXml[‘item’]; for (var item in msg.Articles[i]) { var regNews = new RegExp(’{’ + item + ‘}’, ‘g’); tmp = tmp.replace(regNews, msg.Articles[i][item]); } items += tmp; } weixinTmpl = weixinTmpl.replace(’{ArticleCount}’, Object.keys(msg.Articles).length); weixinTmpl = weixinTmpl.replace(’{items}’, items); } weixinTmpl = this.encrypt(weixinTmpl); // 已生成XML消息,加密为Encrypt // 拼接成要发的xml消息 weixinTmpl = “<xml>”+ "<Encrypt><![CDATA["+weixinTmpl+"]]></Encrypt>"+ "<MsgSignature><![CDATA["+ this.makesignature(_timestamp, _nonce, weixinTmpl)+ "]]></MsgSignature>"+ "<TimeStamp>"+_timestamp+"</TimeStamp>"+ "<Nonce><![CDATA["+_nonce+"]]></Nonce>"+ "</xml>"; if (this.indexofsig() >= 0) this.msgofsig().msg = weixinTmpl; return weixinTmpl; };
/* 生成签名 */ Weixin.prototype.makesignature = function (timestamp,nonce,Encrypt){ return sha1([this.token, timestamp, nonce, Encrypt].sort().join(’’)); };
/* 比对签名是否正确
- 比较 signature 或 msg_signature */ Weixin.prototype.verifysignature = function (timestamp,nonce,Encrypt,signature) { return (this.makesignature(timestamp,nonce,Encrypt) == (signature)); };
var weixinMsgXml = { text: ‘<xml>’ + ’<ToUserName><![CDATA[{ToUserName}]]></ToUserName>’ + ’<FromUserName><![CDATA[{FromUserName}]]></FromUserName>’ + ’<CreateTime>{CreateTime}</CreateTime>’ + ’<MsgType><![CDATA[text]]></MsgType>’ + ’<Content><![CDATA[{Content}]]></Content>’ + ’</xml>’, image: ‘<xml>’ + ’<ToUserName><![CDATA[{ToUserName}]]></ToUserName>’ + ’<FromUserName><![CDATA[{FromUserName}]]></FromUserName>’ + ’<CreateTime>{CreateTime}</CreateTime>’ + ’<MsgType><![CDATA[image]]></MsgType>’ + ’<Image>’ + ’<MediaId><![CDATA[{MediaId}]]></MediaId>’ + ’</Image>’ + ’</xml>’, voice: ‘<xml>’ + ’<ToUserName><![CDATA[{ToUserName}]]></ToUserName>’ + ’<FromUserName><![CDATA[{FromUserName}]]></FromUserName>’ + ’<CreateTime>{CreateTime}</CreateTime>’ + ’<MsgType><![CDATA[voice]]></MsgType>’ + ’<Voice>’ + ’<MediaId><![CDATA[{MediaId}]]></MediaId>’ + ’</Voice>’ + ’</xml>’, video: ‘<xml>’ + ’<ToUserName><![CDATA[{ToUserName}]]></ToUserName>’ + ’<FromUserName><![CDATA[{FromUserName}]]></FromUserName>’ + ’<CreateTime>{CreateTime}</CreateTime>’ + ’<MsgType><![CDATA[video]]></MsgType>’ + ’<Video>’+ ’<MediaId><![CDATA[{MediaId}]]></MediaId>’+ ’<ThumbMediaId><![CDATA[{ThumbMediaId}]]></ThumbMediaId>’ + ’</Video>’+ ’</xml>’, music: ‘<xml>’ + ’<ToUserName><![CDATA[{ToUserName}]]></ToUserName>’ + ’<FromUserName><![CDATA[{FromUserName}]]></FromUserName>’ + ’<CreateTime>{CreateTime}</CreateTime>’ + ’<MsgType><![CDATA[music]]></MsgType>’ + ’<Music>’ + ’<Title><![CDATA[{Title}]]></Title>’ + ’<Description><![CDATA[{Description}]]></Description>’ + ’<MusicUrl><![CDATA[{MusicUrl}]]></MusicUrl>’ + ’<HQMusicUrl><![CDATA[{HQMusicUrl}]]></HQMusicUrl>’ + ’<ThumbMediaId><![CDATA[{ThumbMediaId}]]></ThumbMediaId>’ + ’</Music>’ + ’</xml>’, news: ‘<xml>’ + ’<ToUserName><![CDATA[{ToUserName}]]></ToUserName>’ + ’<FromUserName><![CDATA[{FromUserName}]]></FromUserName>’ + ’<CreateTime>{CreateTime}</CreateTime>’ + ’<MsgType><![CDATA[news]]></MsgType>’ + ’<ArticleCount>{ArticleCount}</ArticleCount>’ + ’<Articles>’ + ’{items}’ + ’</Articles>’ + ’</xml>’, item: ‘<item>’ + ’<Title><![CDATA[{Title}]]></Title>’ + ’<Description><![CDATA[{Description}]]></Description>’ + ’<PicUrl><![CDATA[{PicUrl}]]></PicUrl>’ + ’<Url><![CDATA[{Url}]]></Url>’ + ’</item>’ };
/**
- 接收消息解析 */ Weixin.prototype.analysisMsg = function () { var _this = this; if (this.err) { this.handleMsgErr(); } else { var MsgType = this.msg.MsgType.toLowerCase(); switch (MsgType) { // MsgErr 错误消息 // clickEventMsg 自定义菜单事件 case ‘text’: emitter.emit(‘TextMsg’, this); //文本消息 break; case ‘image’: emitter.emit(‘ImageMsg’); //图片消息 break; case ‘voice’: emitter.emit(‘VoiceMsg’); //语音消息 break; case ‘video’: emitter.emit(‘VideoMsg’); //视频消息 break; case ‘location’: emitter.emit(‘LocationEventMsg’, _this);//地理位置消息 // emitter.emit(‘LocationMsg’);//地理位置消息 break; case ‘link’: emitter.emit(‘LinkMsg’); //链接消息 break; case ‘event’: //事件消息 switch (this.msg.Event.toLowerCase()) { case ‘location’: emitter.emit(‘LocationEventMsg’, _this); // 上报位置事件 break; case ‘click’: emitter.emit(‘clickEventMsg’, this); // 单击事件 break; case ‘enter’: emitter.emit(‘EnterEventMsg’, this); // 进入会话事件 break; case ‘scan’: emitter.emit(‘ScanEventMsg’, this); // 扫码事件 break; case ‘subscribe’: emitter.emit(‘SubEventMsg’, this); // 订阅事件 break; case ‘unsubscribe’: emitter.emit(‘UnSubEventMsg’, this); // 取消订阅事件 break; } break; } } };
Weixin.on = function (msgstr, cb) { emitter.on(msgstr,cb); return this; };
Weixin.prototype.parseXmltoJson = function (strXml, fb) { xml2js.parseString(strXml.toString(), {explicitArray: false}, function (err, json) { if (err) fb(err); else fb(null,json.xml);
});
};
/* 解密 */ Weixin.prototype.decrypt = function (str) { var aesCipher = crypto.createDecipheriv(“aes-256-cbc”, this.aesKey, this.iv); aesCipher.setAutoPadding(false); var decipheredBuff = Buffer.concat([aesCipher.update(str, ‘base64’), aesCipher.final()]); decipheredBuff = PKCS7Decoder(decipheredBuff); var len_netOrder_corpid = decipheredBuff.slice(16); var msg_len = len_netOrder_corpid.slice(0, 4).readUInt32BE(0); //recoverNetworkBytesOrder(len_netOrder_corpid.slice(0, 4)); var result = len_netOrder_corpid.slice(4, msg_len + 4).toString(); var appId = len_netOrder_corpid.slice(msg_len + 4).toString(); if (appId != this.appID)throw new Error(‘appId is invalid’); return result; };
function PKCS7Decoder(buff) { var pad = buff[buff.length - 1]; if (pad < 1 || pad > 32) { pad = 0; } return buff.slice(0, buff.length - pad); }
//加密 Weixin.prototype.encrypt = function (xmlMsg) { var random16 = crypto.pseudoRandomBytes(16); var msg = new Buffer(xmlMsg); var msgLength = new Buffer(4); msgLength.writeUInt32BE(msg.length, 0); var corpId = new Buffer(this.appID); var raw_msg = Buffer.concat([random16,msgLength,msg ,corpId]);//randomString + msgLength + xmlMsg + this.corpID; var encoded = PKCS7Encoder(raw_msg); var cipher = crypto.createCipheriv(‘aes-256-cbc’, this.aesKey, this.iv); // cipher.setAutoPadding(true); var cipheredMsg = Buffer.concat([cipher.update(encoded/raw_msg/), cipher.final()]); return cipheredMsg.toString(‘base64’); };
function PKCS7Encoder(buff) { var blockSize = 32; var strSize = buff.length; var amountToPad = blockSize - (strSize % blockSize); var pad = new Buffer(amountToPad-1); pad.fill(String.fromCharCode(amountToPad)); return Buffer.concat([buff, pad]); }
module.exports = Weixin;