项目结构

>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请求,需要先解析微信发送过来的消息,然后根据情况决定是否返回消息

  1. WeChat.prototype.handleMsg = async function (ctx) {
  2. return new Promise((resolve, reject) => {
  3.  
  4. // let req = ctx.request;
  5. // let res = ctx.response;
  6. let req = ctx.req;
  7. var buffer = [],
  8. that = this;
  9.  
  10. //实例微信消息加解密
  11. // var cryptoGraphy = new CryptoGraphy(that.config,ctx.request);
  12. //监听 data 事件 用于接收数据
  13. req.on('data', function (data) {
  14. // logger.info("on data", data);
  15. buffer.push(data);
  16. });
  17. req.on('end', function () {
  18. // logger.info("on end");
  19. var msgXml = Buffer.concat(buffer).toString('utf-8');
  20.  
  21. parseString(msgXml, {
  22. explicitArray: false
  23. }, function (err, result) {
  24. // logger.info("on result", result);
  25. result = result.xml;
  26. resolve(result)
  27. })
  28. });
  29. })
  30.  
  31. },

接收微信消息

* 接收消息使用的是Promise函数封装,目的是将微信的收发消息两块做隔离

* 涉及到node.js 接收post参数的方式,需要ctx.req.on('data',function(){})方法接受参数,并在ctx.req.on(‘end’,function(){})函数中接受最终的获取参数

* 因为微信发过来的消息是xml格式,所以在node.js 中需要xml2js模块(非node.js内置模块,需要先安装依赖)将xml文件解析

  1. /**
  2. * 微信消息回复
  3. * @param {ctx} context 对象
  4. * @param {result} 微信消息
  5. */
  6. WeChat.prototype.responseMsg = function (ctx, result) {
  7. var toUser = result.ToUserName; //接收方微信
  8. var fromUser = result.FromUserName; //发送仿微信
  9. var reportMsg = ""; //声明回复消息的变量
  10. if (result.MsgType.toLowerCase() === "event") {
  11. //判断事件类型
  12. switch (result.Event.toLowerCase()) {
  13. case 'subscribe':
  14. //回复消息
  15. var content = "欢迎关注 线上随访 公众号\n";
  16. content += "我们致力于帮助出院康复病人与医生建立便捷的沟通渠道~\n";
  17. reportMsg = msg.txtMsg(fromUser, toUser, content);
  18. break;
  19. case 'click':
  20. var contentArr = [{
  21. Title: "Node.js 微信自定义菜单",
  22. Description: "使用Node.js实现自定义微信菜单",
  23. PicUrl: "http://img.blog.csdn.net/20170605162832842?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
  24. Url: "http://blog.csdn.net/hvkcoder/article/details/72868520"
  25. },
  26. {
  27. Title: "Node.js access_token的获取、存储及更新",
  28. Description: "Node.js access_token的获取、存储及更新",
  29. PicUrl: "http://img.blog.csdn.net/20170528151333883?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
  30. Url: "http://blog.csdn.net/hvkcoder/article/details/72783631"
  31. },
  32. {
  33. Title: "Node.js 接入微信公众平台开发",
  34. Description: "Node.js 接入微信公众平台开发",
  35. PicUrl: "http://img.blog.csdn.net/20170605162832842?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
  36. Url: "http://blog.csdn.net/hvkcoder/article/details/72765279"
  37. }
  38. ];
  39. //回复图文消息
  40. reportMsg = msg.graphicMsg(fromUser, toUser, contentArr);
  41. break;
  42. }
  43. } else {
  44. //判断消息类型为 文本消息
  45. if (result.MsgType.toLowerCase() === "text") {
  46. switch (result.Content) {
  47. case '':
  48. reportMsg = msg.txtMsg(fromUser, toUser, 'Hello ,线上随访公众号开通了,快来使用吧……');
  49. break;
  50. case '':
  51. reportMsg = msg.txtMsg(fromUser, toUser, 'Ha Ha,我还有更多的功能待发掘呢,敬请期待吧……');
  52. break;
  53. case '文章':
  54. var contentArr = [{
  55. Title: "Node.js 微信自定义菜单",
  56. Description: "使用Node.js实现自定义微信菜单",
  57. PicUrl: "http://img.blog.csdn.net/20170605162832842?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
  58. Url: "http://blog.csdn.net/hvkcoder/article/details/72868520"
  59. },
  60. {
  61. Title: "Node.js access_token的获取、存储及更新",
  62. Description: "Node.js access_token的获取、存储及更新",
  63. PicUrl: "http://img.blog.csdn.net/20170528151333883?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
  64. Url: "http://blog.csdn.net/hvkcoder/article/details/72783631"
  65. },
  66. {
  67. Title: "Node.js 接入微信公众平台开发",
  68. Description: "Node.js 接入微信公众平台开发",
  69. PicUrl: "http://img.blog.csdn.net/20170605162832842?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHZrQ29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast",
  70. Url: "http://blog.csdn.net/hvkcoder/article/details/72765279"
  71. }
  72. ];
  73. //回复图文消息
  74. reportMsg = msg.graphicMsg(fromUser, toUser, contentArr);
  75. break;
  76. default:
  77. reportMsg = msg.txtMsg(fromUser, toUser, '没有这个选项哦');
  78. break;
  79. }
  80. // logger.info("on reportMsg", reportMsg);
  81. }
  82. }
  83. //返回给微信服务器
  84. ctx.body = reportMsg
  85. },

回复微信消息

# 主要任务是根据微信消息的类型‘MsgType’,来执行不同的动作

#  事件推送消息(MsgType==‘event’)

* 关注取消事件 subscribe,unsubscribe

  此时可以做数据库接入录入订阅者信息或者更新订阅者信息

* 扫描带参数二维码事件

  用户未关注时:qrscene_ +二维码参数值

  用户已关注时:scan

* 上报地理位置事件 LOCATION

* 自定义菜单事件 CLICK

# 普通消息推送 (MsgType=='text')

  可分为 文本消息、图片消息、语音消息、视频消息、小视频消息、地理位置消息、链接消息

2、有参考的node.js 发送get、post请求的两个方法

  1. /**
  2. * 用于处理 https Get请求方法
  3. * @param {String} url 请求地址
  4. */
  5. this.requestGet = function (url) {
  6. return new Promise(function (resolve, reject) {
  7. https.get(url, function (res) {
  8. var buffer = [],
  9. result = "";
  10. //监听 data 事件
  11. res.on('data', function (data) {
  12. buffer.push(data);
  13. });
  14. //监听 数据传输完成事件
  15. res.on('end', function () {
  16. result = Buffer.concat(buffer).toString('utf-8');
  17. //将最后结果返回
  18. resolve(result);
  19. });
  20. }).on('error', function (err) {
  21. reject(err);
  22. });
  23. });
  24. }

node.js 发送get请求

  1. /**
  2. * 用于处理 https Post请求方法
  3. * @param {String} url 请求地址
  4. * @param {JSON} data 提交的数据
  5. */
  6. this.requestPost = function (url, data) {
  7. return new Promise(function (resolve, reject) {
  8. //解析 url 地址
  9. var urlData = urltil.parse(url);
  10. //设置 https.request options 传入的参数对象
  11. var options = {
  12. //目标主机地址
  13. hostname: urlData.hostname,
  14. //目标地址
  15. path: urlData.path,
  16. //请求方法
  17. method: 'POST',
  18. //头部协议
  19. headers: {
  20. 'Content-Type': 'application/x-www-form-urlencoded',
  21. 'Content-Length': Buffer.byteLength(data, 'utf-8')
  22. }
  23. };
  24. var req = https.request(options, function (res) {
  25. var buffer = [],
  26. result = '';
  27. //用于监听 data 事件 接收数据
  28. res.on('data', function (data) {
  29. buffer.push(data);
  30. });
  31. //用于监听 end 事件 完成数据的接收
  32. res.on('end', function () {
  33. result = Buffer.concat(buffer).toString('utf-8');
  34. resolve(result);
  35. })
  36. })
  37. //监听错误事件
  38. .on('error', function (err) {
  39. console.log(err);
  40. reject(err);
  41. });
  42. //传入数据
  43. req.write(data);
  44. req.end();
  45. });
  46. }
  47. }

node.js 发送post请求

  注:使用前需要引用内置的https中间件

3、关于access_token

  调用微信接口的时候都需要发送微信接口凭证access_token(除微信网页授权中获取的access_token并不是这个access_token),因此请求微信接口的第一步都是要先获取这个access_oken

  1. /**
  2. * 获取微信 access_token
  3. */
  4. WeChat.prototype.getAccessToken = function () {
  5. var that = this;
  6. return new Promise(function (resolve, reject) {
  7. //获取当前时间
  8. var currentTime = new Date().getTime();
  9. //格式化请求地址
  10. var url = util.format(that.apiURL.accessTokenApi, that.apiDomain, that.appID, that.appScrect);
  11. //判断 本地存储的 access_token 是否有效
  12. if (accessTokenJson.access_token === "" || accessTokenJson.expires_time < currentTime) {
  13. that.requestGet(url).then(function (data) {
  14. var result = JSON.parse(data);
  15. if (data.indexOf("errcode") < ) {
  16. accessTokenJson.access_token = result.access_token;
  17. accessTokenJson.expires_time = new Date().getTime() + (parseInt(result.expires_in) - ) * ;
  18. //更新本地存储的
  19. fs.writeFile('./wechat/access_token.json', JSON.stringify(accessTokenJson));
  20. //将获取后的 access_token 返回
  21. resolve(accessTokenJson.access_token);
  22. } else {
  23. //将错误返回
  24. resolve(result);
  25. }
  26. });
  27. } else {
  28. //将本地存储的 access_token 返回
  29. resolve(accessTokenJson.access_token);
  30. }
  31. });
  32. }

获取access_token

  1. 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获取、自定义菜单的更多相关文章

  1. node.js 微信开发1-接入

    准备工作1 域名准备 无论是个人开发还是做公司项目域名都是必不可少的 前期我个人用过花生壳做个开发测试,挺好用的,就是现在要收费了,开通花生壳要收费,开通内网穿透要收费(为啥要内网穿透呢,因为微信接入 ...

  2. node.js 微信开发3-网页授权

    1.配置公众号的自定义菜单,如 { "button":[ { "type":"view", "name":"公 ...

  3. node JS 微信开发

    JS-SDK 要点 微信测试号; 扫码登录;无需认证(只是名称统一为微信测试号)http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/logi ...

  4. 4.Node.js 微信消息管理

    一.写在前面的话   当用户发送消息给公众号时(或某些特定的用户操作引发的事件推送时),会产生一个POST请求,开发者可以在响应包(Get)中返回特定XML结构,来对该消息进行响应.   消息推送也是 ...

  5. vue+node.js+webpack开发微信公众号功能填坑——v -for循环

    页面整体框架实现,实现小功能,循环出数据,整体代码是上一篇 vue+node.js+webpack开发微信公众号功能填坑--组件按需引入 修改部门代码 app.vue <yd-flexbox&g ...

  6. Force.com微信开发系列(四)申请Access Token及自定义菜单之创建菜单

    在微信接口开发中,许多服务的使用都离不开Access Token,Access Token相当于打开这些服务的钥匙,正常情况下会在7200秒内失效,重复获取将导致上次获取的Token失效,本文将首先介 ...

  7. node.js之开发环境搭建

    一.安装linux系统 (已安装linux可跳此步骤) 虚拟机推荐选择:VirtualBox 或者 Vmware (专业版永久激活码:5A02H-AU243-TZJ49-GTC7K-3C61N) 我这 ...

  8. AngularJS + Node.js + MongoDB开发

    AngularJS + Node.js + MongoDB开发的基于位置的通讯录(by vczero) 一.闲扯 有一天班长说了,同学们希望我开发一个可以共享位置的通讯录,于是自己简单设计了下功能.包 ...

  9. Ubuntu 14.04下搭建Node.js的开发环境

    最近想找一个轻量级且支持快速开发的服务开发平台,选来选去选择了Node.js,当时有几种选择: Python + Django(用过Django,虽然开发快速,但是感觉性能并不太好). Ruby + ...

随机推荐

  1. getField和getDeclaredField的区别

    这两个方法都是用于获取字段getField 只能获取public的,包括从父类继承来的字段.getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段. ...

  2. k8s记录-kubeadm安装(一)(转载)

    配置 kubeadm 概述 安装 kubernetes 主要是安装它的各个镜像,而 kubeadm 已经为我们集成好了运行 kubernetes 所需的基本镜像.但由于国内的网络原因,在搭建环境时,无 ...

  3. Xilinx Zynq-7000 嵌入式系统设计与实现

    Xilinx Zynq-7000 嵌入式系统设计与实现 基于ARM Cortex-A9双核处理器和Vivado的设计方法 目录 第1章Zynq-7000 SoC设计导论 1.1全可编程片上系统基础知识 ...

  4. 数据结构与抽象 Java语言描述 第4版 pdf (内含标签)

    数据结构与抽象 Java语言描述 第4版 目录 前言引言组织数据序言设计类P.1封装P.2说明方法P.2.1注释P.2.2前置条件和后置条件P.2.3断言P.3Java接口P.3.1写一个接口P.3. ...

  5. EC11编码器的使用方法

    1. EC11编码器的原理图如下 2. 旋转的时候,波形如下,EC11转1格,产生一个上升沿的中断,思路就是检测AX4-1的上升沿中断(平时是低电平),进入中断服务函数,检测AX4-2的电平,低电平逆 ...

  6. 微信小程序bug集

    bug1:navigator标签无法跳转,控制台不报错,解决方案如图

  7. svn查看登录过的账号密码

    直接下载:http://www.leapbeyond.com/ric/TSvnPD/

  8. jenkins publish .net core application to linux server

    最近学习Docker与Jenkins, 网上大部分都是关于Jenkins+Git+Docker进行持续远程部署, 我一直在考虑为什么Jenkins和Docker要绑定一块使用, 因为我想单独使用Jen ...

  9. MongoDB 空间定位(点) 与 距离检索

    转自: http://blog.csdn.net/flamingsky007/article/details/39208837 基于 MongoDB 2.6 GeoJSON 格式 { "ty ...

  10. Python-18-类的内置属性

    1. __getattr__.set__attr__.__delattr__ class Foo: x=1 def __init__(self,y): self.y=y def __getattr__ ...