使用 nodeJs 开发微信公众号(设置自动回复消息)
微信向第三方服务器发送请求时会降 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 开发微信公众号(设置自动回复消息)的更多相关文章
- NodeJs 开发微信公众号(五)真实环境部署
在测试环境下开发完成代表着你离正式上线的目标不远了.接下来本章就主要谈一谈把测试环境的公众号升级为正式的公众号. 服务器和域名 目前为止我们只是在自己的电脑上完成了测试环境.真实的线上环境当然需要自己 ...
- NodeJs 开发微信公众号(四)微信网页授权
微信的网页授权指的是在微信公众号中访问第三方网页时获取用户地理.个人等信息的权限.对于开发了自己的网页app应用时,获取个人的信息非常重要.上篇博客讲到了注册时可以获取用户的信息,很多人会问为什么还需 ...
- NodeJs 开发微信公众号(三)微信事件交互
微信公众号有个规则,一旦开启了开发者模式,其他的常规功能就都必须通过接口调用完成.比如说自定义菜单功能,必须通过发送post请求的方式生成.本章就通过关注到取消关注的整个过程来谈一谈nodejs是怎么 ...
- NodeJs 开发微信公众号(二)测试环境部署
由于卤煮本人是做前端开发的,所以在做公众号过程中基本上没有遇到前端问题,在这方面花的时间是最少的.加上用了mui框架(纯css界面)和自己积累的代码,很快地开发出了界面来.接着是后台开发.卤煮选的是n ...
- NodeJs 开发微信公众号(一)准备工作
前言 大概是一个月前,自己用业余时间做了一个微信公众号.微信开发,尤其是对后台不熟悉的人来说显得尤其困难.首先要克服的是后台语言(nodejs)的一些不熟悉困难,其次,也是最大的一点困难是在跟微信交互 ...
- Nodejs开发微信公众号中控服务
本文已同步到专业技术网站 www.sufaith.com, 该网站专注于前后端开发技术与经验分享, 包含Web开发.Nodejs.Python.Linux.IT资讯等板块. 本项目旨在为多个微信公众号 ...
- 使用 nodeJs 开发微信公众号(上传图片)
在给用户发送消息中涉及到的素材(图片.视频.音频.文章等)需要事先传到微信服务器,然后获得媒体id(media_id),然后把 media_id 传递给用户 上传分上传临时素材(只保存三天)和上传永久 ...
- 使用 nodeJs 开发微信公众号(配置服务器)
流程如下: 1. 申请微信公众号:企业号.服务号.订阅号(前两个要钱) 2. 配置微信公众号后台 选择基本配置,获得 AppId 和 AppSecret ,点击服务器配置 URL:你服务器地址,不能是 ...
- 使用 nodeJs 开发微信公众号(获取access_token)
要使用微信提供的功能接口,就需要获取到access_token,这是开发公众号必不可少的一部 access_token有效期20分钟,建议保存起来,过期后在重新获取 获取流程如下: 我将微信相关的操作 ...
随机推荐
- 第一次博客作业(初识C++)
Q1:学习<C++语言程序设计>课程之前,你知道什么是编程吗?谈谈上这门课之前你对编程的理解,以及你对自己编程能力的评估. A1:开始课程之前,我认为编程是这样的:用计算机的语言写一份流程 ...
- nexus 数据库备份任务webhook 通知
nexus 的数据库备份是比较重要的,可以方便我们在故障的是时候进行应用恢复. 以下使用benthos 组件进行nexus 数据库备份事件的应用通知处理 环境准备 使用docker-compose 运 ...
- nginx ngx_http_image_filter_module 简单试用
nginx包含了一个ngx_http_image_filter_module 模块,我们可以方便的进行图片的缩略图,平时一些简单的功能 已经够用了 环境准备 为了简单使用docker-compose ...
- springboot 添加 jsp支持
idea 新建springboot项目 packageing:选择 war next 勾选web 添加jar包 <dependency> <groupId>org.apac ...
- Java基础随记-不定时更新
一.hashMap与hashTable与ConcurrentHashMap: 1.HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类.不过它们都同时实现 ...
- MongoDB解压报错gzip: stdin: not in gzip format的解决方法
MongoDB解压报错gzip: stdin: not in gzip format的解决方法 在安装MongoDB时出现如下报错: [root@vm172--- mongodb]# tar -zxv ...
- 360或者金山毒霸可能会导致HP网络打印机驱动安装失败“数据无效”的解决办法
360或者金山毒霸可能会导致HP网络打印机驱动安装失败“数据无效”的解决办法 同事办公室的打印机是网线接口的那种网络打印机,不是直接连到电脑的那种,他电脑安装了360和金山毒霸,WIN10下安 ...
- java.util.concurrent包下集合类的特点与适用场景
java.util.concurrent包,此包下的集合都不允许添加null元素 序号 接口 类 特性 适用场景 1 Queue.Collection ArrayBlockingQueue 有界.阻塞 ...
- three.js:使用createMultiMaterialObject创建的多材质对象无法使用光线跟踪Raycaster选中
创建多材质对象: var loader = new THREE.DDSLoader(); var map = loader.load('../assets/textures/Mountains_arg ...
- note 9 列表、时间复杂度、排序
列表 List +内建(built-in)数据结构(data structure),用来存储一系列元素(items) 如:lst = [5.4,'hello',2] 前向索引.后向索引.切片.拼接.成 ...