當(dāng)普通微信用戶向公眾賬號(hào)發(fā)消息時(shí),微信服務(wù)器將POST消息的XML數(shù)據(jù)包到開發(fā)者填寫的URL上。
微信用戶向公共賬號(hào)發(fā)送的消息類型包括:文本消息、圖片消息、語音消息、視頻消息、小視頻消息、地理位置消息、鏈接消息。除了發(fā)送消息,微信用戶還會(huì)向公共賬號(hào)推送事件:
1 關(guān)注/取消關(guān)注事件
2 掃描帶參數(shù)二維碼事件
3 上報(bào)地理位置事件
4 自定義菜單事件
5 點(diǎn)擊菜單拉取消息時(shí)的事件推送
6 點(diǎn)擊菜單跳轉(zhuǎn)鏈接時(shí)的事件推送
當(dāng)你的微信公共賬號(hào)接入第三方服務(wù)器的時(shí)候,微信服務(wù)器會(huì)把這些消息和事件通通推送到第三方服務(wù)器,如果第三方服務(wù)器沒有做合適的處理向微信服務(wù)器返回信息的話,微信服務(wù)器會(huì)向用戶返回:“該公告號(hào)服務(wù)不可用”等錯(cuò)誤信息,所以,我們需要對(duì)這些POST請(qǐng)求進(jìn)行處理。
我們?cè)谔幚砦⑿臥OST請(qǐng)求的時(shí)候先要照“接入微信”的驗(yàn)證流程進(jìn)行驗(yàn)證,如果驗(yàn)證成功的話再做出響應(yīng),微信POST過來的消息和事件都是XML格式的字符串,因此,我們需要下載一個(gè)XML解析插件:express-xml-bodyparser。

下載并應(yīng)用這個(gè)插件后,我們的程序便可以通過這個(gè)插件進(jìn)行解析XML數(shù)據(jù)了
微信POST過來的數(shù)據(jù)是這個(gè)樣子的:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
我們通過這種方式進(jìn)行解析:
var msgtype = req.body.xml.msgtype[0].toString();
var tousername = req.body.xml.tousername[0].toString();
var fromusername = req.body.xml.fromusername[0].toString();
var createtime = Math.round(Date.now() / 1000);
var content = req.body.xml.content[0].toString();
拿到數(shù)據(jù)后我們還需要再次拼接成XML格式的數(shù)據(jù)返回給微信,注意,此處的發(fā)送方(fromusername)和接受方(tousername)發(fā)生了反轉(zhuǎn):
var xmlstr=`<xml>
<ToUserName><![CDATA[${fromusername}]]></ToUserName>
<FromUserName><![CDATA[${tousername}]]></FromUserName>
<CreateTime>${createtime}</CreateTime>
<MsgType><![CDATA[${msgtype}]]></MsgType>
<Content><![CDATA[${content}]]></Content>
</xml>`
res.set('Content-Type','text/xml')
res.send(xmlstr)
公共平臺(tái)響應(yīng)的消息類型和數(shù)據(jù)格式請(qǐng)參考:
1 回復(fù)文本消息
2 回復(fù)圖片消息
3 回復(fù)語音消息
4 回復(fù)視頻消息
5 回復(fù)音樂消息
6 回復(fù)圖文消息
我們需要對(duì)不同類型的消息做出判斷,我們可以在程序中做簡(jiǎn)單處理:
// 4、開發(fā)者獲得加密后的字符串可與signature對(duì)比,標(biāo)識(shí)該請(qǐng)求來源于微信
if (signature===sign) {
// 如果簽名驗(yàn)證通過后
var msgtype = req.body.xml.msgtype[0].toString();
if (msgtype=='text') {
var tousername = req.body.xml.tousername[0].toString();
var fromusername = req.body.xml.fromusername[0].toString();
var createtime = Math.round(Date.now() / 1000);
var content = req.body.xml.content[0].toString();
var xmlstr=`<xml>
<ToUserName><![CDATA[${fromusername}]]></ToUserName>
<FromUserName><![CDATA[${tousername}]]></FromUserName>
<CreateTime>${createtime}</CreateTime>
<MsgType><![CDATA[${msgtype}]]></MsgType>
<Content><![CDATA[${content}]]></Content>
</xml>`
res.set('Content-Type','text/xml')
res.send(xmlstr)
} else if(msgtype=='image') {
var tousername = req.body.xml.tousername[0].toString();
var fromusername = req.body.xml.fromusername[0].toString();
var createtime = Math.round(Date.now() / 1000);
var content = "您好,您發(fā)過來的是一張圖片";
var xmlstr=`<xml>
<ToUserName><![CDATA[${fromusername}]]></ToUserName>
<FromUserName><![CDATA[${tousername}]]></FromUserName>
<CreateTime>${createtime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${content}]]></Content>
</xml>`
res.set('Content-Type','text/xml')
res.send(xmlstr)
} else if(msgtype=='voice') {
var tousername = req.body.xml.tousername[0].toString();
var fromusername = req.body.xml.fromusername[0].toString();
var createtime = Math.round(Date.now() / 1000);
var content = "您好,您發(fā)過來的是一段語音";
var xmlstr=`<xml>
<ToUserName><![CDATA[${fromusername}]]></ToUserName>
<FromUserName><![CDATA[${tousername}]]></FromUserName>
<CreateTime>${createtime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${content}]]></Content>
</xml>`
res.set('Content-Type','text/xml')
res.send(xmlstr)
} else if(msgtype=='video') {
var tousername = req.body.xml.tousername[0].toString();
var fromusername = req.body.xml.fromusername[0].toString();
var createtime = Math.round(Date.now() / 1000);
var content = "您好,您發(fā)過來的是一段視頻";
var xmlstr=`<xml>
<ToUserName><![CDATA[${fromusername}]]></ToUserName>
<FromUserName><![CDATA[${tousername}]]></FromUserName>
<CreateTime>${createtime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${content}]]></Content>
</xml>`
res.set('Content-Type','text/xml')
res.send(xmlstr)
} else if(msgtype=='shortvideo') {
var tousername = req.body.xml.tousername[0].toString();
var fromusername = req.body.xml.fromusername[0].toString();
var createtime = Math.round(Date.now() / 1000);
var content = "您好,您發(fā)過來的是一段短視頻";
var xmlstr=`<xml>
<ToUserName><![CDATA[${fromusername}]]></ToUserName>
<FromUserName><![CDATA[${tousername}]]></FromUserName>
<CreateTime>${createtime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${content}]]></Content>
</xml>`
res.set('Content-Type','text/xml')
res.send(xmlstr)
} else if(msgtype=='location') {
var tousername = req.body.xml.tousername[0].toString();
var fromusername = req.body.xml.fromusername[0].toString();
var createtime = Math.round(Date.now() / 1000);
var content = "您好,您發(fā)過來的是一個(gè)地理位置";
var xmlstr=`<xml>
<ToUserName><![CDATA[${fromusername}]]></ToUserName>
<FromUserName><![CDATA[${tousername}]]></FromUserName>
<CreateTime>${createtime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${content}]]></Content>
</xml>`
res.set('Content-Type','text/xml')
res.send(xmlstr)
} else if(msgtype=='link') {
var tousername = req.body.xml.tousername[0].toString();
var fromusername = req.body.xml.fromusername[0].toString();
var createtime = Math.round(Date.now() / 1000);
var content = "您好,您發(fā)過來的是個(gè)網(wǎng)站鏈接";
var xmlstr=`<xml>
<ToUserName><![CDATA[${fromusername}]]></ToUserName>
<FromUserName><![CDATA[${tousername}]]></FromUserName>
<CreateTime>${createtime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${content}]]></Content>
</xml>`
res.set('Content-Type','text/xml')
res.send(xmlstr)
} else if(msgtype=='event') {
var tousername = req.body.xml.tousername[0].toString();
var fromusername = req.body.xml.fromusername[0].toString();
var Event = req.body.xml.event[0].toString();
var createtime = Math.round(Date.now() / 1000);
var content = "您好,您觸發(fā)了一個(gè)事件";
var xmlstr=`<xml>
<ToUserName><![CDATA[${fromusername}]]></ToUserName>
<FromUserName><![CDATA[${tousername}]]></FromUserName>
<CreateTime>${createtime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${content}]]></Content>
</xml>`
res.set('Content-Type','text/xml')
res.send(xmlstr)
} else{
var tousername = req.body.xml.tousername[0].toString();
var fromusername = req.body.xml.fromusername[0].toString();
var createtime = Math.round(Date.now() / 1000);
var content = "抱歉,我們不能接受此類型的消息";
var xmlstr=`<xml>
<ToUserName><![CDATA[${fromusername}]]></ToUserName>
<FromUserName><![CDATA[${tousername}]]></FromUserName>
<CreateTime>${createtime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${content}]]></Content>
</xml>`
res.set('Content-Type','text/xml')
res.send(xmlstr)
};
}else{
res.send("invalid sign")
}
這是我們?cè)诖a中寫死的回復(fù)規(guī)則,每次更新都需要重新更改代碼,比較麻煩。如果在生產(chǎn)環(huán)境,有需求的話,我們可以做一個(gè)消息回復(fù)管理系統(tǒng),代替微信的默認(rèn)內(nèi)管,這樣只需要在系統(tǒng)中做一個(gè)規(guī)則配置,公共號(hào)便可以按照配置進(jìn)行響應(yīng)。