经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » JavaScript » 查看文章
使用NodeJs作为微信公众号后台服务器 使用node回复公众号消息 node验证公众号tonken node微信公众号开发 - Y-X南川
来源:cnblogs  作者:Y-X南川  时间:2021/5/24 10:52:30  对本文有异议

使用NodeJs作为微信公众号后台服务器

下面演示代码的源码地址 https://gitee.com/szxio/weChatServer

申请测试公众号

首先登录微信公众平台,选择自己的公众号登录。登录成功后点击开发者工具,选择公众平台测试账号

点击进去后我们可以申请一个测试用的公众号,可以体验所有高级接口,这里我们要配置一个线上的接口地址,在验证 Tonken,和收发消息时微信都会请求我们配置的地址,这里推荐一个好用的内网穿透工具,可以把我们本地的项目地址映射到外网上,方便我们调试

小米球内网穿透工具

这里我生成的线上地址是 http://songzx.ngrok2.xiaomiqiu.cn/,下面我们会用这个地址作为我们的公众号的接口配置地址

image-20210518191538886

实现Tonken验证

首先新建一个空白的 node 项目

  1. npm init -y

接着安装一些常用的依赖

  1. npm install express

接在在项目根路径下新建 index.js,初始代码如下

  1. const express = require("express")
  2. const app = express()
  3. app.get("/",(req,res)=>{
  4. res.send('Hello World')
  5. })
  6. app.listen(8088,()=>{
  7. console.log("running 127.0.0.1:8088");
  8. })

然后启动项目并用浏览器访问 127.0.0.1:8088可以看到如下结果,表示服务启动成功

现在我们实现验证 tonken 的逻辑

首先安装如下依赖,用作加密处理

  1. npm install crypto

然后新建 utilrouter 两个文件夹,分别放置我们的统一的方法和普通请求方法

然后新建 util -> validateToken.js 文件,代码如下,这个方法专门用来验证微信传递过来的 Tonken

  1. var crypto = require("crypto");
  2. // 加密方法
  3. function sha1(str) {
  4. var md5sum = crypto.createHash("sha1");
  5. md5sum.update(str);
  6. str = md5sum.digest("hex");
  7. return str;
  8. }
  9. // 验证tonken
  10. function validateToken(req) {
  11. return new Promise((resolve, reject) => {
  12. let query = req.query;
  13. let signature = query.signature;
  14. let echostr = query["echostr"];
  15. let timestamp = query["timestamp"];
  16. let nonce = query["nonce"];
  17. let oriArray = new Array();
  18. oriArray[0] = nonce;
  19. oriArray[1] = timestamp;
  20. oriArray[2] = "admin123"; // 这里是在公众号接口配置信息里面填写的Token
  21. oriArray.sort();
  22. let original = oriArray.join("");
  23. let scyptoString = sha1(original);
  24. if (signature == scyptoString) {
  25. // 验证通过,返回 echostr
  26. resolve(echostr);
  27. } else {
  28. reject(false);
  29. }
  30. });
  31. }
  32. // 导出验证 Tonken 的发放
  33. module.exports = validateToken;

然后新建 router -> weChat.js 文件,这个文件专门用来处理微信发送过来的请求,在这个文件中编写如下代码

  1. const express = require("express");
  2. const router = express.Router(); // 配置路由模块
  3. const validateToken = require("../util/validateToken");
  4. // get请求验证tonken有效性
  5. router.get("/", (req, res) => {
  6. validateToken(req).then((t) => {
  7. res.send(t);
  8. });
  9. });
  10. // 导出 router
  11. module.exports = router;

最后修改一下index.js文件,引入我们新建的 router.js 文件

  1. const express = require("express");
  2. const app = express();
  3. const path = require("path");
  4. const weChat = require(path.resolve(__dirname, "./router/weChat"));
  5. app.use(weChat);
  6. app.listen(8088, () => {
  7. console.log("running 127.0.0.1:8088");
  8. });

现在我们去微信公众号配置页面中测试一下

image-20210519162630934

页面中弹出 配置成功 就表示我们验证 Tonken 的业务已经完成了

获取Tonken并定时刷新

微信中获取 Tonken 要发送一个 get 请求来获取,并且这个 Tonken 有过期时间,我们需要自己保存这个 Tonken 并定时刷新,以保证 Tonken 有效性

微信官方对于获取 Tonken 的描述

接口调用说明

  • 请求方式: GET
  • 请求地址:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

既然要用到请求,我们安装一个 axios 用来发送请求

  1. npm install axios

然后在根目录新建 public -> tonken.json ,用来存放我们获取到的 tonken,也是对 tonken 的一种持久化存储方式,json文件内容为空即可

接着新建 util -> tonkenConfig.js 文件,代码如下

  1. const fs = require("fs");
  2. const path = require("path");
  3. const http = require("axios");
  4. const fileUrl = path.resolve(__dirname, "../public/tonken.json");
  5. const APPID = "wx2188729b190d357d"; // 测试号的 APPID
  6. const APPSECRET = "d976b0e6262b829ba003e9a24032447c"; // 测试号的 APPSECRET
  7. let INTERTIME = (7200 - 60) * 1000; // 设置一个默认的定期获取tonken的时间
  8. // 保存Tonken
  9. function setTonken() {
  10. return new Promise((resolve, reject) => {
  11. http
  12. .get(
  13. `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${APPSECRET}`
  14. )
  15. .then((res) => {
  16. // 更新tonken的过期时间,每隔这个时间重新获取一次tonken
  17. INTERTIME = (res.data.expires_in - 60) * 1000;
  18. // 获取到Tonken后保存到json文件中
  19. fs.writeFile(
  20. fileUrl,
  21. JSON.stringify({
  22. tonken: res.data.access_token,
  23. }),
  24. () => {
  25. // 通知外界Tonken获取成功
  26. resolve();
  27. }
  28. );
  29. });
  30. });
  31. }
  32. // 定时获取Tonken
  33. function timingSetTonken() {
  34. // 定时刷新tonken
  35. setInterval(() => {
  36. setTonken();
  37. }, INTERTIME);
  38. }
  39. // 获取Tonken
  40. function getTonken() {
  41. return new Promise((resolve, reject) => {
  42. // 从json中读取保存的Tonken
  43. fs.readFile(fileUrl, (err, data) => {
  44. // 返回获取到的tonken
  45. resolve(JSON.parse(data).tonken);
  46. });
  47. });
  48. }
  49. // 导出封装好的方法
  50. module.exports = {
  51. setTonken, // 更新tonken
  52. getTonken, // 返回获取到的tonken
  53. timingSetTonken, // 定时更新tonken
  54. };

然后在 router -> weChat.js 中引入 tonkenConfig.js

  1. const express = require("express");
  2. const router = express.Router(); // 配置路由模块
  3. const validateToken = require("../util/validateToken");
  4. const { setTonken, timingSetTonken } = require("../util/tonkenConfig");
  5. // 项目启动后自动执行获取tonken的方法
  6. setTonken().then(() => {
  7. // tonken 获取成功后开始定时刷新tonken操作
  8. timingSetTonken();
  9. });
  10. // get请求验证tonken有效性
  11. router.get("/", (req, res) => {
  12. validateToken(req).then((t) => {
  13. res.send(t);
  14. });
  15. });
  16. // 导出 router
  17. module.exports = router;

此时我们在启动项目后会自动调用一下获取 tonken 的接口,然后从接口中获取到一个过期时间,微信返回的过期时间是以秒为单位,减去60秒是为了下一次tonken时与这次tonken之间的平滑过渡,之后每隔这个时间会重新获取一次tonken

我们将这个tonken写入到了一个json文件中,我们可以在任何文件中通过如下方法获取tonken

  1. const { getTonken } = require("./util/tonkenConfig");
  2. // 调用封装好的获取token方法
  3. getTonken().then((tonken) => {
  4. console.log(tonken); // 45_7k55HHRaYxM4MkD4aREraHZpgdjmT......
  5. });

接收微信消息并回复

官方对于接收消息的描述

简单说就是:我们在微信公众号中发送消息后,微信会发送一个 post 请求给我们上面配置的地址,参数时一段 xml 文本,我们需要解析这个 xml,并按照微信指定的格式回复一个 xml 格式的字符串,注意是回复 xml 格式的字符串

首先安装依赖,用来解析post请求中的xml参数

  1. npm install express-xml-bodyparser

然后在 index.js 文件中引用并配置中间件

  1. const express = require("express");
  2. const app = express();
  3. const path = require("path");
  4. const weChat = require(path.resolve(__dirname, "./router/weChat"));
  5. const xmlparser = require('express-xml-bodyparser'); // 解析 xml
  6. app.use(express.json());
  7. app.use(express.urlencoded());
  8. app.use(xmlparser());
  9. app.use(weChat);
  10. app.listen(8088, () => {
  11. console.log("running 127.0.0.1:8088");
  12. });

然后在 weChat.js 中添加一个 post 请求,打印一下看看微信给我们发过来的是什么东西

  1. // post请求处理微信发送过来的消息
  2. router.post("/", (req, res) => {
  3. console.log(req.body);
  4. res.send("");
  5. });

重启项目,我们往微信公众号中随便发送一个消息

解析后的参数如下

  1. {
  2. xml: {
  3. tousername: [ 'gh_a0f004c20d2b' ],
  4. fromusername: [ 'olttN6WJOYe-lTysV8_tsnZ7-HMQ' ],
  5. createtime: [ '1621416487' ],
  6. msgtype: [ 'text' ],
  7. content: [ 'hello' ],
  8. msgid: [ '23213103466653274' ]
  9. }
  10. }

拿到参数后我们可以根据参数中的 msgtype 判断传递过来的消息类型,以及 content 是消息内容,获取到了参数,接下要做的就是根据消息回复内容了

官方被动回复用户消息文档

下面是一个回复消息的模板代码,可以很方便的帮助我们生成指定的 xml 格式的字符串

  1. // 回复文本消息
  2. exports.textMessage = function (message) {
  3. var createTime = new Date().getTime();
  4. return `<xml>
  5. <ToUserName><![CDATA[${message.FromUserName}]]></ToUserName>
  6. <FromUserName><![CDATA[${message.ToUserName}]]></FromUserName>
  7. <CreateTime>${createTime}</CreateTime>
  8. <MsgType><![CDATA[text]]></MsgType>
  9. <Content><![CDATA[${message.reply}]]></Content>
  10. </xml>`;
  11. };
  12. // 回复图片消息
  13. exports.imageMessage = function (message) {
  14. var createTime = new Date().getTime();
  15. return `<xml>
  16. <ToUserName><![CDATA[${message.FromUserName}]]></ToUserName>
  17. <FromUserName><![CDATA[${message.ToUserName}]]></FromUserName>
  18. <CreateTime>${createTime}</CreateTime>
  19. <MsgType><![CDATA[image]]></MsgType>
  20. <Image>
  21. <MediaId><![CDATA[${message.mediaId}]]></MediaId>
  22. </Image>
  23. </xml>`;
  24. };
  25. // 回复语音消息
  26. exports.voiceMessage = function (message) {
  27. var createTime = new Date().getTime();
  28. return `<xml>
  29. <ToUserName><![CDATA[${message.FromUserName}]]></ToUserName>
  30. <FromUserName><![CDATA[${message.ToUserName}]]></FromUserName>
  31. <CreateTime>${createTime}</CreateTime>
  32. <MsgType><![CDATA[voice]]></MsgType>
  33. <Voice>
  34. <MediaId><![CDATA[${message.mediaId}]]></MediaId>
  35. </Voice>
  36. </xml>`;
  37. };
  38. // 回复视频消息
  39. exports.videoMessage = function (message) {
  40. var createTime = new Date().getTime();
  41. return `<xml>
  42. <ToUserName><![CDATA[${message.FromUserName}]]></ToUserName>
  43. <FromUserName><![CDATA[${message.ToUserName}]]></FromUserName>
  44. <CreateTime>${createTime}</CreateTime>
  45. <MsgType><![CDATA[video]]></MsgType>
  46. <Video>
  47. <MediaId><![CDATA[${message.mediaId}]]></MediaId>
  48. <Title><![CDATA[${message.title}]]></Title>
  49. <Description><![CDATA[${message.description}]]></Description>
  50. </Video>
  51. </xml>`;
  52. };
  53. // 回复图文消息
  54. exports.articleMessage = function (message) {
  55. var createTime = new Date().getTime();
  56. return `<xml>
  57. <ToUserName><![CDATA[${message.FromUserName}]]></ToUserName>
  58. <FromUserName><![CDATA[${message.ToUserName}]]></FromUserName>
  59. <CreateTime>${createTime}</CreateTime>
  60. <MsgType><![CDATA[news]]></MsgType>
  61. <ArticleCount>${message.articles.length}</ArticleCount>
  62. <Articles>
  63. ${message.articles
  64. .map(
  65. (article) =>
  66. `<item><Title><![CDATA[${article.title}]]></Title>
  67. <Description><![CDATA[${article.description}]]></Description>
  68. <PicUrl><![CDATA[${article.img}]]></PicUrl>
  69. <Url><![CDATA[${article.url}]]></Url></item>`
  70. )
  71. .join("")}
  72. </Articles>
  73. </xml>`;
  74. };

weChat.js 中引入上面的模板,这里我把模板代码放到了 util -> template.js 中,然后修改刚刚新建的 post 方法

  1. // 引入消息模板
  2. const template = require("../util/template");
  3. // post请求处理微信发送过来的消息
  4. router.post("/", (req, res) => {
  5. let xml = req.body.xml;
  6. let msgtype = xml.msgtype[0];
  7. switch (msgtype) {
  8. case "text":
  9. // 封装要回复的消息参数
  10. let message = {
  11. FromUserName: xml.fromusername[0],
  12. ToUserName: xml.tousername[0],
  13. reply: "你好呀,我是通过代码回复你的",
  14. };
  15. res.send(template.textMessage(message));
  16. break;
  17. default:
  18. res.send(""); // 不是文本消息是默认响应一个空
  19. break;
  20. }
  21. });

我们现在在发送消息试一试

我们看到公众号已经可以回答我们了。

原文链接:http://www.cnblogs.com/songzxblog/p/14786318.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号