微信向第三方服務器發送請求時會降 signature 、timestamp、 nonce 、 openid(用戶標識),發送內容會以 xml 的形式附加在請求中git
回覆消息前提咱們得拿到用戶id , 用戶發送內容等信息,用戶發送內容格式參考微信官方文檔:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140453express
想要獲取用戶發送信息,須要從請求中得到 xml ,所以須要用到 raw-body(得到原生請求體)npm
npm install raw-body --save
接下來須要將xml從請求中分離而且格式化成jsonjson
var getRawBody = require('raw-body') var contentType = require('content-type')
var data = getRawBody(req, { length: req.headers['content-length'], limit: '1mb', encoding: contentType.parse(req).parameters.charset }, function(err, buf) { utils.formatMessage(buf.toString()).then(message => { //判斷消息,作出迴應 }) } )
我將格式化 xml 的操做封裝在 formatMessage服務器
var xml2js = require('xml2js') exports.formatMessage = function(xml) { return new Promise((resolve, reject) => { // 接收文本信息格式 // <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> xml2js.parseString(xml, function(err, content) { var result = content.xml var message = {}; if (typeof result === 'object') { var keys = Object.keys(result); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var item = result[key]; if (!(item instanceof Array) || item.length === 0) continue; if (item.length === 1) { var val = item[0]; if (typeof val === 'object') message[key] = formatMessage(val); else message[key] = (val || '').trim(); } else { message[key] = []; for (var j = 0, k = item.length; j < k; j++) message[key].push(formatMessage(item[j])); } } } resolve(message) }) }) }
解析完成後咱們能夠拿到 FromUserName、MsgType 和 Content微信
MsgType多是 event(事件)或者是 text (文本)ide
event類型有:subscribe,unsubscribe,LOCATION,CLICK,SCANui
根據 content中發送的內容,咱們能夠進行判斷,返回自定義消息回覆this
微信規定咱們返回的數據必須是xml格式的,格式參考:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140543url
所以在返回信息前須要拼接內容成指定xml格式,我將拼接方法封裝在 template.js 文件中,使用時只要直接調用便可
lib/template.js:
exports.textMessage = function(message){ var createTime = new Date().getTime() return `<xml> <ToUserName><![CDATA[${message.FromUserName}]]></ToUserName> <FromUserName><![CDATA[${message.ToUserName}]]></FromUserName> <CreateTime>${createTime}</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[${message.reply}]]></Content> </xml>` } exports.imageMessage = function(message){ var createTime = new Date().getTime() return `<xml> <ToUserName><![CDATA[${message.FromUserName}]]></ToUserName> <FromUserName><![CDATA[${message.ToUserName}]]></FromUserName> <CreateTime>${createTime}</CreateTime> <MsgType><![CDATA[image]]></MsgType> <Image> <MediaId><![CDATA[${message.mediaId}]]></MediaId> </Image> </xml>` } exports.voiceMessage = function(message){ var createTime = new Date().getTime() return `<xml> <ToUserName><![CDATA[${message.FromUserName}]]></ToUserName> <FromUserName><![CDATA[${message.ToUserName}]]></FromUserName> <CreateTime>${createTime}</CreateTime> <MsgType><![CDATA[voice]]></MsgType> <Voice> <MediaId><![CDATA[${message.mediaId}]]></MediaId> </Voice> </xml>` } exports.videoMessage = function(message){ var createTime = new Date().getTime() return `<xml> <ToUserName><![CDATA[${message.FromUserName}]]></ToUserName> <FromUserName><![CDATA[${message.ToUserName}]]></FromUserName> <CreateTime>${createTime}</CreateTime> <MsgType><![CDATA[video]]></MsgType> <Video> <MediaId><![CDATA[${message.mediaId}]]></MediaId> <Title><![CDATA[${message.title}]]></Title> <Description><![CDATA[${message.description}]]></Description> </Video> </xml>` } exports.articleMessage = function(message){ var createTime = new Date().getTime() return `<xml> <ToUserName><![CDATA[${message.FromUserName}]]></ToUserName> <FromUserName><![CDATA[${message.ToUserName}]]></FromUserName> <CreateTime>${createTime}</CreateTime> <MsgType><![CDATA[news]]></MsgType> <ArticleCount>${message.articles.length}</ArticleCount> <Articles> ${message.articles.map(article => `<item><Title><![CDATA[${article.title}]]></Title> <Description><![CDATA[${article.description}]]></Description> <PicUrl><![CDATA[${article.img}]]></PicUrl> <Url><![CDATA[${article.url}]]></Url></item>` ).join('')} </Articles> </xml>` }
自動回覆總體流程:收到微信請求->校驗是否來自微信->獲取access_token->解析請求體xml->根據類型以及內容做出相應
回覆代碼:
var express = require('express') var router = express.Router() var getRawBody = require('raw-body') var contentType = require('content-type') var utils = require('../lib/utils.js') var template = require('../lib/template.js') // 微信官方請求回調接口 router.all('/', function(req, res, next) { var data = getRawBody(req, { length: req.headers['content-length'], limit: '1mb', encoding: contentType.parse(req).parameters.charset }, function(err, buf) { if (err) return next(err) utils.formatMessage(buf.toString()).then(message => { if (message.MsgType == 'event') { if (message.Event === 'subscribe') { if (message.EventKey) { console.log('掃描二維碼關注:' + message.EventKey + ' ' + message.ticket); } message.reply = '終於等到你,還好我沒放棄'; } else if (message.Event === 'unsubscribe') { message.reply = ''; console.log(message.FromUserName + ' 悄悄地走了...'); } else if (message.Event === 'LOCATION') { message.reply = '您上報的地理位置是:' + message.Latitude + ',' + message.Longitude; } else if (message.Event === 'CLICK') { message.reply = '您點擊了菜單:' + message.EventKey; } else if (message.Event === 'SCAN') { message.reply = '關注後掃描二維碼:' + message.Ticket; } res.send(template.textMessage(message)) } else if (message.MsgType === 'text') { var content = message.Content if (content === '1') { message.reply = '終於等到你' res.send(template.textMessage(message)) } else if (content === '2') { message.mediaId = '須要發送圖片的媒體id' res.send(template.imageMessage(message)) } else if (content === '3') { message.articles = [{ title: '標題', description: '描述', picUrl: '圖片路徑,不須要事先上傳', url: '素材路徑,素材須要事先上傳' }] res.send(template.articleMessage(message)) } else { message.reply = '你說的話:「' + content + '」,我聽不懂呀' res.send(template.textMessage(message)) } } }) }) }); module.exports = router;