微信小程序云开发环境搭建与REST API混合架构实战
微信小程序云开发环境搭建与REST API混合架构实战
本文介绍如何在微信小程序中同时使用云开发(云函数、数据库)和外部REST API,构建灵活高效的混合架构,并解决跨域、鉴权等常见问题。
一、为什么需要混合架构?
微信小程序云开发提供了便捷的云函数和云数据库,但在实际项目中,我们经常遇到以下场景:
- 需要对接企业已有的业务系统(ERP、CRM、支付网关)
- 需要使用第三方AI服务(腾讯混元、百度文心一言)
- 需要调用外部数据源(天气API、物流API)
此时,混合架构就是最佳选择:核心业务数据走云开发保证安全与稳定,外部接口走REST API保证灵活性。
二、项目结构设计
miniprogram/ ├── cloudfunctions/ # 云函数目录 │ └── callExternalApi/ # 云函数:代理外部API ├── pages/ # 页面 ├── services/ # 业务服务层 │ ├── api.js # REST API 封装 │ └── cloudDb.js # 云数据库封装 └── utils/ └── auth.js # 鉴权工具三、云函数:代理外部API(解决跨域问题)
在小程序端直接调用外部API会面临跨域限制。正确的做法是通过云函数作为代理层。
3.1 创建云函数目录
mkdircloudfunctions/callExternalApicdcloudfunctions/callExternalApi3.2 云函数入口文件
// cloudfunctions/callExternalApi/index.jsconstcloud=require('wx-server-sdk')constaxios=require('axios')// 可在云函数中引入 npm 包cloud.init({env:cloud.DYNAMIC_CURRENT_ENV})exports.main=async(event,context)=>{const{url,method='GET',data={},headers={}}=eventtry{// 构建请求配置constconfig={method,url,headers:{'Content-Type':'application/json',...headers},timeout:10000// 10秒超时}// GET请求参数放params,POST/PUT放dataif(method==='GET'){config.params=data}else{config.data=data}// 调用外部APIconstresponse=awaitaxios(config)return{success:true,data:response.data}}catch(error){console.error('External API call failed:',error.message)return{success:false,error:error.message}}}3.3 package.json(云函数依赖)
{"name":"call-external-api","version":"1.0.0","dependencies":{"wx-server-sdk":"~2.6.3","axios":"^1.6.0"}}注意:云函数上传时需要先在本地安装依赖,再上传整个目录。也可在云函数根目录执行
npm install axios。
四、小程序端服务封装
4.1 REST API 封装
// services/api.js/** * 调用云函数代理外部API * @param {string} url 外部API地址 * @param {string} method 请求方法 * @param {object} data 请求参数 */asyncfunctioncallExternalApi(url,method='GET',data={}){try{constresult=awaitwx.cloud.callFunction({name:'callExternalApi',data:{url,method,data}})if(result.result.success){returnresult.result.data}else{thrownewError(result.result.error)}}catch(err){console.error('API call failed:',err)throwerr}}// ========== 具体业务API示例 ==========/** * 获取天气信息(调用高德天气API) */asyncfunctiongetWeather(cityCode){constapiKey='YOUR_AMAP_KEY'// 高德开放平台密钥consturl=`https://restapi.amap.com/v3/weather/weatherinfo`returnawaitcallExternalApi(url,'GET',{key:apiKey,city:cityCode})}/** * 调用物流查询API */asyncfunctionqueryExpress(no,type='auto'){consturl='https://api.kuaidi100.com/poll/query.do'returnawaitcallExternalApi(url,'POST',{compat:true,sign:'',// 需要按快递100文档计算签名param:JSON.stringify({number:no,type,resultv2:1})})}/** * 调用AI服务(示例:腾讯混元) */asyncfunctioncallAI(prompt,sessionId){// 注意:实际项目中应使用云开发环境的云函数调用AI// 此处仅示例外部API调用模式consttoken='YOUR_AI_TOKEN'consturl='https://hunyuan.cloud.tencent.com/hyllm/v1/chatCompletions'returnawaitcallExternalApi(url,'POST',{headers:{'Authorization':`Bearer${token}`},data:{model:'hunyuan',messages:[{role:'user',content:prompt}],stream:false}})}module.exports={getWeather,queryExpress,callAI}4.2 云数据库封装
// services/cloudDb.jsconstdb=wx.cloud.database()/** * 通用查询方法 */asyncfunctionquery(collectionName,where={},options={}){const{limit=20,skip=0,orderBy='_createTime',desc=true}=optionsreturnawaitdb.collection(collectionName).where(where).orderBy(orderBy,desc?'desc':'asc').skip(skip).limit(limit).get()}/** * 通用新增方法 */asyncfunctionadd(collectionName,data){returnawaitdb.collection(collectionName).add({data})}/** * 通用更新方法 */asyncfunctionupdate(collectionName,id,data){returnawaitdb.collection(collectionName).doc(id).update({data})}/** * 通用删除方法 */asyncfunctionremove(collectionName,id){returnawaitdb.collection(collectionName).doc(id).remove()}module.exports={query,add,update,remove}五、页面中混合调用示例
// pages/order-detail/order-detail.jsconstapi=require('../../services/api.js')constcloudDb=require('../../services/cloudDb.js')Page({data:{orderInfo:null,expressInfo:null,weather:null},asynconLoad(options){const{orderId}=options// 并行调用:本地云数据库 + 外部物流API + 外部天气APIconst[orderResult,expressResult,weatherResult]=awaitPromise.allSettled([this.getOrderFromDb(orderId),// 云数据库this.getExpressInfo(orderId),// 外部REST APIapi.getWeather('南宁')// 外部REST API])// 处理结果this.setData({orderInfo:orderResult.status==='fulfilled'?orderResult.value:null,expressInfo:expressResult.status==='fulfilled'?expressResult.value:null,weather:weatherResult.status==='fulfilled'?weatherResult.value:null})},// 从云数据库获取订单asyncgetOrderFromDb(orderId){constresult=awaitcloudDb.query('orders',{_id:orderId},{limit:1})returnresult.data[0]},// 从外部API获取物流信息asyncgetExpressInfo(orderId){// 假设订单数据中包含快递单号constorder=awaitcloudDb.query('orders',{_id:orderId},{limit:1})constexpressNo=order.data[0]?.expressNoif(!expressNo)returnnullreturnawaitapi.queryExpress(expressNo)}})六、安全注意事项
6.1 敏感信息不要放在小程序端
- API密钥不要硬编码在小程序代码中
- 云函数的环境变量中存储敏感配置
// 云函数中读取环境变量constsecretKey=process.env.SECRET_KEY// 通过云开发环境变量配置6.2 接口限流与防护
// 云函数中添加简单的限流逻辑constrateLimit=newMap()// 可改用RedisasyncfunctioncheckRateLimit(openId){constnow=Date.now()constkey=`rate:${openId}`constlastTime=rateLimit.get(key)||0if(now-lastTime<1000){// 1秒内最多1次thrownewError('请求过于频繁')}rateLimit.set(key,now)}6.3 数据校验
// 云函数入口处校验参数functionvalidateParams(event){const{url}=eventif(!url||typeofurl!=='string'){thrownewError('url参数必填且为字符串')}// 校验是否为合法域名constallowedDomains=['api.kuaidi100.com','restapi.amap.com']consturlObj=newURL(url)if(!allowedDomains.includes(urlObj.hostname)){thrownewError('不支持的API域名')}}七、部署与调试
7.1 本地调试云函数
# 进入云函数目录cdcloudfunctions/callExternalApi# 初始化npm并安装依赖npminit-ynpminstallaxios wx-server-sdk# 使用微信开发者工具的云函数本地调试功能7.2 云函数上传
# 在项目根目录执行miniprogram/ ├── cloudbaserc.json# 云开发配置文件└── cloudfunctions/ └── callExternalApi/ ├── index.js ├── package.json └── node_modules/# 需要一起上传提示:大型项目建议使用CI/CD自动部署云函数,配合GitHub Actions实现代码提交自动发布。
**本文作者:优睿科技(技术团队)
