微信公众号项目-day01思路整理

目录

整体思维导图

详细步骤

1、申请公众号,完成微信公众平台接口测试申请。

2、填写服务器配置

3、验证服务器的有效性

3、模块化

4、获取access_token

5、获取用户消息

6、处理用户发送过来的信息

7、通过解析用户发送来的消息,进行相应的回复


 

整体思维导图

详细步骤

1、申请公众号,完成微信公众平台接口测试申请。

2、填写服务器配置

在本地搭建好服务器,并利用ngrok输入指令ngrok http 3000(相应的端口号)生成对应的IP,将本地生成的服务器端口号生成对应的外网跨域访问的网址。在服务器中配置相应的信息。 

  • 配置信息的代码实现

    const config = {
        appID:"wx6077e5089a73e15d",
        appsecret:"4408047fc4a1337366a94bdaf63cee57",
        token:"lemonclass0412"
    }
    

    说明:appID 是第三方用户唯一凭证

    appsecret 是 第三方用户唯一凭证密钥

3、验证服务器的有效性

  1. 首先将参数签名加密的三个参数timestamp、nonce、token按照字典序进行排序
  2. 将排序后的参数拼接成一个字符串,进行sha1加密
  3. 将加密后的字符串与signature进行对比

    如果匹配,说明验证成功的,返回echostr给微信服务器. 如果不匹配,说明验证失败的,返回''给微信服务器

  • 代码实现

     const {signature,echostr,timestamp,nonce} =req.query;
    //1、将timestamp、nonce、token排序、加密
     const sha1Str = sha1([timestamp,nonce,token].sort().join(''))
        //2、匹配,返回echostr,不匹配,返回''
          if(sha1Str === signature)res.send(echostr)
          else res.send("");
    

3、模块化

1、暴露模块--->module.exports

2、暴露的模块,要有返回值,利用return

3、异步的函数,要结合async...await使用

4、获取access_token

access_token特点:

accesstoken是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用accesstoken。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。accesstoken的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的accesstoken失效。

获取方法:

  1、上来判断本地有没有access_token (readAccessToken)
        如果有
          判断access_token有无过期,(isValidAccessToken)
            如果没有过期,直接使用
            如果过期了,再次请求获取access_token(getAccessToken),保存下来(saveAccessToken)
        如果没有
          发送请求获取access_token(getAccessToken),保存下来(至少1小时55分钟)(saveAccessToken)

2、官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183

思路实现

将四个方法定义成一个类Wechat,然后通过构造实例对象,调用Wechat的方法。

    const wechatAPI = new Wechat;

代码实现

  • getAccessToken函数
    • 注意: 在node服务器中发送请求,不能利用ajax,所以我们借助两个包---request、request-promise-native来完成。
      • npm install --save request
      • npm install --save request-promise-native
      • 引用 const rq = require("request-promise-native"); (注意下载两个,但是只引用一个)
      • 说明:rq的返回值是一个promise对象,可以调用.then,catch方法。
    • 由于方法是异步的,我们借助new Promise((resolve, reject) => {}来获取返回值

        getAccessToken() {
            //https请求方式: GET
            //https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

              const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appID}&secret=${appsecret}`;

              return new Promise((resolve, reject) => {
                // rq的本质是一个promise对象
                rq({method: "GET", url, json: true})
                  .then(res => {
                    //相应的数据
                    console.log(res);
                    //设置凭据的过期时间
                    res.expires_in = Date.now() + (res.expires_in - 300) * 1000;
                    resolve(res);
                  })
                  .catch(err =>reject("getAccessToken方法出了错误:"+err));
              })
            }
  • saveAccessToken函数
    • 说明:利用文件的简单写入来保存获取到的数据 const {writeFile,readFile} = require("fs");

        saveAccessToken(data){
        return new Promise((resolve, reject) => {
          //微信会返回下述JSON数据包给公众号,要转一下js格式
          data = JSON.stringify(data);
          //为了方便读取,将获取到的数据保存到accessToken.txt文件中
          writeFile("accessToken.txt",data,err=>{
            if(!err){
              resolve(data)
            }else{
              reject("saveAccessToken方法出错了"+err)
            }
          })
        })
        }
  • readAccessToken函数

    • 说明:利用文件的简单读取来读取相应的数据 const {readFile} = require("fs");
    • fs.readFile语法:

      fs.readFile('accessToken.txt', (err, data) => { if (!err) console.log(data); else throw err; });


 readAccessToken(){
      return new Promise((resolve, reject) => {
        readFile("accessToken.txt",(err,data)=>{
          if (!err){
            //返回时将读取到的数据再转化为json数据
            resolve(JSON.parse(data.toString()))
          } else{
            reject("readAccessToken方法出错了"+err)
          }
        })
      })

    }
  • isValidAccessToken函数

    isValidAccessToken(data){
      //优化:如果没有以上的属性,直接返回,不用再走下面逻辑
        if(!data || !data.access_token || !data.expires_in) return false;
        //如果没有过期,返回true,过期了,返回false
        return Date.now()< data.expires_in;
    }
    

5、获取用户消息

  • fetchAccessToken函数:获取accessToken,定义再wechat类中

    fetchAccessToken(){
      //性能优化
      if(this.access_token && this.expires_in && this.isValidAccessToken(data)){
        //如果没有过期,且有这几个属性,直接返回出去,不用重新获取
        return Promise.resolve({access_token:this.access_token,expires_in:this.expires_in});
      }
     return this.readAccessToken()
        .then(async res=>{  //如果有
          if(this.isValidAccessToken(res)){
            //没有过期
            return Promise.resolve(res)
          }else{  //过期了
            const data = await this.getAccessToken();
            await this.saveAccessToken(data);
            return Promise.resolve(data)
          }
        })
        .catch(async ()=>{  //如果 没有
          const data  = this.getAccessToken();
          await this.saveAccessToken(data);
          return Promise.resolve(data)
        })
    
        /*以上返回的结果为一个promise对象,但是返回的结果都是成功的回调。这样还可以利用.then方法,
        这一步调用.then方法是为了优化----将两个属性挂载再this的这个类上,外界就可以直接进行判断,见上面 性能优化 部分*/
    
        .then(res=>{
          //将access_token和expires_in挂载到this上
          this.access_token = res.access_token;
          this.expires_in = res.expires_in;
          //给函数一个整体的返回值
          return Promise.resolve(res);
        })
    }
    
  • 调用

    (async ()=>{
      const wechatAPI = new Wechat;
      const data = await wechatAPI.fetchAccessToken();
    })();
    

6、处理用户发送过来的信息

说明

1、微信会发送两个消息:

    GET:验证服务器的有消息(我们在服务器验证模块中已经验证过了)
    POST:接受用户发送过来的消息(以下代码处理POST请求):
                else if(req.method === "POST"){
                    console.log(req.query);}

2、req.query返回出来的数据是一个xml格式的:




1532582999


6582393859576522806

3、所以要将其转化为一个js格式的对象。转化步骤:

  • 先利用xml2js将 xml ---带有xml属性的对象

    • xml2js转化语法:

      var parseString = require('xml2js').parseString;
      var xml = "Hello xml2js!"
      parseString(xml, function (err, result) {
          console.dir(result);
      });
      
    • 引入:const {parseString} = require("xml2js");
  • 将xml属性的对象遍历,转化为js一般对象

4、定义三个专门用来转化的方法:

  • getuserDataAsyc() //获取用户发送来的信息
  • parserXMLDataAsyc() //xml--->带有xml属性的对象
  • formatData() //带有xml属性的对象--->js一般对象的形式

图示: 

代码实现

1、getuserDataAsyc()

      getuserDataAsyc(req) {
        return new Promise((resolve, reject) => {
          let userData = '';
          req
            //传送数据,不断累加
            .on('data', data => {
              userData += data;
            })
            //数据传送完毕后,将结果返回出去
            .on('end', () => {
              resolve(userData);
            })
        })
      }

说明:此时userData的返回值形式为:xml格式,见上。

2、 parserXMLDataAsyc()

前提:引入const {parseString} = require("xml2js")

  parserXMLDataAsyc(xmlData) {
    return new Promise((resolve, reject) => {
      parseString(xmlData, {trim: true}, function (err, result) {
        if (!err) {
          resolve(result);
        } else {
          reject("parserXMLData方法出错了" + err);
        }
      });
    })
  }

说明:此时result返回的数据格式:

{ xml:
       { ToUserName: [ 'gh_406d257c2cc2' ],
         FromUserName: [ 'owOA61F8J8xzZv1EpmUcsAcalXPI' ],
         CreateTime: [ '1532519920' ],
         MsgType: [ 'text' ],
         Content: [ '2222' ],
         MsgId: [ '6582122937334454364' ] } }

3、formatData()

  formatData(newData) {
    const jsData = newData.xml;
    for (let item in jsData) {
      let value = jsData[item];
      //防止不是数组获取非法数据
      if(Array.isArray(value) && value.length)
      jsData[item] = value[0];
    }
    return jsData;
  }

说明:jsData数据格式:

{ ToUserName: 'gh_406d257c2cc2',
  FromUserName: 'owOA61F8J8xzZv1EpmUcsAcalXPI',
  CreateTime: '1532583006',
  MsgType: 'text',
  Content: '2',
  MsgId: '6582393889641293880' }

7、通过解析用户发送来的消息,进行相应的回复

说明:

1、一旦遇到以下情况,微信都会在公众号会话中,

  向用户下发系统提示“该公众号暂时无法提供服务,请稍后再试”:
  1、开发者在5秒内未回复任何内容
  2、开发者回复了异常数据,比如JSON数据等

2、通过if判断MsgType类型,利用Content进行内容回复

    const replyMessage = '' +
        ' ' +
        ' ' +
        ''+Date.now()+' ' +
        '' +
        '  ' +
        '';
  //返回相应给服务器
  res.send(replyMessage);

你可能感兴趣的:(微信公众号项目实战)