微信公众号开发教程

本教程为看微信公众号视频做的笔记,原视频链接:尚硅谷公众号开发,微信公众号开发实战_哔哩哔哩_bilibili

平台

微信公众号管理:公众号 (qq.com)

微信公众测试号平台:微信公众平台 (qq.com)

微信公众号开发文档:微信公众平台开发概述 | 微信开放文档 (qq.com)

目录说明:

|_ config 存放配置文件

|_ index.js 配置文件,存放了需要用到的内容

|_ utils 工具

|_ tool.js 存放写的工具类

|_ wechat 存放微信接口

|_ auth.js 微信服务器的有效性验证

|_ accessToken.js 获取微信 access_token(调用微信接口必须携带的参数,是全局的唯一凭证,2小时失效)

|_ app.js 程序入口

接口配置信息

进入微信公众平台 (qq.com),看到如下页面

首先配置接口(接口配置信息),用 js 使用 express 框架编写如下代码:

// 引入express
const express = require('express');
const app = express();
​
app.use((req, res, next) => {
  console.log(111);
  res.send(111);
});
app.listen(3000, () => {
  console.log('Server is running at http://localhost:3000');
});

也就是创建一个运行在 3000 端口的服务器,这里我们 localhost:3000 这个端口并不能用于微信这个配置接口。

URL

打开 ngrok.exe ,目标路径D:\A-study\software\cpolar.exe,进行内网穿透,将本地端口号开启的服务映射外网跨域访问一个网址,输入如下命令:

// 此处 3000 为服务器所在端口号
cpolar http 3000

页面如下:

 

此时访问:http://7ed2c829.r3.cpolar.top即可实现和 localhost:3000 一样的效果。

这个地址可用于微信这个配置接口。复制此路径,粘贴到配置接口中。

ngrok 这个程序不要关闭,关闭此接口无法响应。

Token

这里的 Token 是一个微信签名的一个参数,在这里可以随意填写,越长越好。

配置完如下

 

验证消息来源

这里我们之所以使用 app.use(),原因是可以接受处理所有消息,配置好上两项,点击提交,在app.use()中查看req.query,得到一下结果:

{
    // 微信的加密签名
    signature: '0393d4e16dc0cf2c907c6d7d9c6bfdfce4c2efce',
    // 微信的随机字符串
    echostr: '4359501947458813682',
    // 微信的发送请求时间戳
    timestamp: '1661430480',
    // 微信的随机数字
    nonce: '1215363190'
}

我们的目的是要验证消息是否是来自微信服务器的,原理:

计算得出 signature 微信加密签名,和微信传递过来的 signature 进行对比,如果二者相同,则说明消息来自于微信服务器,反之,不是微信服务器发送的消息。

那么怎么进行计算呢,如下是步骤:

  1. 将参与微信加密签名的三个参数(传来的:timestamp、nonce、自己设置的:token),组合到一起,按照字典排序,并组合在一起形成一个数组

  2. 将数组拼接所有项拼接成一个字符串,进行 sha1 加密

  3. 加密完成就生成了一个 signatrue ,和微信发送过来的进行对比

    如果一样,说明消息来自于微信服务器,返回 echostr 给微信服务器

    如果不一样,说明不是微信发送的消息,返回 error

设置一个 config 变量用于储存我们所有需要用到的值:

const config = {
  token: "VikeyAlvinForever",
  appID: "wx56e0df28b365f4d2",
  appsevret: "5b7d5c835c65e984ecb21e0c63864103"
}

安装 sha1 :cnpm i sha1

验证代码如下:

// 引入express
const express = require('express');
const app = express();
// 引入 sha1 模块将计算出的结果加密
const sha1 = require('sha1');
// 配置对象,存我们需要的内容
const config = {
  token: 'VikeyAlvinForever',
  appID: 'wx56e0df28b365f4d2',
  appsevret: '5b7d5c835c65e984ecb21e0c63864103',
};
​
app.use((req, res, next) => {
  console.log(req.query);
  // 解构赋值
  const { signature, echostr, timestamp, nonce } = req.query;
  const { token } = config;
  console.log('1232' + echostr);
  //将参与微信加密签名的三个参数(传来的:timestamp、nonce、自己设置的:token),组合到一起,按照字典排序,并组合在一起形成一个数组
  const arr = [timestamp, nonce, token];
  // 排序
  const arrSort = arr.sort();
  console.log('arrSort:' + arrSort);
  // 将数组所有参数拼接
  const str = arr.join('');
  console.log('str:' + str);
  // 加密
  const sha1Str = sha1(str);
  console.log('加密后:' + sha1Str);
  // 判断是否相同
  if (sha1Str === signature) {
    res.json(echostr);
  } else {
    res.end('error');
    console.log('error');
  }
});
app.listen(3000, () => {
  console.log('Server is running at http://localhost:3000');
});

然后在微信公众平台上点击提交,这里我遇到了一个问题,各种代码都没有问题,然而一直配置失败,是那个 ngrok 不行,换成 cpolar 就好使,气死我了。

如图即为成功。

cploar官网:cpolar - secure introspectable tunnels to localhost

获取access_token

access_token 是微信调用接口的唯一凭证,访问微信的接口必须都携带此凭证。

特点:

  1. 唯一的

  2. 有效期为2小时,最好留出时间来请求,我们一般提前5分钟请求

  3. 接口权限 每天最多 2000 次

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

请求方式:GET

携带参数:

参数是否必须说明
grant_type获取access_token填写client_credential
appid第三方用户唯一凭证
secret第三方用户唯一凭证密钥,即appsecret

设计思路:

  1. 首次本地没有,发送请求获取 access_token,保存下来(本地文件)

  2. 第二次及以后:

  • 先去本地读取文件,判断他是否过期

    • 过期了 -> 重新请求获取 access_token ,保存下来覆盖之前的文件(保证文件唯一)

    • 没有过期 -> 直接使用

整体思路:

读取本地文件(readAccessToken)

  • 本地有文件

    • 判断是否过期(isValidAccessToken)

    • 过期 -> 重新请求获取 access_token(getAccessToken) ,保存下来覆盖之前的文件(保证文件唯一)(saveAccessToken)

    • 没过期 -> 直接使用

  • 本地没有文件

    • 发送请求获取 access_token(getAccessToken),保存下来(本地文件)(saveAccessToken),直接使用

自动回复

接收用户发来的消息

微信服务器会发送两种类型的消息给开发者服务器:

  1. GET 请求 -> 验证服务器的有效性

  2. POST 请求 -> 微信服务器会将用户发送的数据以POST请求的方式转发到开发者服务器上

再 auth.js 中验证微信服务器发送的是 GET请求 还是 POST请求 获取。

使用手机像测试号发送消息,得到如下

{
  signature: 'bd1ffd2a36fdc44ca5b6b0d45ac3980b78489346',
  timestamp: '1661772245',
  nonce: '1886903686',
  openid: 'o1cZB6SJhrmbTWVCfo6l_rzwrkgY'
}

这个 openid 为用户的微信id。

需要接收请求体中的数据,这是一个流式数据。

请求后获取数据:

<xml>
<ToUserName><![CDATA[gh_5cc210c9b162]]></ToUserName>   // 开发者id
<FromUserName><![CDATA[o1cZB6SJhrmbTWVCfo6l_rzwrkgY]]></FromUserName>   // 用户的openid
<CreateTime>1661773339</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
<MsgId>23790873740096595</MsgId>
</xml>

将xml数据解析为 js 对象

这里我们需要使用一个库:xml2js,使用 xml2js 所提供的 parseString 方法来转换:

parseString(要转换的xml数据,{trim:是否留白},(err,data)=>{})

上文转换成如下:

{
  xml: {
    ToUserName: [ 'gh_5cc210c9b162' ],
    FromUserName: [ 'o1cZB6SJhrmbTWVCfo6l_rzwrkgY' ],
    CreateTime: [ '1661774019' ],
    MsgType: [ 'text' ],
    Content: [ '哈喽' ],
    MsgId: [ '23790883295887531' ]
  }
}

回复用户发来的消息

判断用户发送的消息:

全匹配: ===

半匹配:message.Content.match('爱')

const content = '您在说什么,我听不懂'
if(message.MsgType === 'text'){
    // 全匹配 用户必须发送指定消息才匹配到
    if(message.Content === "1"){
        content = "大吉大利,今晚吃鸡"
        // 半匹配 只要用户发送消息包含 爱 就会匹配
    }else if(message.Content.match("爱")){
        content = '我爱你~'
    }
}
// 这里ju
let reply = `<xml>
      <ToUserName><![CDATA[${message.FromUserName}]]></ToUserName>
      <FromUserName><![CDATA[${message.ToUserName}]]></FromUserName>
      <CreateTime>${Date.now()}</CreateTime>
      <MsgType><![CDATA[text]]></MsgType>
      <Content><![CDATA[${content}]]></Content>
      </xml>`;
res.send(reply);

详细请看文档,这里不多赘述。

自定义菜单创建接口

// 自定义菜单
module.exports = {
  button: [
    {
      type: 'click',
      name: '戳我啊~',
      key: 'CLICK',
    },
    {
      name: '菜单',
      sub_button: [
        {
          type: 'view',
          name: '跳转链接',
          url: 'http://www.baidu.com/',
        },
        // {
        //   // 小程序
        //   type: 'miniprogram',
        //   name: 'wxa',
        //   url: 'http://mp.weixin.qq.com',
        //   appid: 'wx286b93c14bbf93aa',
        //   pagepath: 'pages/lunar/index',
        // },
        {
          type: 'click',
          name: '赞一下我们',
          key: 'V1001_GOOD',
        },
        {
          type: 'scancode_waitmsg',
          name: '扫码带提示',
          key: '扫码带提示',
        },
        {
          type: 'scancode_push',
          name: '扫码推事件',
          key: '扫码推事件',
        },
        // {
        //   type: 'media_id',
        //   name: '点击按钮发送图片',
        //   media_id: 'MEDIA_ID1',
        // },
        // {
        //   type: 'view_limited',
        //   name: '图文消息',
        //   media_id: 'MEDIA_ID2',
        // },
      ],
    },
    {
      name: '发图',
      sub_button: [
        {
          type: 'pic_sysphoto',
          // 弹出相机
          name: '系统拍照发图',
          key: '系统拍照发图',
        },
        {
          type: 'pic_photo_or_album',
          // 相册或者拍照
          name: '拍照或者相册发图',
          key: '拍照或者相册发图',
        },
        {
          type: 'pic_weixin',
          // 打开相册
          name: '微信相册发图',
          key: '微信相册发图',
        },
        {
          name: '发送位置',
          type: 'location_select',
          key: 'rselfmenu_2_0',
        },
      ],
    },
  ],
};
​

开发页面

首先安装 ejs,这个ejs,相当于 html 页面,直接可以在 node 中指定跳转

在 app.js 中配置 ejs,配置完才能使用:

// 配置模板资源目录
app.set('views','./views')
// 配置模板引擎
app.set('view engine','ejs')

配置完模板资源以及模板引擎,在day01下创建 views 目录,在此文件夹下创建 search.ejs 文件,编写代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>这是一个搜索页面</h1>
  </body>
</html>

在 app.js 中编写代码:

app.get('/search', (req, res) => {
  // 渲染页面,把渲染好的页面传给用户
  res.render('search');
});

这样当访问search 这个路径时,将会渲染出此页面

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值