美文网首页基础前端
微信公众号获取 acess_token 和 jsapi-tick

微信公众号获取 acess_token 和 jsapi-tick

作者: CondorHero | 来源:发表于2019-12-29 23:12 被阅读0次
一、获取 acess_token

access_token 的介绍:

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

access 的特点:

特点:
1. 唯一的
2. 有效期为2小时,因网络延迟需要,提前5分钟结束
3. 接口权限 每天 2000次,所以的保存为文件

文件目录结构:

├─ config/              # 配置目录
│   ├─ index.js         # 存储配置信息
├── wechat/          # 核心功能库
│   ├─ accessToken.txt  # 存储access_token
│   ├─ auth.js          # 验证服务器功能
│   ├─ wechat.js        # 类wechat
├── index.js            # 入口启动文件
├── package.json        # 配置文件

开发前需要安装的依赖:

npm install --save request request-promise-native

config.js 文件夹里面的 index.js 文件内容为:

//配置对象模块
module.exports = {
  token: 'xxxxxxx',
  appID: 'xxxxxxxxxxxxxxxxxxxx',
  appsecret: 'xxxxxxxxxxxxxxxxx',
  url: 'http:www.baidu.com'
}

index.js 入口文件内容:

const express = require('express');
const auth = require('./wechat/auth');
const app = express();

//接受微信服务器发送过来的请求 GET
//应用中间件,能够接受处理所有请求
app.use(auth());

app.listen(3000, err => {
  if (!err) console.log('服务器的3000端口号成功启动!');
});

wechat 文件夹里面的 auth.js:

/*
  验证服务器的有效性:
  
    1、填写服务器配置(测试号管理页面)
      - URL 开发者服务器地址(保证能在互联网中能访问)
        通过 ngrok http 端口号 或netapp就得到一个网址
      - Token  参与微信签名的加密
    2、验证服务器地址的有效性
      - 将timestamp、nonce、token三个参数按照字典序排序
      - 将三个参数拼接在一起,进行sha1加密
      - 将加密后生成字符串和微信签名进行对比,
            如果相同说明成功,返回一个echostr给微信服务器,
            如果不相同,说明签名算法出了问题,配置不成功
 */
//引入配置对象
const config = require('../config');
//引入sha1加密模块
const sha1 = require('sha1');

module.exports = () => {
  
  return (req, res, next) => {
    //接受微信服务器发送过来的请求参数
    console.log(req.query);
    /*
      { signature: 'c4409bdd012bf28d8b4aabf7ac5847c5560d6cf0',   微信的加密签名(timestamp、nonce、token)
        echostr: '11283286178012191741',  随机字符串
        timestamp: '1529977721',          时间戳
        nonce: '1462949582' }             随机数字
     */
    //获取参与加密的参数
    const {signature, echostr, timestamp, nonce} = req.query;
    const {token} = config;
    /*// - 将timestamp、nonce、token三个参数按照字典序排序
    const arr = [timestamp, nonce, token].sort();
    // - 将三个参数拼接在一起,进行sha1加密
    const str = arr.join('');
    const sha1Str = sha1(str);*/
    //简写方式
    const sha1Str = sha1([timestamp, nonce, token].sort().join(''));
    // - 将加密后生成字符串和微信签名进行对比,
    if (sha1Str === signature) {
      //说明成功,返回echostr给微信服务器
      res.send(echostr);
    } else {
      //说明失败
      res.send('');
    }
  }
}

上面都是对第一次验证服务器有效性的代码优化,下面的才是重点。

关于如何获取 access_token 讲解:
wechat.js 文件的设计思路:

设计思路:
  首先发送请求获取凭据,保存为一个唯一的文件
  然后后面每次请求先去本地文件读取凭据
    判断凭据是否过期
      如果没有过期,直接使用
      如果过期了,重新发送请求获取凭据,保存下来覆盖之前的文件

上面的思路变成代码的走两条路线,我们完全可以只走一条路线就完成上面的功能。

先去本地查看有没有指定文件(readAccessToken)
如果有(之前请求过凭据)
  判断凭据是否过期(isValidAccessToken)
    如果没有过期,直接使用
    如果过期了,重新发送请求获取凭据,保存下来覆盖之前的文件(getAccessToken、saveAccessToken)
如果没有(之前都没有请求过凭据) 发送请求获取凭据getAccessToken,保存为一个唯一的文件saveAccessToken

获取access_token的请求地址:

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

根据上面的思路这个 wechat.js 应该是一个类。

不过你先弄明白下面这个函数输出多少?第一个 then 里面 return 的值是第二个then里面的 res。你直接 return 2;也行不过 Promise.resolve() 可以确保 main 可以链式调用。

function main(){
  return new Promise((resolve,reject)=>{
    resolve(1);
  })
}
main().then(res=>{
  console.log(res);
  return Promise.resolve(2)
}).then(res=>{
  console.log(res);
});

Wxchat.js 初版

//引入配置对象
const {appID, appsecret} = require('../config');
//引入发送http请求的库
const rp = require('request-promise-native');
//引入fs模块
const {readFile, writeFile} = require('fs');

class Wechat {
  
  getAccessToken () {
    //定义请求地址
    const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appID}&secret=${appsecret}`;
    /*
      问题:需要将回调函数中的数据返回出去?
      解决:用promise解决
      
      所有的异步操作,都应该包装一层promise,让这个异步操作执行完毕之后,再去执行后面的代码
      简化: 所有的异步操作,都应该包装一层promise
     */
    return new Promise((resolve, reject) => {
      //发送http请求
      //下载 request-promise-native  request
      rp({method: 'GET', json: true, url})
        .then(res => {
          //请求成功的状态
          // console.log(res);
          //重新赋值凭据的过期时间 : 当前时间 + (7200 - 5分钟) * 1000
          res.expires_in = Date.now() + (res.expires_in - 300) * 1000;
          // console.log(res);
          resolve(res);
        })
        .catch(err => {
          //请求失败
          reject('getAccessToken方法出了问题:' + err);
        })
    })
  }
  saveAccessToken (data) {
    /*
      问题:writeFile方法会将对象转化为字符串
      解决:我将对象转化为json字符串
     */
    data = JSON.stringify(data);
    return new Promise((resolve, reject) => {
      //将凭据保存为一个文件
      writeFile('accessToken.txt', data, err => {
        if (!err) {
          //写入成功
          resolve();
        } else {
          //写入失败
          reject('saveAccessToken方法出了问题:' + err);
        }
      })
    })
  }
  readAccessToken () {
    return new Promise((resolve, reject) => {
      //将凭据读取出来
      readFile('accessToken.txt', (err, data) => {
        if (!err) {
          //将读取的Buffer数据转化为json字符串
          data = data.toString();
          //将json字符串转化为对象
          data = JSON.parse(data);
          //读取成功
          resolve(data);
        } else {
          //读取失败
          reject('readAccessToken方法出了问题:' + err);
        }
      })
    })
  }
  isValidAccessToken (data) {
    /*
      判断凭据是否过期
        true   凭据没有过期
        false  凭据过期了
     */
    //过滤非法的数据
    if (!data || !data.access_token || !data.expires_in) return false;
    //判断凭据是否过期
    /*if (data.expires_in > Date.now()) {
      //如果凭据的过期时间大于当前时间,说明没有过期
      return true
    } else {
      //如果凭据的过期时间小于当前时间,说明过期了
      return false
    }*/
    //简写方式
    return data.expires_in > Date.now();
  }
};

如果使用上面这个 class :

const wx = new Wechat();
const fetchAccessToken = ()=>{
  return new Promise((resolve,reject)=>{
    wx.readAccessToken().then(res=>{
      // 本地有文件
      //判断是否过期
      if(wx.isValidAccessToken(res)){
        //没有过期直接使用
        resolve(res);
      }else{
        //过期重新请求
        wx.getAccessToken().then(res=>{
          // 读取文件成功
          wx.saveAccessToken(res).then(()=>{
            resolve(res);
          });
        })      
      }
    }).catch(err=>{
      //没有文件
      wx.getAccessToken().then(res=>{
        // 读取文件成功
        wx.saveAccessToken(res).then(()=>{
          resolve(res);
        });
      })
    });
  });
}

fetchAccessToken().then(res=>{
  console.log(res);
})
//catch可以捕获 reject 和 throw new Error 里面的值。

fetchAccessToken 调用这个函数就能获取有效的Access_token,而不用去管其内部
逻辑,这时的 fetchAccessToken 完全可以提到类里面去,wx 实例也就可以换成 this 表示这个实例了。同时使用 async 和 await 解决链式回调黑洞。
优化的 2.0 版本:

//引入配置对象
const {appID, appsecret} = require('../config');
//引入发送http请求的库
const rp = require('request-promise-native');
//引入fs模块
const {readFile, writeFile} = require('fs');

class Wechat {
  
  getAccessToken () {
    //定义请求地址
    const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appID}&secret=${appsecret}`;
    /*
      问题:需要将回调函数中的数据返回出去?
      解决:用promise解决
      
      所有的异步操作,都应该包装一层promise,让这个异步操作执行完毕之后,再去执行后面的代码
      简化: 所有的异步操作,都应该包装一层promise
     */
    return new Promise((resolve, reject) => {
      //发送http请求
      //下载 request-promise-native  request
      rp({method: 'GET', json: true, url})
        .then(res => {
          //请求成功的状态
          // console.log(res);
          //重新赋值凭据的过期时间 : 当前时间 + (7200 - 5分钟) * 1000
          res.expires_in = Date.now() + (res.expires_in - 300) * 1000;
          // console.log(res);
          resolve(res);
        })
        .catch(err => {
          //请求失败
          reject('getAccessToken方法出了问题:' + err);
        })
    })
  }
  saveAccessToken (data) {
    /*
      问题:writeFile方法会将对象转化为字符串
      解决:我将对象转化为json字符串
     */
    data = JSON.stringify(data);
    return new Promise((resolve, reject) => {
      //将凭据保存为一个文件
      writeFile('accessToken.txt', data, err => {
        if (!err) {
          //写入成功
          resolve();
        } else {
          //写入失败
          reject('saveAccessToken方法出了问题:' + err);
        }
      })
    })
  }
  readAccessToken () {
    return new Promise((resolve, reject) => {
      //将凭据读取出来
      readFile('accessToken.txt', (err, data) => {
        if (!err) {
          //将读取的Buffer数据转化为json字符串
          data = data.toString();
          //将json字符串转化为对象
          data = JSON.parse(data);
          //读取成功
          resolve(data);
        } else {
          //读取失败
          reject('readAccessToken方法出了问题:' + err);
        }
      })
    })
  }
  isValidAccessToken (data) {
    /*
      判断凭据是否过期
        true   凭据没有过期
        false  凭据过期了
     */
    //过滤非法的数据
    if (!data || !data.access_token || !data.expires_in) return false;
    //判断凭据是否过期
    /*if (data.expires_in > Date.now()) {
      //如果凭据的过期时间大于当前时间,说明没有过期
      return true
    } else {
      //如果凭据的过期时间小于当前时间,说明过期了
      return false
    }*/
    //简写方式
    return data.expires_in > Date.now();
  }
  fetchAccessToken () {
    //优化操作,优化不去执行读取文件操作
    if (this.access_token && this.expires_in && this.isValidAccessToken(this)) {
      //说明this有凭据和过期时间,并且凭据未过期
      return Promise.resolve({access_token: this.access_token, expires_in: this.expires_in});
    }
    
    return this.readAccessToken()
      .then(async res => {
        //判断凭据是否过期(isValidAccessToken)
        if (this.isValidAccessToken(res)) {
          //没有过期,直接使用
          return Promise.resolve(res);
        } else {
          //重新发送请求获取凭据
          const data = await this.getAccessToken();
          //保存下来
          await this.saveAccessToken(data);
          //将请求回来的凭据返回出去
          return Promise.resolve(data);
        }
      })
      .catch(async err => {
        console.log(err);
        //重新发送请求获取凭据
        const data = await this.getAccessToken();
        //保存下来
        await this.saveAccessToken(data);
        //将请求回来的凭据返回出去
        return Promise.resolve(data);
      })
      .then(res => {
        //将其请求回来的凭据和过期时间挂载到this上,防止多次回调时,减小响应时间
        this.access_token = res.access_token;
        this.expires_in = res.expires_in;
        //指定fetchAccessToken方法返回值
        return Promise.resolve(res);
      })
  }
};
const wx = new Wechat();
wx.fetchAccessToken().then(res=>{
  console.log(res);
});

改写的思路就是把 1.0 版本的代码复制到 class 类里面去,把实例改成 this,同时使用 async 和 await 来解决链式回调黑洞,在通过 Promise 函数 then,返回会被下一个打点的 then 接收优化,最后把最外面的 new Promise 去掉直接变成同步函数,最后把返回的值绑定到这个类的属性上,这样在 fetchAccessToken 函数内部一开始就先检测此类是否被实例化,不用走流程去读文件了。

二、获取JS-SDK

微信JS-SDK ( JavaScript Software Development Kit )是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包。

通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验。

简单来说,使用JS-SDK我们可以引入外部网页。

6.1 绑定域名

根据接口域名来更改JS接口安全域名:


绑定域名

需要注意的是 JS 接口安全域名你修改成什么都不会报错,但是只有一种方式是能使用的,就是相对 URL 里面的地址去掉 http 前缀填入才是对的。

6.2 JS-SDK使用权限签名算法

jsapi_ticket 了解:

生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。

jsapi_ticket 和 access_token 特别像,不同的是只有获取到 access_token 才能去拿到 jsapi_ticket 这个票据。既然这两个东西那么的像我们就把他们封装在一起,都放在 WeChat 这个类里面去。

拿到 jsapi_ticket 的请求链接和请求方式:

请求地址:
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
请求方法:
GET

首先在项目目录在新建一个 util 文件夹,里面新建一个 api.js 用来管理接口,这时候的文件目录结构是这样的:

├─ config/              # 配置目录
│   ├─ index.js         # 存储配置信息
│── wechat/          # 核心功能库
│   ├─ accessToken.txt  # 存储access_token
│   ├─ auth.js          # 验证服务器功能
│   ├─ wechat.js        # 类wechat
│── utils/
│── │── api.js            # api 管理
│── index.js            # 入口启动文件
│── package.json        # 配置文件

api.js 文件内容为:

/*
  所有api接口
 */

//地址前缀
const prefix = 'https://api.weixin.qq.com/cgi-bin/';

module.exports = {
    accessToken: `${prefix}token?grant_type=client_credential`,
    ticket: `${prefix}ticket/getticket?type=jsapi`
}

这时候的 WeChat.js 文件内容为,只展示获取 jsapi-ticket:

/**
 * 用来获取jsapi_ticket
 */
getTicket() {

    //发送请求
    return new Promise(async (resolve, reject) => {
        //获取access_token
        const data = await this.fetchAccessToken();
        //定义请求的地址
        const url = `${api.ticket}&access_token=${data.access_token}`;

        rp({ method: 'GET', url, json: true })
            .then(res => {
                // console.log(res);打印格式如下
                // {
                //     "errcode":0,
                //     "errmsg":"ok",
                //     "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
                //     "expires_in":7200
                // }
                //我们只需要两个字段就够用的了,同时改写过期时间
                //将promise对象状态改成成功的状态
                resolve({
                    ticket: res.ticket,
                    expires_in: Date.now() + (res.expires_in - 300) * 1000
                });
            })
            .catch(err => {
                console.log(err);
                //将promise对象状态改成失败的状态
                reject('getTicket方法出了问题:' + err);
            })
    })
}

/**
 * 用来保存jsapi_ticket
 * @param ticket 要保存的票据
 */
saveTicket(ticket) {
    ticket = JSON.stringify(ticket);
    return new Promise((resolve, reject) => {
        //将票据保存为一个文件
        writeFile('ticket.txt', ticket, err => {
            if (!err) {
                //写入成功
                resolve();
            } else {
                //写入失败
                reject('saveTicket方法出了问题:' + err);
            }
        })
    });
}

/**
 * 用来读取ticket
 */
readTicket() {
    return new Promise((resolve, reject) => {
        //将票据读取出来
        readFile("ticket.txt", (err, data) => {
            if (!err) {
                //将读取的Buffer数据转化为json字符串
                data = data.toString();
                //将json字符串转化为对象
                data = JSON.parse(data);
                //读取成功
                resolve(data);
            } else {
                //读取失败
                reject('readTicket方法出了问题:' + err);
            }
        })
    })
}

/**
 * 用来检测ticket是否有效的
 * @param data
 */
isValidTicket(data) {
    //检测传入的参数是否是有效的
    if (!data && !data.ticket && !data.expires_in) {
        //代表ticket无效的
        return false;
    }

    return data.expires_in > Date.now();
}

/**
 * 用来获取没有过期的ticket
 * @return {Promise<any>} ticket
 */
fetchTicket() {
    //优化
    if (this.ticket && this.ticket_expires_in && this.isValidTicket(this)) {
        //说明之前保存过ticket,并且它是有效的, 直接使用
        return Promise.resolve({
            ticket: this.ticket,
            expires_in: this.ticket_expires_in
        })
    }

    return this.readTicket()
        .then(async res => {
            //本地有文件
            //判断它是否过期
            if (this.isValidTicket(res)) {
                //有效的
                return Promise.resolve(res);
            } else {
                //过期了
                const res = await this.getTicket();
                await this.saveTicket(res);
                return Promise.resolve(res);
            }
        })
        .catch(async err => {
            //本地没有文件
            const res = await this.getTicket();
            await this.saveTicket(res);
            return Promise.resolve(res);
        })
        .then(res => {
            //将ticket挂载到this上
            this.ticket = res.ticket;
            this.ticket_expires_in = res.expires_in;
            //返回res包装了一层promise对象(此对象为成功的状态)
            return Promise.resolve(res);
        })
}

想想写 acess_token 和 ticket 逻辑的时候,你会发现保存和读取两个函数可以提取出来。
在 utils 里面新建一个工具函数 tool.js

//引入fs模块
const { writeFile, readFile } = require('fs');
//引入path模块
const { resolve } = require('path');

module.exports = {
    writeFileAsync(data, fileName) {
        //将对象转化json字符串
        data = JSON.stringify(data);
        const filePath = resolve(__dirname, fileName);
        return new Promise((resolve, reject) => {
            writeFile(filePath, data, err => {
                if (!err) {
                    console.log('文件保存成功~');
                    resolve();
                } else {
                    reject('writeFileAsync方法出了问题:' + err);
                }
            })
        })
    },
    readFileAsync(fileName) {
        const filePath = resolve(__dirname, fileName);
        return new Promise((resolve, reject) => {
            readFile(filePath, (err, data) => {
                if (!err) {
                    console.log('文件读取成功~');
                    //将json字符串转化js对象
                    data = JSON.parse(data);
                    resolve(data);
                } else {
                    reject('readFileAsync方法出了问题:' + err);
                }
            })
        })
    }
}

这里引入 path 模块,是为了保证生成的文件使用绝对路径永远在同一个地方。经过改装过的 WeChat.js 文件为:

//引入配置对象
const { appID, appsecret } = require('../config');
//引入发送http请求的库
const rp = require('request-promise-native');
//引入api模块
const api = require('../utils/api');
//引入工具函数
const { writeFileAsync, readFileAsync } = require('../utils/tool');

class Wechat {

    getAccessToken() {
        //定义请求地址
        const url = `${api.accessToken}&appid=${appID}&secret=${appsecret}`;
        /*
          问题:需要将回调函数中的数据返回出去?
          解决:用promise解决
          
          所有的异步操作,都应该包装一层promise,让这个异步操作执行完毕之后,再去执行后面的代码
          简化: 所有的异步操作,都应该包装一层promise
         */
        return new Promise((resolve, reject) => {
            //发送http请求
            //下载 request-promise-native  request
            rp({ method: 'GET', json: true, url })
                .then(res => {
                    //请求成功的状态
                    // console.log(res);
                    //重新赋值凭据的过期时间 : 当前时间 + (7200 - 5分钟) * 1000
                    res.expires_in = Date.now() + (res.expires_in - 300) * 1000;
                    // console.log(res);
                    resolve(res);
                })
                .catch(err => {
                    //请求失败
                    reject('getAccessToken方法出了问题:' + err);
                })
        })
    }
    saveAccessToken(accessToken) {
        return writeFileAsync(accessToken, 'access_token.txt');
    }
    readAccessToken() {
        return readFileAsync('access_token.txt');
    }
    isValidAccessToken(data) {
        /*
          判断凭据是否过期
            true   凭据没有过期
            false  凭据过期了
         */
        //过滤非法的数据
        if (!data || !data.access_token || !data.expires_in) return false;
        //判断凭据是否过期
        /*if (data.expires_in > Date.now()) {
          //如果凭据的过期时间大于当前时间,说明没有过期
          return true
        } else {
          //如果凭据的过期时间小于当前时间,说明过期了
          return false
        }*/
        //简写方式
        return data.expires_in > Date.now();
    }
    fetchAccessToken() {
        //优化操作,优化不去执行读取文件操作
        if (this.access_token && this.expires_in && this.isValidAccessToken(this)) {
            //说明this有凭据和过期时间,并且凭据未过期
            return Promise.resolve({ access_token: this.access_token, expires_in: this.expires_in });
        }

        return this.readAccessToken()
            .then(async res => {
                //判断凭据是否过期(isValidAccessToken)
                if (this.isValidAccessToken(res)) {
                    //没有过期,直接使用
                    return Promise.resolve(res);
                } else {
                    //重新发送请求获取凭据
                    const data = await this.getAccessToken();
                    //保存下来
                    await this.saveAccessToken(data);
                    //将请求回来的凭据返回出去
                    return Promise.resolve(data);
                }
            })
            .catch(async err => {
                console.log(err);
                //重新发送请求获取凭据
                const data = await this.getAccessToken();
                //保存下来
                await this.saveAccessToken(data);
                //将请求回来的凭据返回出去
                return Promise.resolve(data);
            })
            .then(res => {
                //将其请求回来的凭据和过期时间挂载到this上
                this.access_token = res.access_token;
                this.expires_in = res.expires_in;
                //指定fetchAccessToken方法返回值
                return Promise.resolve(res);
            })
    }
    /**
     * 用来获取jsapi_ticket
     */
    getTicket() {

        //发送请求
        return new Promise(async (resolve, reject) => {
            //获取access_token
            const data = await this.fetchAccessToken();
            //定义请求的地址
            const url = `${api.ticket}&access_token=${data.access_token}`;

            rp({ method: 'GET', url, json: true })
                .then(res => {
                    // console.log(res);打印格式如下
                    // {
                    //     "errcode":0,
                    //     "errmsg":"ok",
                    //     "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
                    //     "expires_in":7200
                    // }
                    //我们只需要两个字段就够用的了,同时改写过期时间
                    //将promise对象状态改成成功的状态
                    resolve({
                        ticket: res.ticket,
                        expires_in: Date.now() + (res.expires_in - 300) * 1000
                    });
                })
                .catch(err => {
                    console.log(err);
                    //将promise对象状态改成失败的状态
                    reject('getTicket方法出了问题:' + err);
                })
        })
    }

    /**
     * 用来保存jsapi_ticket
     * @param ticket 要保存的票据
     */
    saveTicket(ticket) {
        return writeFileAsync(ticket, 'ticket.txt');
    }

    /**
     * 用来读取ticket
     */
    readTicket() {
        return readFileAsync('ticket.txt');
    }

    /**
     * 用来检测ticket是否有效的
     * @param data
     */
    isValidTicket(data) {
        //检测传入的参数是否是有效的
        if (!data && !data.ticket && !data.expires_in) {
            //代表ticket无效的
            return false;
        }

        return data.expires_in > Date.now();
    }

    /**
     * 用来获取没有过期的ticket
     * @return {Promise<any>} ticket
     */
    fetchTicket() {
        //优化
        if (this.ticket && this.ticket_expires_in && this.isValidTicket(this)) {
            //说明之前保存过ticket,并且它是有效的, 直接使用
            return Promise.resolve({
                ticket: this.ticket,
                expires_in: this.ticket_expires_in
            })
        }

        return this.readTicket()
            .then(async res => {
                //本地有文件
                //判断它是否过期
                if (this.isValidTicket(res)) {
                    //有效的
                    return Promise.resolve(res);
                } else {
                    //过期了
                    const res = await this.getTicket();
                    await this.saveTicket(res);
                    return Promise.resolve(res);
                }
            })
            .catch(async err => {
                //本地没有文件
                const res = await this.getTicket();
                await this.saveTicket(res);
                return Promise.resolve(res);
            })
            .then(res => {
                //将ticket挂载到this上
                this.ticket = res.ticket;
                this.ticket_expires_in = res.expires_in;
                //返回res包装了一层promise对象(此对象为成功的状态)
                return Promise.resolve(res);
            })
    }
};
//先实例化
//const wx = new Wechat();
//测试获取access_token
wx.fetchAccessToken().then(res=>console.log(res));
//测试获取ticket
wx.fetchTicket().then(res=>console.log(res));
//暴露出去
module.exports = Wechat;

至此,需要调取微信 API 的 access_token 和使用 JS-SDK 时的 ticket 票据都已经封装并准备好了。下面就可以进项业务开发了。

相关文章

网友评论

    本文标题:微信公众号获取 acess_token 和 jsapi-tick

    本文链接:https://www.haomeiwen.com/subject/zeoyoctx.html