HTTP API 提供了小程序外
访问云开发资源
的能力,,使用 HTTP API 开发者可在已有服务器上访问云资源,实现与云开发的互通
access_token
-
access_token
接口调用凭证,当我们通过HTTP API这种方式调用各种接口的时候,都需要这个参数。 -
这个凭证,有一定的调用限制,所以,需要对它进行相应的缓存和定时更新。具体的限制如下:
- 还有就是,这个凭证每天最多获取
2000次
,这也是为什么我们要缓存的原因,如果不缓存,而是每次用户一个操作都去获取一个新的access_token,那么这2000次的请求数量,是根本不够的,所以需要缓存。 - 请求地址
GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
-
参数解析
-
返回值
封装一个更新和获取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
-
参数解析:
封装触发云函数的方法
// 把后端调用小程序云函数的方法,封装为一个独立的文件
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
-
参数解析
- 请求参数示例
{
"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
-
参数
- 获取文件下载链接,
我们展示在前端的图片链接,就是这个方法放回结果中的download_url字段
POST https://api.weixin.qq.com/tcb/batchdownloadfile?access_token=ACCESS_TOKEN
-
参数
封装一个调用云存储的方法
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