微信开发模式为什么消息有时回不到手机上
发布于 8 年前 作者 jinhx 4691 次浏览 来自 问答

我的微信程序不知道为什么总是不能正常回发到用户手机上,从收到微信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);
	//如果不重复,开始构造一个weixin,并处理微信发来的消息
	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](/user/param) fb    回调
 * [@returns](/user/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](/user/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;```
6 回复

请你先markdown

@haozxuan 谢谢,markdown是什么东西?我百度了好像是一个软件!

@jinhx 格式化下你贴出来的东西,自己看着都不舒服,别人没办法帮你;

@haozxuan 加上了

@jinhx untitled1.png 在你的自定义模块里,并没有这个方法,不确定做了什么; BTW:微信公众号开发有成熟的模块可以 参考

@haozxuan 就是一般的res的发送方法,我把req和res都附加到aweixin里去了,方便操作

 * 发送一般数据
 * @param data
 * @param type
 */
function res_send(data,type) {
    if (!type)
        this.writeHead(200,{});
    else
        this.writeHead(200,{"Content-Type": type});
    this.write(data);
    this.end();
}
回到顶部