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

    mapActions() 返回的是一个对象, 用了 ... 扩展符后,才可以放进一个对象里,和其他组件内定义的 method 在同一个 methods 对象. { methods: mapAction ...

  2. Java入门练习之四则运算

    Java入门练习之四则运算 项目目的 制作一个Java控制台程序,让他可以随机生成四则运算,并针对用户的输入判断对错. 项目分析 该项目本质上是一个四则运算出题器,可以创建一个Question类,其中 ...

  3. Redis连接方式

    连接redis 本地安装了Redis并运行在6379端口,密码设置为 foobared. 1. from redis import StrictRedis redis = StrictRedis(ho ...

  4. sql注入-输入’or 1=1#

    比如:在用户名输入框中输入:’or 1=1#,密码随便输入,这时候的合成后的SQL查询语句为:     select * from users where username='' or 1=1#' a ...

  5. mysql 生成时间序列数据 - 存储过程

    由于时间自动转换为int值, 做一步转化,也可在调用时处理 use `test`; CREATE table test.test1 as SELECT state, id, `规格条码`, `色号条码 ...

  6. EasyMall注册功能

    EasyMall注册功能 1. 环境搭建 创建一个EasyMall的web应用 配置www.easymall.com网站,并配置为缺省的虚拟主机,将EasyMall配 置为缺省的web应用,在配置in ...

  7. 使用korofileheader插件vs code添加文件头注释和函数注释

    korofileheadervs code添加文件头注释和函数注释1.extensions搜索fileheader,安装koroFileHeader2.设置:edit=>perference=& ...

  8. JavaScript 高级特性

    1. 原型Prototype 1.1 构造函数 所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量.对构造函数使用new运算符,就能生成实例,并且this变量会绑定 ...

  9. .Net Core跨平台应用研究-HelloArm(串口篇)

    引言 为了验证采用dotnet core技术开发的物联网设备数据采集接入服务应用是否能在高性价比的linux嵌入式平台运行,针对dotnet core应用程序进行嵌入式linux环境的发布部署运行验证 ...

  10. [蓝桥杯]PREV-12.历届试题_危险系数

    问题描述 抗日战争时期,冀中平原的地道战曾发挥重要作用. 地道的多个站点间有通道连接,形成了庞大的网络.但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系. 我们来定义一个危险系数DF( ...