美文网首页
通过HTTP API 方式去调用小程序的云开发

通过HTTP API 方式去调用小程序的云开发

作者: 泡杯感冒灵 | 来源:发表于2021-01-20 08:58 被阅读0次
    HTTP API 提供了小程序外访问云开发资源的能力,,使用 HTTP API 开发者可在已有服务器上访问云资源,实现与云开发的互通
    access_token
    • access_token 接口调用凭证,当我们通过HTTP API这种方式调用各种接口的时候,都需要这个参数。
    • 这个凭证,有一定的调用限制,所以,需要对它进行相应的缓存和定时更新。具体的限制如下:


      image.png
    • 还有就是,这个凭证每天最多获取2000次,这也是为什么我们要缓存的原因,如果不缓存,而是每次用户一个操作都去获取一个新的access_token,那么这2000次的请求数量,是根本不够的,所以需要缓存。
    • 请求地址
    GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
    
    • 参数解析


      image.png
    • 返回值


      image.png
    封装一个更新和获取access_token的方法
    const axios = require('axios') 
    const APPID = 'wx13c4ba61e665db3'
    const APPSECRET = 'e7318761870106efad750ad5532ec40'
    const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${APPSECRET}`
    const fs = require('fs')
    const path = require('path')
    // 存储access_token的json文件的路径
    const fileName = path.resolve(__dirname, './access_token.json')
    
    // 更新access_token的方法(第一次是获取并保存)
    const updateAccessToken = async () => {
      const resStr = await axios.get(url)
      const access_token = resStr.data.access_token
      // 如果请求到了access_token
      if (access_token) {
        // 就写文件
        fs.writeFileSync(fileName, JSON.stringify({
          access_token,
          createTime:new Date()  // 存access_token的时候,配合createTime以方便计算过期时间
        }))
      } else {
        // 在access_token没有获取到的情况下,再次调用updateAccessToken去获取access_token
        await updateAccessToken()
      }
    }
    
    // 每两个小时更新一下access_token
    // 一般情况下,不是等到2个小时才更新,而是提前五分钟
    setInterval(async () => { 
      await updateAccessToken()
    },(7200 - 300) * 1000)
    
    // 取access_token的方法
    const getAccessToken = async () => {
      // 假如是第一次调用,access_token还没有生成,那么这个函数会报错,因为找不到access_token.json文件
      // 所以要放入 try catch进行异常捕获,在捕获到异常的时候,去更新一下access_token
      // 更新完成以后,再去读取access_token,所以需要再次调用一下getAccessToken
      try {
        //读取文件
        const readRes = fs.readFileSync(fileName, 'utf8')
        const readObj = JSON.parse(readRes)
        // 在我们获取access_token的时候,虽然每2个小时会更新一下access_token,但是因为更新的代码时放在服务器上的,假如服务器宕机了,
        // 超过了2个小时,那么当我们再去调用getAccessToken的时候,获取的就是过期的access_token,这明细是不行的。
        // 所以,我们在获取access_token的方法里,加入判断逻辑,判断JSON文件里存的access_token的createTime和当前调用获取方法的时间的间隔
        // 如果间隔超过2个小时,就更新access_token并重新获取
        const createTime = new Date(readObj.createTime).getTime()
        const nowTime = new Date().getTime()
        if ((nowTime - createTime)/1000/60/60 >= 2) {
          await updateAccessToken()
          await getAccessToken()
        }
        return readObj.access_token
      } catch (error) {
        await updateAccessToken()
        await getAccessToken()
      }
    }
    
    module.exports = getAccessToken
    
    通过HTTP API触发云函数
    • 触发云函数请求的地址以及参数
    POST https://api.weixin.qq.com/tcb/invokecloudfunction?access_token=ACCESS_TOKEN&env=ENV&name=FUNCTION_NAME
    
    • 参数解析:


      image.png
    • 封装触发云函数的方法

    // 把后端调用小程序云函数的方法,封装为一个独立的文件
    
    const getAccessToken = require('./getAccessToken')
    const axios = require('axios')
    
    const callCloudFn = async (ctx, fnName, params) => {
      const ACCESS_TOKEN = await getAccessToken()
      const options = {
        method: 'post',
        url: `https://api.weixin.qq.com/tcb/invokecloudfunction?access_token=${ACCESS_TOKEN}&env=${ctx.state.env}&name=${fnName}`,
        data: {
          ...params
        }
      }
    
      return await axios(options)
        .then((res) => {
          // console.log(res)
          return res.data
        })
        .catch((err) => {
          console.error(err)
        })
    }
    
    module.exports = callCloudFn
    
    • 然后在需要调用云函数的地方,再调用刚才封装的方法,传入三个参数即可
    const Router = require('koa-router')
    const router = new Router()
    const callCloudFn = require('../utils/callCloudFn')
    const callCloudDB = require('../utils/callCloudDB')
    
    
    router.get('/list', async (ctx, next) => {
      // 查询歌单列表
      const query = ctx.request.query
      const res = await callCloudFn(ctx, 'music', {
        // 这3个是云函数本身需要的参数
        $url: 'playlist',
        start: parseInt(query.start),
        count: parseInt(query.count)
      })
      let data = []
      if (res.resp_data) {
        data = JSON.parse(res.resp_data).data
      }
    
      ctx.body = {
        data,
        code:20000
      }
    })
    
    module.exports = router
    
    • 实际开发中,如果我们只是单纯的想要读取一些数据,那么直接调用云数据库就行了,不是非要先调用云函数再通过云函数调用云数据库。当我们需要通过后台管理系统给用户推送模板消息,或者想要生成小程序码这样的需求,这种情况下,通过调用云函数去实现是比较合适的。
    通过HTTP API 操作数据库(增删改查)
    • 查询数据库记录
    POST https://api.weixin.qq.com/tcb/databasequery?access_token=ACCESS_TOKEN
    
    • 参数解析


      image.png
    • 请求参数示例
    {
      "env":"test2-4a89da",
      "query": "db.collection(\"geo\").where({done:true}).limit(10).skip(1).get()"
    }
    
    • query是一个数据库查询语句,应使用limit()限制单次拉取的数量,默认10条
    • 返回数据示例
    {
        "errcode": 0,
        "errmsg": "ok",
        "pager": {
            "Offset": 1,
            "Limit": 10,
            "Total": 2
        },
        "data": [
            "{\"_id\":\"b15498af-1a5a-40b4-a4e7-b3fc4a1df482\",\"done\":true,\"name\":\"test\"}"
        ]
    }
    
    • 封装一个调用云数据库的方法
    // 把后端调用小程序云函数的方法,封装为一个独立的文件
    
    const getAccessToken = require('./getAccessToken')
    const axios = require('axios')
    
    // 这里的fnName参数,对应的是 HTTP API 请求查询云数据库请求地址里的tab后边的字段,分别是
    // databasequery,databaseupdate,databasedelete,databaseadd
    const callCloudFn = async (ctx, fnName, params) => {
      const ACCESS_TOKEN = await getAccessToken()
      const options = {
        method: 'post',
        url: `https://api.weixin.qq.com/tcb/invokecloudfunction?access_token=${ACCESS_TOKEN}&env=${ctx.state.env}&name=${fnName}`,
        data: {
          ...params
        }
      }
    
      return await axios(options)
        .then((res) => {
          // console.log(res)
          return res.data
        })
        .catch((err) => {
          console.error(err)
        })
    }
    
    module.exports = callCloudFn
    
    • 然后再调用这个方法
    router.get('/getById', async (ctx, next) => {
      const params = ctx.request.query
      const query = `db.collection('playlist').doc('${params.id}').get()`
      const res = await callCloudDB(ctx, 'databasequery', query)
      ctx.body = {
        code: 20000,
        data:JSON.parse(res.data)
      }
    })
    
    HTTP API 调用云存储
    • 常用的就是上传图片和文件了,我们以上传图片为例。首先要做的,就是读取云数据库里的轮播图显示在前端页面上。但是直接读取数据库返回的结果里只有fileId这个字段,这个字段只能在小程序端的image显示,不能用于前端网页展示。所以我们需要通过获取文件下载链接这个方法,拿到download_url字段才可以展示在页面上

    • 获取文件上传链接

    POST https://api.weixin.qq.com/tcb/uploadfile?access_token=ACCESS_TOKEN
    
    • 参数


      image.png
    • 获取文件下载链接,我们展示在前端的图片链接,就是这个方法放回结果中的download_url字段
    POST https://api.weixin.qq.com/tcb/batchdownloadfile?access_token=ACCESS_TOKEN
    
    • 参数


      image.png
    • 封装一个调用云存储的方法

    const getAccessToken = require('./getAccessToken')
    const axios = require('axios')
    const rp = require('request-promise')
    const fs = require('fs')
    
    const cloudStorage = {
      async download(ctx, fileList) {
        const ACCESS_TOKEN = await getAccessToken()
        const options = {
          method: 'post',
          url: `https://api.weixin.qq.com/tcb/batchdownloadfile?access_token=${ACCESS_TOKEN}`,
          data: {
            env: ctx.state.env,
            file_list:fileList
          }
        }
    
        return await axios(options).then((res) => {
          return res
        }).catch((err) => {
          console.error(err)
        })
      },
    
      async upload(ctx) {
        // 1.请求地址
        const ACCESS_TOKEN = await getAccessToken()
        const file = ctx.request.files.file
        const path = `swiper/${Date.now()}-${Math.random()}-${file.name}`
        const options = {
          method: 'post',
          url: `https://api.weixin.qq.com/tcb/uploadfile?access_token=${ACCESS_TOKEN}`,
          data: {
            path,
            env: ctx.state.env
          }
        }
    
        const info =  await axios(options).then((res) => {
          return res.data
        }).catch((err) => {
          console.error(err)
        })
    
    
        // 2. 上传图片
        const params = {
          method: 'post',
          headers: {
            'content-type':'multipart/form-data'
          },
          uri: info.url,
          formData: {
            key: path,
            Signature:info.authorization,
            'x-cos-security-token': info.token,
            'x-cos-meta-fileid': info.cos_file_id,
            file:fs.createReadStream(file.path)
          },
          json:true
        }
        await rp(params)
        return info.file_id
      },
    
      async delete(ctx, fileid_list) {
        const ACCESS_TOKEN = await getAccessToken()
        const options = {
          method: 'post',
          url: `https://api.weixin.qq.com/tcb/batchdeletefile?access_token=${ACCESS_TOKEN}`,
          body: {
            fileid_list,
            env: ctx.state.env
          },
          json:true
        }
        return await rp(options).then((res) => {
          return res
        }).catch((err) => {
          console.error(err)
        })
      }
    }
    
    module.exports = cloudStorage
    
    当删除图片的时候,跟我们删除歌单列表是不一样的,歌单列表只把云数据库里的记录删除就可以了,而图片不单单要删除云数据库里的数据,而且还要删除云存储里的数据,也就是分两个步骤

    相关文章

      网友评论

          本文标题:通过HTTP API 方式去调用小程序的云开发

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