概述:小程序云开发提供给开发者可以无需搭建服务器,即可使用云端能力来开发微信小程序、小游戏。云开发为开发者提供完整的原生云端支持和微信服务支持,弱化后端和运维概念,无需搭建服务器,使用平台提供的 API 进行核心业务开发,即可实现快速上线和迭代,可以说这个能力可以极大的提高一个前端开发的单兵作战能力。因为个人爱好,闲暇时间也用云开发去开发过几个小程序,作为一个简单系统包括了后台管理小程序和用户使用的小程序,那么问题来了,数据库和云函数只能对应在一个小程序,而且存储在云端文件系统里的图片的访问也只能在当前的小程序中访问,对于另一个小程序怎么去访问数据、访问图片呢?
一、从小程序B访问小程序A的云函数
这里主要利用了云开发提供的通过http接口访问云函数的能力,文档链接
1、开通小程序A的云开发权限 & 新建集合
由于文章篇幅以及内容重点原因,创建小程序、开通云开发权限我们先略过了,我们首先创建一个集合”users“(数据库),设置它的权限为“所有用户可读,仅创建者可写”,这里是为了将来可以通过云函数进行数据的插入,权限链接。
2、新建小程序A的云函数
这个步骤相对简单,在这里我们假设创建了一个"users_add"的一个添加用户的云函数,作用是用于添加用户。主要逻辑是首先判断用户是否已经存在,若存在则返回“已存在”,若不存在则添加用户并返回“成功”。代码完成之后云函数上传并部署。直接上代码吧~
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
const _users = db.collection('users')
// 云函数入口函数
exports.main = async (event, context) => {
const user = await _users.where({ openid: event.openid }).get().catch((err) => err);
if (!user || user.errMsg != 'collection.get:ok' || user.data.length != 0) {
return {
errcode: -1001,
errmsg: "新增失败"
}
}else if (user.data.length != 0) {
return {
errcode: -2001,
errmsg: "用户已存在"
}
}
const res = await _users.add({
data: {
name: event.name,
age: event.age,
sex: event.sex,
tel: event.tel,
openid: event.openid,
createtime: new Date().getTime(),
}
})
if (res && res._id != '') {
return {
errcode: 0,
errmsg: 'success',
data: {
res: res
}
}
}else{
return {
errcode: -9999,
errmsg: "新增失败",
data: {
res: res
}
}
}
}
3、小程序B调用小程序A的云函数思路
还是一样创建小程序、开通云开发权限我们先略过了。通过官方文档的说明我们知道,通过http的方式可以访问我们的云函数,官方给予我们的示例是这个样子的:POST https://api.weixin.qq.com/tcb/invokecloudfunction?access_token=ACCESS_TOKEN&env=ENV&name=FUNCTION_NAME,那么我们需要的获取的参数有:access_token(接口调用凭证,获取的文档可以点击此处) 、env(云开发环境ID)、name(云函数名称),这里的access_token比较烦,需要先获取,时效为2小时,官方文档建议我们有个中控服务统一获取和刷新。另外,我们每次调用A小程序的云函数都直接在小程序B里去发这个请求的话,每次都要重复获取token和填写env、name参数,这不符合我们的设计模式。那么还是老办法,我们创建一个云函数,每次小程序B里需要调小程序A的云函数时,先经过中间层即B自己的工具云函数,将方法名称和参数传给该工具云函数,由其完成token的获取、填写env、调小程序A的云函数,将返回的数据返回给小程序。
4、开通小程序B的云开发权限 & 新建集合 & 新建工具云函数
基于此思路我们首先创建一个集合用来记录调用接口凭证的集合"token",并设置其权限;然后新建一个云函数"api",代码如下~
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
const _token = db.collection('token')
const _ = db.command;
var rp = require('request-promise')
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
// 获取数据库集合中的“token”
const token = await _token.get().catch((err) => err);
var access_token = '';
// token不存在、token过期的情况下,重新获取token
if (!token || token.errMsg != 'collection.get:ok' || token.data.length == 0 || token.data[0].expires_in < new Date().getTime()) {
//删除token里保存的数据
await _token.where({
expires_in: _.gt(0)
}).remove()
// 获取access_token,真实情况下请替换自己的appid和secret
var res = await rp("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wxb1c2c7******0759&secret=9840548c1348*****aa91bc74ae94")
.then(function (res) {
return res
})
.catch(function (err) {
return {
errcode: -1,
errmsg: "获取access_token失败"
}
});
res = JSON.parse(res);
// 将access_token插入到token表中缓存起来
await _token.add({
data: {
access_token: res.access_token,
expires_in: res.expires_in * 1000 + new Date().getTime()
}
})
access_token = res.access_token
} else {//token存在且未过期,直接取出来使用
access_token = token.data[0].access_token
}
// 调小程序A的云函数的参数
var options = {
method: 'POST',
uri: `https://api.weixin.qq.com/tcb/invokecloudfunction?access_token=${access_token}&env=prod-en**h&name=${event.func}`,// 注意env参数,请替换自己的环境id
body: event.data,
json: true
};
options.body.openid = wxContext.OPENID// 加上当前用户的openid
var responseData = await rp(options).then(res => res);
if (responseData.errcode == 0) {
return {
errcode: 0,
errmsg: 'success',
data: JSON.parse(responseData.resp_data)
}
} else {
return {
errcode: -3001,
errmsg: '接口调用失败',
data: responseData && responseData.resp_data ? JSON.parse(responseData.resp_data) : {}
}
}
}
5、小程序B页面中调小程序A的云函数
这个就相对简单了,直接看代码吧~
wx.cloud.callFunction({
name: 'api',// 小程序B自己的云函数
data: {
func: 'users_add',// 小程序A的云函数
data: {// 小程序A的云函数需要传递的参数
name: this.data.name,
age: this.data.age,
sex: this.data.sex,
tel: this.data.tel
}
}
}).then(res => {
if (res && res.result && res.result.data && res.result.data.errcode == 0) {
wx.showToast({
icon: 'none',
title: '添加成功',
duration: 2000
})
}else{
wx.showToast({
icon: 'none',
title: '添加失败',
duration: 2000
})
}
})
二、从小程序B访问小程序A上传的图片或文件
一开始的时候是直接使用了小程序A云开发自带的文件上传,文档可以看这里,上传之后返回的是文件id,如cloud://xxx.png,在小程序A里访问是没有问题的,但是在小程序B里访问时就打不开了。。。当然我们可以使用官方提供的方法去换取临时链接(有效期只有2个小时,哎......),想了想每次小程序B去访问A上传的图片还得去转链接,另外小程序B上传的图片怎么储存在小程序A的云数据库里呢?尤其是像上传到同一个集合中的同一个字段名,还得去记录是哪个小程序上传的,每次访问时都得需要换取临时链接,太尴尬了。。。所以,个人推荐使用的是第三方的文件存储服务器,生成一个永久链接,直接保存在数据库中,不管哪个小程序访问都可以直接打开。
三、总结
正如概述中所说的小程序云开发给予开发人员极强的单兵作战能力,我们只需关注业务开发而无需去投入过多的精力关注数据库和运维的内容,开发起来快速且流畅。本文记录里一下本人开发过程中碰到的一些问题,分享出来跟大家一起交流学习,若大家由更好的方法,请联系我,我们一起探讨探讨 : )
ps:云存储的文件能开放个功能让我们生成永久链接该多爽。。。还能当个文件服务器使用,哈哈哈哈~
用第三方的文件存储服务器,生成一个永久链接,直接保存在数据库中,不管哪个小程序访问都可以直接打开,优秀!