微信向第三方服务器发送请求时会降 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. robot framework中的返回值

    1.若想要再setup中有返回值,给后续的操作使用 A)在setup的关键词中需要的返回值,设置为global variable或者suit variable:如下图:但是在编译器中,会报错,但是执行 ...

  2. [工作积累] TAA Ghosting 的相关问题

    因为TAA要使用上一帧的历史结果,那么在相机移动的时候,颜色就会有残留,出现ghosting(残影). 由于上一帧历史是累积的,是由上一帧的直接渲染结果和上上帧的结果做了合并,所以ghosting并不 ...

  3. JavaScript中的ononline事件和onoffline事件

    关于这个时间的描述到处都有,但基本上都是说离线在线什么的我一下子还没反应过来.后再在这里看到了一句话:"断开网络再联网试试,就可以看到连线的提示."这才反应过来,原来指的是网络状态 ...

  4. CCNet: Criss-Cross Attention for Semantic Segmentation 里的Criss-Cross Attention计算方法

    论文地址:https://arxiv.org/pdf/1811.11721v1.pdf  code address: https://github.com/speedinghzl/CCNet 相关论文 ...

  5. C#如何调用以管理员身份运行的cmd命令提示符

    windows自从vista.win7.win8/8.1以及win10以来,命令行提示符分为两种模式,一种是普通用户模式,一种的管理员模式,很多情况下,我们的程序需要在命令提示符(管理员身份)的状况下 ...

  6. Kuberneteser二进制安装与配置(二)

    环境:Centos7 版本:Kubernetes v1.11.4   一.下载Kubernetes   1)下载 wget https://github.com/kubernetes/kubernet ...

  7. js 中逻辑为 false 的8种情况

    如果对象无初始值或者其值为 数字0.-0.null."".false.undefined 或者 NaN,那么对象的逻辑值为 false. 注意:字符串 '0',值为 true ty ...

  8. long和int互转

    现在分long,Long,int,Integer互相转换,分8种情况 a     ,    b long, int                b=(int)a; long,Integer      ...

  9. winmount导致资源管理器崩溃

    今天,在别人电脑上遇到一个问题,右键点击某个特定文件夹会导致资源管理器崩溃重启,提示为: Runtime Error! Program: C:\Windows\Explorer.exe This ap ...

  10. Kettle解决方案: 第一章ETL入门

    第一章ETL入门 1.1 OLPT和数据仓库对比 普通的事务系统和商业智能系统(BI)有什么区别? 1个独立的普通事务系统也被称为在线事务处理系统(OLTP) 商业智能系统也常被称为决策支持系统(DS ...