微信向第三方服务器发送请求时会降 signature 、timestamp、 nonce 、 openid(用户标识),发送内容会以 xml 的形式附加在请求中
回复消息前提我们得拿到用户id , 用户发送内容等信息,用户发送内容格式参考微信官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140453
想要获取用户发送信息,需要从请求中获得 xml ,因此需要用到 raw-body(获得原生请求体)
npm install raw-body --save
接下来需要将xml从请求中分离并且格式化成json
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) => { // 接收文本信息格式 //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) }) })} // // 1348831860 //// // 1234567890123456
解析完成后我们可以拿到 FromUserName、MsgType 和 Content
MsgType可能是 event(事件)或者是 text (文本)
event类型有:subscribe,unsubscribe,LOCATION,CLICK,SCAN
根据 content中发送的内容,我们可以进行判断,返回自定义消息回复
微信规定我们返回的数据必须是xml格式的,格式参考:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140543
因此在返回信息前需要拼接内容成指定xml格式,我将拼接方法封装在 template.js 文件中,使用时只要直接调用即可
lib/template.js:
exports.textMessage = function(message){ var createTime = new Date().getTime() return ``}exports.imageMessage = function(message){ var createTime = new Date().getTime() return ` ${createTime} `}exports.voiceMessage = function(message){ var createTime = new Date().getTime() return ` ${createTime} `}exports.videoMessage = function(message){ var createTime = new Date().getTime() return ` ${createTime} `}exports.articleMessage = function(message){ var createTime = new Date().getTime() return ` ${createTime} `} ${createTime} ${message.articles.length} ${message.articles.map(article => ` - ` ).join('')}
<![CDATA[${article.title}]]>
自动回复整体流程:收到微信请求->校验是否来自微信->获取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;