@tmsfe/tms-cloud-sdk
[TOC]
Thoughts
云开发提供了完整的一套服务端能力,可以让我们快速开发服务端业务逻辑而不用关注运维部署的细节。在过往的项目中,已经有一些典型的应用场景。
但是在云函数的开发和发布环节,还是有不少痛点,tms-cloud-sdk致力于解决云函数开发环节的痛点。部署环节的痛点另行方案解决,不在此讨论。
下面列举大家日常云开发联调过程中反馈的问题。我们围绕这些问题来封装公共能力。
重复且模板化的云函数框架初始代码
在每个云函数初始化时,都会存在类似的代码:
const TcbRouter = require('tcb-router');
const cloud = require('wx-server-sdk');
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV });
const getWxContext = () => {
const wxContext = cloud.getWXContext();
return {
openId: wxContext.OPENID,
appId: wxContext.APPID,
unionId: wxContext.UNIONID,
};
};
// 云函数入口函数
exports.main = async (event) => {
const app = new TcbRouter({ event });
app.use(async (ctx, next) => {
// 统一参数处理
const { openId } = cloud.getWXContext()();
const { event } = ctx._req; // eslint-disable-line
Object.assign(ctx, { data: {
openId,
...event,
} });
try {
const resData = await next();
Object.assign(ctx, { body: { errCode: 0, errMsg: 'success', resData } });
} catch (e) {
// 统一异常处理
const { errCode, errMsg } = e;
Object.assign(ctx, { body: { errCode, errMsg, resData: null } });
}
});
app.router('xxx/xxx', () => {}); // 路由注册
return app.serve();
};
分析这段代码,不同云函数之间有差异的只是路由注册的部分,参数预处理的中间件完全可以复用。
我们也有必要统一所有云函数的基础中间件,以实现日志打印的标准化;之前遇到部分云函数日志打印不全,需要排查时无从查起的尴尬场面;如果我们将日志打印标准化在基础中间件里,就可以避免此类问题。
海报图开发调试效率低下
很多时候我们需要绘制一张动态的还报图片,有很多云函数有写过这个逻辑,现在也有一个 canvasImg
的云函数来画图;其思路是在Node端用canvas画图,然后将canvas保存成图片。这种实现思路,开发联调的效率必然不高。
我们没有现成的可视化canvas画图工具,只能边改边发布云函数来调试,且此方案依赖的node-canvas包,在云函数环境无法正常安装,本地和云端需要兼容不同的node_modules依赖,见云函数中特殊npm包的分层解决方案。
换个思路,如果我们不去画canvas,而是去写HTML页面,这样本地就可以快速完成复杂视觉的HTML开发,云函数要做的只是将这个HTML渲染,保存成图片即可。且云函数环境已默认集成 puppeteer
包,不存在依赖安装问题。
按照这个思路,我们在tms-cloud-sdk中封装了画图的公共方法,拿来即用。
生成小程序码还要依赖后端
云函数中可以免鉴权调用微信的开放接口,直接生成小程序码,在一些业务场景需要生成动态小程序码让用户分享,前端可自己完成。
我们在tms-cloud-sdk中封装了生成小程序码的接口,在微信开放能力的基础上做了扩展,支持上传到云存储或转base64,方便大家拿来即用。
内容安全检测不能拿来即用
云函数中可以免鉴权调用微信的内容安全检测接口,但接口需要传很多定制参数,需要了解上下文背景。我们对这个接口做了一层封装,使用时只需传要检测的内容即可。
Documentation
serve 云函数初始化
serve方法是tms-cloud-sdk提供的云函数入口高阶函数,业务只需传入路由表和自定义中间件即可。serve方法中封装了统一的参数预处理,包括对不同调用方(小程序端、定时任务、微信回调、HTTP访问)的入参标准化。
同时,serve方法统一增加了云函数出入参的日志打印中间件。
参数
Object routes
路由表,Object类型,key为路由路径,value为处理函数
Array middleware
自定义中间件,Array类型,每一项为独立中间件函数
返回值
Function fn
返回云函数入口函数,可以直接用于云函数初始化。
示例代码
const tmsCloud = require('@tmsfe/tms-cloud-sdk');
const routes = {
'testfn/testroute': (ctx) => console.log(ctx),
};
const middleware = [async (ctx, next) => {
tmsCloud.logger.info(ctx);
return await next();
}];
exports.main = tmsCloud.serve(routes, middleware);
html2img 将HTML渲染为图片
顾名思义,将html渲染成图片,适用于动态生成海报图、分享图等业务场景。
此方法是对 puppeteer
的二次封装,拓展了对图片文件的处理,可以拿来即用。
性能优化提示:
云函数运行时环境内置了
puppeteer
, 我们的实现思路是用puppeteer
在headless
模式下打开要绘制的html页面,然后调用screenshot
方法截图。在这个过程中,初始化
puppeteer
实例需要时间,加载html页面需要时间,截图也需要时间,尤其是加载html页面,这个过程的耗时取决于html中加载的资源体量。如果在页面中使用了大量网络资源(图片、css)等时,加载耗时会明显增加。因此建议在html中严格控制使用的网络资源,尽可能用
base64
的方式嵌入在html中。
在绘制图片时,难免会用到一些特殊字体,我们已经将常用的字体以css文件的形式存放在cdn上,在html中可以直接用link标签引用。 如有其他字体包定制需求,请参照下面的base64字体css文件,或联系petegao协助处理。
<!--腾讯体W3-->
<link rel="stylesheet" href="https://static.img.tai.qq.com/web/fonts/TencentSans-W3.css">
<!--腾讯体W7-->
<link rel="stylesheet" href="https://static.img.tai.qq.com/web/fonts/TencentSans-W3.css">
<!--苹果平方标准体-->
<link rel="stylesheet" href="https://static.img.tai.qq.com/web/fonts/PingFang-Normal.css">
<!--DIN-Alternate-->
<link rel="stylesheet" href="https://static.img.tai.qq.com/web/fonts/DIN-Alternate.css">
参数
Object options
绘图配置
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
output | string | 否 | 生成图片的云函数本地路径,只需指定目录路径,文件名自动生成;为空时不保存本地文件 | |
cloudOutput | string | 否 | 生成图片的云存储路径,只需指定云存储目录路径,文件名自动生成;为空时不存储 | |
html | string | 是 | 要渲染的html内容,支持本地html文件路径、云存储fileID、网络https文件路径、或直接传html字符串 | |
content | string | 否 | handlebars模板内容,传入后将把html视为handlebars模板来渲染 | |
transparent | boolean | false | 否 | 绘制的图片是否透明背景 |
encoding | string | binary | 否 | 绘制图片的编码格式,支持 binary/base64, 默认为binary |
selector | string | body | 否 | 要绘制的元素,默认是body,可以通过css选择器标识要绘制的元素 |
type | string | png | 否 | 生成图片的文件类型,jpeg/png,默认为png |
返回值
Object rsp
属性 | 类型 | 说明 |
---|---|---|
localPath | string | 生成图片的云函数本地路径,入参传入output时有效 |
fileContent | binary/base64 | 生成的图片内容,内容格式为binary/base64,取决于入参的encoding |
encoding | string | 与入参的encoding相同 |
fileID | string | 云存储文件ID,当入参传cloudOutput时有效 |
tempFileURL | string | 云存储文件http访问链接,当入参传cloudOutput时有效 |
示例代码
const tmsCloud = require('@tmsfe/tms-cloud-sdk');
const routes = {
'testfn/drawimg': async () => {
const html = `
<!DOCTYPE html>
<html>
<head>
<style>
#myHeader {
background-color: lightblue;
color: black;
padding: 40px;
text-align: center;
}
</style>
</head>
<body>
<h2>{{title}}</h2>
<p>{{desc}}</p>
<h1 id="myHeader">My Header</h1>
</body>
</html>
`;
const { tempFileURL } = await tmsCloud.html2img({
html,
content: {
title: 'Yehuda',
desc: 'Katz',
}
cloudOutput: 'testfn/',
});
return { tempFileURL };
},
};
exports.main = tmsCloud.serve(routes);
callSinanServer 在云函数中调用SinanServer
在云函数中以L5的方式调用接入层SinanServer。
注意调用此方法的云函数必须是由微信环境(小程序调用/定时任务调用)触发,否则会提示微信cloudApiToken错误。
参数
String path
要请求的接口路径
Object param
要携带的参数,注意必须携带userId、token、wxAppId、seqId这几个公共参数,这些参数小程序里通过 tms.callCloudFunc 调用云函数时都有,直接透传即可。
String method
请求方法,支持 get/post
String env
请求的SinanServer环境,支持 test/production,分别对应SinanServer测试/生产环境
返回值
Object rsp
属性 | 类型 | 说明 |
---|---|---|
errCode | string | 接口错误码 |
errMsg | string | 接口错误信息描述 |
resData | object | 业务数据 |
示例代码
const tmsCloud = require('@tmsfe/tms-cloud-sdk');
const routes = {
'testfn/callsinan': async (ctx) => {
const { userId, token, seqId, wxAppId } = ctx.event;
const res = await tmsCloud.callSinanServer(
'user/carlist',
{ userId, token, seqId, wxAppId },
'post',
'production'
);
return res;
},
};
exports.main = tmsCloud.serve(routes);
getAppCode 生成小程序码
在云函数中生成小程序码。
此方法是对微信开放能力 cloud.openapi.wxacode.getUnlimited
的二次封装,拓展了对小程序码图片文件的处理,可以拿来即用。
注意调用此方法的云函数必须是由微信环境(小程序调用/定时任务调用)触发,否则会提示微信cloudApiToken错误。
参数
Object config
生成小程序码的配置
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
page | string | 否 | 页面 page,例如 pages/index/index,根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面 | |
scene | string | 否 | query参数,会拼在page后,在扫码打开的页面onLoad可以拿到。最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) | |
checkPath | boolean | true | 否 | 检查 page 是否存在,为 true 时 page 必须是已经发布的小程序存在的页面(否则报错);为 false 时允许小程序未发布或者 page 不存在, 但 page 有数量上限(60000个)请勿滥用 |
envVersion | string | release | 否 | 要打开的小程序版本。正式版为 release,体验版为 trial,开发版为 develop |
width | number | 否 | 二维码的宽度,单位 px,最小 280px,最大 1280px | |
autoColor | boolean | 否 | 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调,默认 false | |
lineColor | object | 否 | autoColor 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} 十进制表示 | |
isHyaline | boolean | false | 否 | 是否需要透明底色,为 true 时,生成透明底色的小程序 |
saveToCloud | boolean | false | 否 | 是否包存到云存储,默认false,传true时将自动上传至云存储,并返回文件ID与http访问链接 |
返回值
Object rsp
属性 | 类型 | 说明 |
---|---|---|
contentType | string | 小程序码图片类型,png/jpeg |
buffer | buffer | 小程序码图片文件流 |
string64 | string | 小程序码base64字符串,可直接用于 image 标签 src 展示 |
fileID | string | 云存储文件ID,当入参saveToCloud为true时有效,可直接用于 image 标签 src 展示 |
tempFileURL | string | 云存储文件http访问链接 |
示例代码
const tmsCloud = require('@tmsfe/tms-cloud-sdk');
const routes = {
'testfn/wxacode': async () => {
const { tempFileURL, fileID, string64 } = await tmsCloud.getAppCode({
page: 'modules/car/index/index',
scene: 'wecarId=xxxx',
checkPath: true,
envVersion: 'release',
saveToCloud: true,
});
return { tempFileURL, fileID, string64 };
},
};
exports.main = tmsCloud.serve(routes);
msgSecCheck 文本内容安全检测
检查一段文本是否含有违法违规内容。
此方法是对 cloud.openapi.security.msgSecCheck
的封装,简化了调用参数。
注意调用此方法的云函数必须是由微信环境(小程序调用/定时任务调用)触发,否则会提示微信cloudApiToken错误。
参数
String openid
用户的openid,可以从云函数请求上下文中获取
String content
需检测的文本内容,文本字数的上限为2500字,需使用UTF-8编码
返回值
Object result
属性 | 类型 | 说明 |
---|---|---|
label | number | 命中标签枚举值,100 正常;10001 广告;20001 时政;20002 色情;20003 辱骂;20006 违法犯罪;20008 欺诈;20012 低俗;20013 版权;21000 其他 |
suggest | string | 建议,有risky、pass、review三种值 |
示例代码
const tmsCloud = require('@tmsfe/tms-cloud-sdk');
const routes = {
'testfn/msgcheck': async (ctx) => {
const { OPENID } = ctx.wx_context;
const { label, suggest } = await tmsCloud.msgSecCheck(OPENID, 'xxxxx');
return { label, suggest };
},
};
exports.main = tmsCloud.serve(routes);
cloud
require('@tmsfe/tms-cloud-sdk').cloud
等价于
require('wx-server-sdk').cloud
logger
require('@tmsfe/tms-cloud-sdk').logger
等价于
require('wx-server-sdk').cloud.logger()
Contributing
在日常云开发过程中,肯定还有很多痛点,大家有想法欢迎随时提出需求,当然如果大家能协同共建,贡献代码,自然是最好的。