xiaoliwang

微信向第三方服务器发送请求时会降 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) => {
        
        // 接收文本信息格式
        // <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 (文本)

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 `<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;

 

分类:

技术点:

相关文章:

  • 2021-11-17
  • 2021-07-14
  • 2022-12-23
  • 2021-09-12
  • 2021-11-29
  • 2021-09-16
  • 2021-04-08
  • 2021-06-12
猜你喜欢
  • 2021-12-25
  • 2021-12-23
  • 2021-07-04
  • 2022-01-08
  • 2021-09-03
  • 2021-12-16
  • 2021-05-29
相关资源
相似解决方案