微信向第三方服务器发送请求时会降 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;

使用 nodeJs 开发微信公众号(设置自动回复消息)的更多相关文章

  1. NodeJs 开发微信公众号(五)真实环境部署

    在测试环境下开发完成代表着你离正式上线的目标不远了.接下来本章就主要谈一谈把测试环境的公众号升级为正式的公众号. 服务器和域名 目前为止我们只是在自己的电脑上完成了测试环境.真实的线上环境当然需要自己 ...

  2. NodeJs 开发微信公众号(四)微信网页授权

    微信的网页授权指的是在微信公众号中访问第三方网页时获取用户地理.个人等信息的权限.对于开发了自己的网页app应用时,获取个人的信息非常重要.上篇博客讲到了注册时可以获取用户的信息,很多人会问为什么还需 ...

  3. NodeJs 开发微信公众号(三)微信事件交互

    微信公众号有个规则,一旦开启了开发者模式,其他的常规功能就都必须通过接口调用完成.比如说自定义菜单功能,必须通过发送post请求的方式生成.本章就通过关注到取消关注的整个过程来谈一谈nodejs是怎么 ...

  4. NodeJs 开发微信公众号(二)测试环境部署

    由于卤煮本人是做前端开发的,所以在做公众号过程中基本上没有遇到前端问题,在这方面花的时间是最少的.加上用了mui框架(纯css界面)和自己积累的代码,很快地开发出了界面来.接着是后台开发.卤煮选的是n ...

  5. NodeJs 开发微信公众号(一)准备工作

    前言 大概是一个月前,自己用业余时间做了一个微信公众号.微信开发,尤其是对后台不熟悉的人来说显得尤其困难.首先要克服的是后台语言(nodejs)的一些不熟悉困难,其次,也是最大的一点困难是在跟微信交互 ...

  6. Nodejs开发微信公众号中控服务

    本文已同步到专业技术网站 www.sufaith.com, 该网站专注于前后端开发技术与经验分享, 包含Web开发.Nodejs.Python.Linux.IT资讯等板块. 本项目旨在为多个微信公众号 ...

  7. 使用 nodeJs 开发微信公众号(上传图片)

    在给用户发送消息中涉及到的素材(图片.视频.音频.文章等)需要事先传到微信服务器,然后获得媒体id(media_id),然后把 media_id 传递给用户 上传分上传临时素材(只保存三天)和上传永久 ...

  8. 使用 nodeJs 开发微信公众号(配置服务器)

    流程如下: 1. 申请微信公众号:企业号.服务号.订阅号(前两个要钱) 2. 配置微信公众号后台 选择基本配置,获得 AppId 和 AppSecret ,点击服务器配置 URL:你服务器地址,不能是 ...

  9. 使用 nodeJs 开发微信公众号(获取access_token)

    要使用微信提供的功能接口,就需要获取到access_token,这是开发公众号必不可少的一部 access_token有效期20分钟,建议保存起来,过期后在重新获取 获取流程如下: 我将微信相关的操作 ...

随机推荐

  1. [WC2006]水管局长

    原题链接 前言 搞不懂为什么要写LCT,搞不懂为什么要加强数据.像这道题是用父亲表示法来做的.虽然复杂度不是log,但是现在下面这份代码却是无论从空间,还是代码量,还是时间都是优秀不止一点. 而且这样 ...

  2. javascript 创建节点和新增节点

    createElement(tabName) 创建一个为tagName的新元素节点 ANode.appendChild(BNode)把B节点追加至A节点的末尾 insertBefore(ANode,B ...

  3. mac电脑上如何启动mysql

    export PATH=$PATH:/usr/local/mysql/bin/   mysql -uroot -p

  4. 编写一个简单的基于jmespath 的prometheus exporter

    目的很简单,因为系统好多监控指标是通过json 暴露的,并不是标准的prometheus metrics 格式,处理方法 实际上很简单,我们可以基于jsonpath 解析json数据,转换为prome ...

  5. Shell脚本出现$'\r': command not found

    Centos7下执行shell脚本报错如下 [root@ip---- ~]# sh install_zabbix_agent.sh install_zabbix_agent.: $'\r': comm ...

  6. 安装netcat(-bash: netcat: command not found)

    安装netcat 在用swoole UDP服务器可以使用netcat -u 来连接测试时,报错-bash: netcat: command not found,是因为centos7未安装netcat. ...

  7. Python学习:列表、元组、字典、集合

    转载:https://www.cnblogs.com/xc-718/p/9632942.html 列表/元组 列表和元组都是序列结构,它们本身很相似,但又有一点不同: 列表是用方括号标记,如:a=[1 ...

  8. php7 使用dom动态生成xml文档

    <?php $dom = new DomDocument('1.0','gb2312'); //创建DOM对象 $store = $dom->createElement('store'); ...

  9. Convert.ChangeType不能处理Nullable类型的解决办法(转)

    https://www.cnblogs.com/patrickyu/p/3211115.html 在做一个ORMapping功能的时候发现,Convert.ChangeType不能处理nullable ...

  10. 使用 random() 生成礼包码

    需求:生成100个游戏礼包码 具体如下 # 1.构造100个5位随机数 listNumA = [] for n in range(100): listNumA.append(random.randin ...