node.js 微信开发2-消息回复、token获取、自定义菜单
项目结构
>config/wechat.json 微信公众号的配置文件
>controllers/oauth.js 微信网页授权接口(下一篇再细讲讲)
>controllers/wechat.js 微信公众号接口(包括接入接口和其他调用微信api的接口)
>wechat/access_token.json 请求微信api接口之前都需要使用的access_token
>wechat/crytoGraphy.js 加密解密文件(这里使用的是明文方式,未用到)
>wechat/menus.json 微信公众号的自定义菜单
>wechat/wechat.js 调用微信api的方法
项目的架构说明和使用步骤可以参考前面一篇的‘node.js 接口调用示例’:https://www.cnblogs.com/eye-like/p/11743744.html
一些说明点
1、公众号的接入接口和接收消息接口
两个接口的地址是一致的,区别是:
> 接入接口是GET请求,需要的是对get的接口参数进行解析验证,然后按需返回就可以了
> 接收消息接口是POST请求,需要先解析微信发送过来的消息,然后根据情况决定是否返回消息
WeChat.prototype.handleMsg = async function (ctx) {
return new Promise((resolve, reject) => { // let req = ctx.request;
// let res = ctx.response;
let req = ctx.req;
var buffer = [],
that = this; //实例微信消息加解密
// var cryptoGraphy = new CryptoGraphy(that.config,ctx.request);
//监听 data 事件 用于接收数据
req.on('data', function (data) {
// logger.info("on data", data);
buffer.push(data);
});
req.on('end', function () {
// logger.info("on end");
var msgXml = Buffer.concat(buffer).toString('utf-8'); parseString(msgXml, {
explicitArray: false
}, function (err, result) {
// logger.info("on result", result);
result = result.xml;
resolve(result)
})
});
}) },
接收微信消息
* 接收消息使用的是Promise函数封装,目的是将微信的收发消息两块做隔离
* 涉及到node.js 接收post参数的方式,需要ctx.req.on('data',function(){})方法接受参数,并在ctx.req.on(‘end’,function(){})函数中接受最终的获取参数
* 因为微信发过来的消息是xml格式,所以在node.js 中需要xml2js模块(非node.js内置模块,需要先安装依赖)将xml文件解析
/**
* 微信消息回复
* @param {ctx} context 对象
* @param {result} 微信消息
*/
WeChat.prototype.responseMsg = function (ctx, result) {
var toUser = result.ToUserName; //接收方微信
var fromUser = result.FromUserName; //发送仿微信
var reportMsg = ""; //声明回复消息的变量
if (result.MsgType.toLowerCase() === "event") {
//判断事件类型
switch (result.Event.toLowerCase()) {
case 'subscribe':
//回复消息
var content = "欢迎关注 线上随访 公众号\n";
content += "我们致力于帮助出院康复病人与医生建立便捷的沟通渠道~\n";
reportMsg = msg.txtMsg(fromUser, toUser, content);
break;
case 'click':
var contentArr = [{
Title: "Node.js 微信自定义菜单",
Description: "使用Node.js实现自定义微信菜单",
PicUrl: "http://img.blog.csdn.net/20170605162832842?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
Url: "http://blog.csdn.net/hvkcoder/article/details/72868520"
},
{
Title: "Node.js access_token的获取、存储及更新",
Description: "Node.js access_token的获取、存储及更新",
PicUrl: "http://img.blog.csdn.net/20170528151333883?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
Url: "http://blog.csdn.net/hvkcoder/article/details/72783631"
},
{
Title: "Node.js 接入微信公众平台开发",
Description: "Node.js 接入微信公众平台开发",
PicUrl: "http://img.blog.csdn.net/20170605162832842?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
Url: "http://blog.csdn.net/hvkcoder/article/details/72765279"
}
];
//回复图文消息
reportMsg = msg.graphicMsg(fromUser, toUser, contentArr);
break;
}
} else {
//判断消息类型为 文本消息
if (result.MsgType.toLowerCase() === "text") {
switch (result.Content) {
case '':
reportMsg = msg.txtMsg(fromUser, toUser, 'Hello ,线上随访公众号开通了,快来使用吧……');
break;
case '':
reportMsg = msg.txtMsg(fromUser, toUser, 'Ha Ha,我还有更多的功能待发掘呢,敬请期待吧……');
break;
case '文章':
var contentArr = [{
Title: "Node.js 微信自定义菜单",
Description: "使用Node.js实现自定义微信菜单",
PicUrl: "http://img.blog.csdn.net/20170605162832842?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
Url: "http://blog.csdn.net/hvkcoder/article/details/72868520"
},
{
Title: "Node.js access_token的获取、存储及更新",
Description: "Node.js access_token的获取、存储及更新",
PicUrl: "http://img.blog.csdn.net/20170528151333883?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
Url: "http://blog.csdn.net/hvkcoder/article/details/72783631"
},
{
Title: "Node.js 接入微信公众平台开发",
Description: "Node.js 接入微信公众平台开发",
PicUrl: "http://img.blog.csdn.net/20170605162832842?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
Url: "http://blog.csdn.net/hvkcoder/article/details/72765279"
}
];
//回复图文消息
reportMsg = msg.graphicMsg(fromUser, toUser, contentArr);
break;
default:
reportMsg = msg.txtMsg(fromUser, toUser, '没有这个选项哦');
break;
}
// logger.info("on reportMsg", reportMsg);
}
}
//返回给微信服务器
ctx.body = reportMsg
},
回复微信消息
# 主要任务是根据微信消息的类型‘MsgType’,来执行不同的动作
# 事件推送消息(MsgType==‘event’)
* 关注取消事件 subscribe,unsubscribe
此时可以做数据库接入录入订阅者信息或者更新订阅者信息
* 扫描带参数二维码事件
用户未关注时:qrscene_ +二维码参数值
用户已关注时:scan
* 上报地理位置事件 LOCATION
* 自定义菜单事件 CLICK
# 普通消息推送 (MsgType=='text')
可分为 文本消息、图片消息、语音消息、视频消息、小视频消息、地理位置消息、链接消息
2、有参考的node.js 发送get、post请求的两个方法
/**
* 用于处理 https Get请求方法
* @param {String} url 请求地址
*/
this.requestGet = function (url) {
return new Promise(function (resolve, reject) {
https.get(url, function (res) {
var buffer = [],
result = "";
//监听 data 事件
res.on('data', function (data) {
buffer.push(data);
});
//监听 数据传输完成事件
res.on('end', function () {
result = Buffer.concat(buffer).toString('utf-8');
//将最后结果返回
resolve(result);
});
}).on('error', function (err) {
reject(err);
});
});
}
node.js 发送get请求
/**
* 用于处理 https Post请求方法
* @param {String} url 请求地址
* @param {JSON} data 提交的数据
*/
this.requestPost = function (url, data) {
return new Promise(function (resolve, reject) {
//解析 url 地址
var urlData = urltil.parse(url);
//设置 https.request options 传入的参数对象
var options = {
//目标主机地址
hostname: urlData.hostname,
//目标地址
path: urlData.path,
//请求方法
method: 'POST',
//头部协议
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(data, 'utf-8')
}
};
var req = https.request(options, function (res) {
var buffer = [],
result = '';
//用于监听 data 事件 接收数据
res.on('data', function (data) {
buffer.push(data);
});
//用于监听 end 事件 完成数据的接收
res.on('end', function () {
result = Buffer.concat(buffer).toString('utf-8');
resolve(result);
})
})
//监听错误事件
.on('error', function (err) {
console.log(err);
reject(err);
});
//传入数据
req.write(data);
req.end();
});
}
}
node.js 发送post请求
注:使用前需要引用内置的https中间件
3、关于access_token
调用微信接口的时候都需要发送微信接口凭证access_token(除微信网页授权中获取的access_token并不是这个access_token),因此请求微信接口的第一步都是要先获取这个access_oken
/**
* 获取微信 access_token
*/
WeChat.prototype.getAccessToken = function () {
var that = this;
return new Promise(function (resolve, reject) {
//获取当前时间
var currentTime = new Date().getTime();
//格式化请求地址
var url = util.format(that.apiURL.accessTokenApi, that.apiDomain, that.appID, that.appScrect);
//判断 本地存储的 access_token 是否有效
if (accessTokenJson.access_token === "" || accessTokenJson.expires_time < currentTime) {
that.requestGet(url).then(function (data) {
var result = JSON.parse(data);
if (data.indexOf("errcode") < ) {
accessTokenJson.access_token = result.access_token;
accessTokenJson.expires_time = new Date().getTime() + (parseInt(result.expires_in) - ) * ;
//更新本地存储的
fs.writeFile('./wechat/access_token.json', JSON.stringify(accessTokenJson));
//将获取后的 access_token 返回
resolve(accessTokenJson.access_token);
} else {
//将错误返回
resolve(result);
}
});
} else {
//将本地存储的 access_token 返回
resolve(accessTokenJson.access_token);
}
});
}
获取access_token
var url = util.format(that.apiURL.accessTokenApi, that.apiDomain, that.appID, that.appScrect);
url参数依次为:
that.apiURL.accessTokenApi:"%scgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"
that.apiDomain:"https://api.weixin.qq.com/"
that.appID:公众号的appID
that.appScrect:公众号的密匙
请求后获取的accee_token 有效时间为7200s,在此时间内,再次请求微信接口都可以使用已经缓存的accee_token
4、关于菜单
最好是在请求接口的时候加入请求密匙(如oauth2认证),尤其是更新和删除
git:https://github.com/wuyongxian20/wechat-api
node.js 微信开发2-消息回复、token获取、自定义菜单的更多相关文章
- node.js 微信开发1-接入
准备工作1 域名准备 无论是个人开发还是做公司项目域名都是必不可少的 前期我个人用过花生壳做个开发测试,挺好用的,就是现在要收费了,开通花生壳要收费,开通内网穿透要收费(为啥要内网穿透呢,因为微信接入 ...
- node.js 微信开发3-网页授权
1.配置公众号的自定义菜单,如 { "button":[ { "type":"view", "name":"公 ...
- node JS 微信开发
JS-SDK 要点 微信测试号; 扫码登录;无需认证(只是名称统一为微信测试号)http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/logi ...
- 4.Node.js 微信消息管理
一.写在前面的话 当用户发送消息给公众号时(或某些特定的用户操作引发的事件推送时),会产生一个POST请求,开发者可以在响应包(Get)中返回特定XML结构,来对该消息进行响应. 消息推送也是 ...
- vue+node.js+webpack开发微信公众号功能填坑——v -for循环
页面整体框架实现,实现小功能,循环出数据,整体代码是上一篇 vue+node.js+webpack开发微信公众号功能填坑--组件按需引入 修改部门代码 app.vue <yd-flexbox&g ...
- Force.com微信开发系列(四)申请Access Token及自定义菜单之创建菜单
在微信接口开发中,许多服务的使用都离不开Access Token,Access Token相当于打开这些服务的钥匙,正常情况下会在7200秒内失效,重复获取将导致上次获取的Token失效,本文将首先介 ...
- node.js之开发环境搭建
一.安装linux系统 (已安装linux可跳此步骤) 虚拟机推荐选择:VirtualBox 或者 Vmware (专业版永久激活码:5A02H-AU243-TZJ49-GTC7K-3C61N) 我这 ...
- AngularJS + Node.js + MongoDB开发
AngularJS + Node.js + MongoDB开发的基于位置的通讯录(by vczero) 一.闲扯 有一天班长说了,同学们希望我开发一个可以共享位置的通讯录,于是自己简单设计了下功能.包 ...
- Ubuntu 14.04下搭建Node.js的开发环境
最近想找一个轻量级且支持快速开发的服务开发平台,选来选去选择了Node.js,当时有几种选择: Python + Django(用过Django,虽然开发快速,但是感觉性能并不太好). Ruby + ...
随机推荐
- k8s记录-dockerfile
FROM 代表基于哪个镜像 RUN 安装软件使用 MAINTAINER 镜像的创建者 CMD 容器启动时执行的命令,但是一个Dockerfile中只能有一条CMD命令,多条则只执行最后一条CMD EN ...
- matlab基本函数min
一起来学演化计算-matlab基本函数min 觉得有用的话,欢迎一起讨论相互学习~Follow Me 找到数组中最小的元素 语法 M = min(A) 返回A的最小元素 如果A是一个向量,那么min( ...
- 为什么在MySQL数据库中无法创建外键?(MyISAM和InnoDB详解)
问题描述:为什么在MySQL数据库中不能创建外键,尝试了很多次,既没有报错,也没有显示创建成功,真实奇了怪,这是为什么呢? 问题解决:通过查找资料,每次在MySQL数据库中创建表时默认的情况是这样的: ...
- spring 使用@Bean装配Bean
通过@Component装配Bean,但是@Component只能注解在类上,不能注解到方法上.对于Java而言,大部分的开发都需要引入第三方的包(jar文件),而且往往并没有这些包的源码,这时候将无 ...
- 实现Modbus TCP多网段客户端应用
对于Modbus TCP来说与Modbus RTU和Modbus ASCII有比较大的区别,因为它是运行于以太网链路之上,是运行于TCP/IP协议之上的一种应用层协议.在协议栈的前两个版本中,Modb ...
- 使用docker搭建FastDFS文件系统
1.首先下载FastDFS文件系统的docker镜像 docker search fastdfs 2.使用docker镜像构建tracker容器(跟踪服务器,起到调度的作用): docker run ...
- 高级UI-自定义动画框架
有的时候会需要做一些自定义的动画效果,在会反复用到的动画效果可以考虑做成动画框架,方便使用,做成框架的话就需要考虑很多的问题,最典型的问题就是属性和方法必须要是可配置的,这里就来聊一聊自定义动画框架的 ...
- JIRA数据库的迁移,从HSQL到MYSQL/Oracle
Jira数据库迁移,从HSQL到MYSQL 通过JIRA管理员登录,进入“管理员页面”,“系统”--“导入&导出”,以XML格式备份数据. 在MySQL中创建Schema,命名为jira 关闭 ...
- LeetCode 617. 合并二叉树(Merge Two Binary Trees)
617. 合并二叉树 617. Merge Two Binary Trees 题目描述 给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠. 你需要将他们合并为一个新 ...
- Win10 将本地连接设置为按流量计费网络
在Win10中默认是不允许用户将本地连接设置为按流量计费网络的,不过我们可以通过修改注册表的方式来实现. 将本地连接设置为按流量计费网络后,Windows更新将不会自动下载.同样,Windows应用商 ...