添加数据源自定义方法
#
方法意图根据数据源方法的目的及实际使用场景, 可将数据源动作分为下述五类:
- 查询列表: 返回多条数据记录, 适用于前端需要展示列表、表格等的场景
- 查询单条: 返回单条数据记录, 适用于前段需要展示详情、信息卡片的场景, 前端编辑数据的场景也会需要使用该类型的方法获取要编辑的数据
- 修改数据: 更新已存在的数据记录, 如更新个人信息, 修改已发布的文章等等
- 删除数据: 删除(也包括多选删除)已有数据记录, 如多选删除,
- 新增数据: 添加新的数据记录, 如注册用户, 发布文章等等
在微搭应用编辑器中使用数据源时, 方法意图
能辅助使用者快速了解并选择合适的数据源方法.
#
自建数据源的预置方法提示
自建数据源已预置实现了 create(新增)、delete(删除)、update(更新)、getItem(查看单条)、getList(查看多条)、getRecords(查询列表) 方法, 与上述五种目的一一对应. 用户在新建、编辑数据源模型类型的数据源时, 可选择是否启用上述方法.
#
方法入参入参
即用于描述上述函数 params
的结构. 若函数无参数, 则不填写.
caution
若函数有参数, 则参数必须为 对象, 入参中描述字段结构应当与函数实际使用的 params
结构一致, 否则在微搭应用编辑器中使用时可能出错.
在微搭应用编辑器中使用数据源时, 会使用到数据源方法 入参
中描述的结构信息, 比如:
- 数据源变量的定义中, 会根据入参提示的结构生成变量的配置表单(提示出字段的中文名, 根据不同类型显示不同的表单控件)
- 表单容器关联数据源方法后, 会根据入参结构信息生成完整的表单, 如: 入参中 字段名称 会变成表单输入框的 字段标题, 入参中字段的 数据类型 可能会用于生成不同类型的输入框(手机号输入框/邮件输入框/开关选择器等), 入参中 枚举 信息会用于生成 多选输入框 或者下拉选框, 等等.
以上只是举例说明, 数据源方法入参在微搭应用编辑器中的使用场景很多, 故在填写 入参
时信息提供的越完整, 在微搭应用编辑器中使用时则越方便.
#
方法类型数据源方法根据实现方式, 分为两种:
- HTTP 请求: 仅外部数据源中提供该类型方法, 可简单对接第三方http接口, 可以通过简单的配置 HTTP 请求地址 、方法 、参数等等 即可往完成方法的配置
- 云函数: 通过编写js代码来实现更灵活的功能, 包括发起 HTTP 请求
#
HTTP请求HTTP请求底层也基于云开发的云函数能力封装, 提供了可视化、快速接入第三方 HTTP API 的功能.
HTTP 配置中配置的是 HTTP 请求底层的请求参数, 在配置 URL、 Query 和 Headers 的 value 、Body(仅请求方法Method为 POST 和 PUT时可配置) 可以使用模版 {{ }}
方式引用下述字段:
- 引用入参中定义的字段, 使用
{{ params.字段标识 }}
形式 - 引用环境变量信息, 使用
{{ env.字段标识 }}
形式, 如{{ env.openId }}
- 引用公共量信息, 使用
{{ vars.字段标识 }}
形式
关于http配置中的Body
在配置Body时, 填写的内容作会为一个完整的字符串模版处理, 应保证模版处理后的结果符合Body类型(JSON/FORM/XML)的要求, 否则请求会直接失败.
例如:
入参 params
为 { age: 12, name: 'Saurus', tags: ['aaa', 'bbb'] }
, HTTP配置中 Body 的类型为 JSON.
正确的 Body 示例, 请求可以正常发送:
- 假如 Body 内容为
{ "age": {{params.age}} }
, 得到的结果为{ "age": 12 }
- 假如 Body 内容为
{ "age": "{{params.age}}" }
, 得到的结果为{ "age": "12" }
, 注意age
的值 为字符串 - 假如 Body 内容为
{ "tags": {{params.tags}} }
, 得到的结果为{ "tags": ["aaa", "bbb"] }
错误的 Body 示例, 请求将直接失败:
- 假如 Body 内容为
{ age: {{params.age}} }
, 得到的结果为{ age: 12 }
,age
缺少双引号"
包裹 - 假如 Body 内容为
{ "name": {{params.name}} }
, 得到的结果为{ "name": Saurus }
,Saurus
缺少双引号"
包裹 - 假如 Body 内容为
{ "tags": "{{params.tags}}" }
, 得到的结果为{ "tags": "["aaa", "bbb"]" }
,tags
的值语法错误
#
云函数云函数是一种特殊的Javascript函数, 底层基于云开发的云函数能力封装, 最终会运行在服务器端的 Nodejs 10.15 环境中, 可以通过函数的 context参数来访问 云开发node sdk 的所有能力。
云函数编写需注意以下几点:
- 云函数必须使用
module.exports
导出 - 云函数只接受两个参数:
params
: 函数接受的参数, 即在方法入参描述的结构. 需保证实际使用时params
与 方法入参 描述的结构一致context
: 云函数上下文对象, 方便在云函数中实现各种功能, 具体结构可参考 云函数context
- 云函数返回的结果, 即
return
语句返回的内容应当是一个 对象, 切应当与 方法出参 描述的一致 - 云函数中若发现错误, 可以直接
throw new Error('xxxx')
来抛出错误, 也可以使用throw new TCBError(code, 'msg')
抛出错误并自定义错误代码. - 云函数开发过程中可以在通过
console.log('xxxx')
输出日志, 再使用 方法测试 来测试查看日志 - 云函数运行的环境为 Nodejs 10.15, 注意不要使用当前版本不支持的js特性, 比如可选链等.
下边是为自建数据源添加自定义的创建方法的示例代码:
假设该自建数据源有两个自定义数据源字段 name、email, 以下代码将对参数进行校验, 并限制只能使用 qq 邮箱, 并最后返回新记录的ID
module.exports = async function (params, context) { // 输出日志, 方便调试, 在 方法测试 中可以看到日志 console.log('params', params); if (!params.name || !params.email) throw new TCBError(1, 'name 或 email 不能为空'); if (!/@qq\.com$/i.test(params.email)) throw new TCBError(2, '仅支持使用qq邮箱'); const now = Date.now(); // 追加创建时间、更新时间 const newParams = Object.assign({}, params, { createdAt: now, updatedAt: now, });
// 使用云开发的 collection 相关API添加记录 const result = await context.collection.add(newParams); // 返回新记录的ID信息 return { _id: result.id };}
#
云函数contextcontext 为低码向云函数提供的上下文对象, 里面提供了操作数据库、调用云开发云函数的对象以及一些有用的环境变量.
参数 | 类型 | 必须 | 说明 |
---|---|---|---|
cloudbase | tcb | 是 | 云开发node sdk 对象, 即 import tcb from '@cloudbase/node-sdk'; 引入的 tcb , 使用文档 |
app | tcb.CloudBase | 是 | 云开发 node sdk 初始化后返回的 app 对象, 即 const app = tcb.init({...}) 得到的 app . 初始化默认使用当前低码使用的云开发环境相关参数进行初始化.可使用 app 直接调用 云开发云函数及存储相关能力(如app.callFunction({...}) 、app.uploadFile({...}) ), 使用文档 |
auth | 云开发鉴权对象 | 是 | 上述 app.auth() 返回得到的 auth 对象, 可用于直接调用鉴权相关的能力如(如 auth.getUserInfo(), auth.getEndUserInfo() 等等), 使用文档 |
database | 云开发数据库对象 | 是 | 云开发 node sdk 的数据库对象, 即 app.database() 返回的对象. 可使用 database 获取集合的引用(database.collection(('<collection-name>') ), 访问指令对象(database.command )等等.使用文档 |
collection | 云开发数据库集合对象 | 否 | 当前数据源关联的数据库表的引用对象(云开发 node sdk 的集合引用对象), 仅自建数据源才有该属性, 即 context.database.collection(context.env.dataSourceFullName) 返回的对象. 可直接使用该对象当前数据源的数据库表进行读写操作, 使用文档 |
env | 环境变量 | 是 | 具体请参考环境变量 |
httpAuth | auth处理对象 | 否 | 仅使用非空白模版创建的第三方数据源有该对象, 具体使用可参考如何为使用模版创建的第三方数据源集成新的方法 |
vars | 对象 | 是 | 当前数据源的公共变量 |
#
环境变量参数 | 类型 | 必须 | 说明 |
---|---|---|---|
openId | string | 否 | 微信openId,非微信授权登录则空 |
fromOpenId | string | 否 | 微信中, 多个小程序共享同一个环境时(即一个微搭环境绑定了多个小程序), 来源小程序的用户 openId, 实际使用时应当优先使用该值 |
currentOpenId | string | 否 | 当多个小程序共享同一个环境时(即一个微搭环境绑定了多个小程序), 若请求来自于共享的小程序, 则 currentOpenId 为 上述的 fromOpenId , 否则为 openId |
appId | string | 否 | 微信appId,非微信授权登录则空 |
fromAppId | string | 否 | 微信中, 多个小程序共享环境时(即一个微搭环境绑定了多个小程序), 来源小程序的appId, 实际使用时应当优先使用该值 |
currentAppId | string | 否 | 当多个小程序共享同一个环境时(即一个微搭环境绑定了多个小程序), 若请求来自于共享的小程序, 则 currentAppId 为 上述的 fromAppId , 否则为 appId |
uid | string | 否 | tcb用户唯一ID |
customUserId | string | 否 | 开发者自定义的用户唯一id,非自定义登录则空 |
isAnonymous | boolean | 是 | 是否为匿名用户 |
dataSourceName | string | 是 | 数据源标识, 用户在数据源管理中创建的数据源才有该属性 |
dataSourceFullName | string | 否 | 数据源完整名称, 为部署后底层(云函数名称/数据库表名)实际使用的标志 |
envId | string | 是 | 云开发环境ID |
isPreview | boolean | 是 | 是否为 体验环境, 开发者一般不需要使用改信息. 详见下方 环境说明 |
envType | prod pre | 是 | 环境类型, pre: 体验环境, prod: 正式环境; 开发者一般不需要使用改信息. 详见下方 环境说明 |
#
环境说明微搭生成出的应用分为两种:
体验应用
: 用于在微搭中开发测试应用. 在应用编辑器中通过预览区的实时真实预览
开关、编辑器顶部的预览
以及顶部 发布对话框 中的发布方式体验
得到的应用均属于体验应用
.正式应用
: 用于正式发布给终端用户使用. 在应用编辑器通过顶部 发布对话框 中的发布方式正式
得到的应用即为正式应用
.
体验应用
和 正式应用
调用的数据源及微搭后端支撑服务的数据则是互相隔离、互不影响的, 分别会调用到不同的后端环境:
体验环境
: 被体验应用
调用.正式环境
: 被正式应用
调用.
用户在微搭的数据源管理中, 新建/编辑保存数据源的时候, 则会自动更新 体验环境
的数据源. 当数据源测试验证通过后, 点击数据源管理中的 立即发布
按钮则会将数据源更新至 正式环境
.
#
方法测试数据源的方法(http请求及云函数)配置好后, 使用方法测试进行测试验证. 在「提交参数」中填写测试使用的参数, 点「运行测试」即可测试, 在测试输出的结果中可以看到方法运行的日志信息. 测试通过后, 点击 出参映射 即可快速填写 方法出参
在编写云函数的时候, 可以代码中使用 console.log
来输出日志, 在方法测试的「日志」中可以查看到日志信息.
caution
方法测试
功能无法模拟不同的终端用户身份, 若方法在不同用户身份、权限下处理有差异, 则无法通过 方法测试
测试验证. 可参考 使用数据源 来在应用中使用数据源, 并使用预览功能来测试应用, 以在真实场景中对数据源进行完整测试.
#
方法出参云函数代码最后返回的结果即为函数出参.
caution
出参
中描述的结构必须与方法返回结果的结构一致, 且必须为对象, 否则在微搭应用编辑器中使用可能出错
在微搭应用编辑器中使用数据源时, 会使用到数据源方法出参
中描述的结构信息, 比如:
- 数据源变量会使用关联的数据源方法的
出参
生成示例数据 - 表格关联数据源方法后, 会根据出参结构信息生成表格的表头信息
以上只是举例说明, 数据源方法出参在微搭应用编辑器中的使用场景很多, 故在填写 出参
时信息提供的越完整, 在微搭应用编辑器中使用时则越方便.
快速填写出参
可以使用 方法测试, 运行测试
后再点击 出参映射
后, 平台会分析返回的结果的结构并自动填充出参
#
HTTP方法出参中的字段映射在HTTP方法中, 使用 方法测试 的出参映射后, 填充的出参结构与HTTP请求原始返回结果结构一致.
HTTP方法的出参提供了 字段映射 的功能, 可对最终前端应用拿到的数据结构进行调整:
- 可以删除出参中不需要的字段, 删除后, 前端应用调用该方法时则不会返回相应字段
- 可以编辑出参中的字段, 字段中的 字段标识 为前端应用使用数据源时拿到的字段名称, 而 字段映射 则为该字段对应HTTP请求原始返回结果的字段路径, 即: 将 字段映射 路径所对应的值赋值给名称为 字段标识 的字段
- 修改 字段标识, 修改后, 前端应用拿到的字段名称则为修改后的名称
- 修改 字段映射, 修改后, 当前字段则在原始响应结果中从 字段映射 所描述的路径里取值
修改 字段映射 需要注意以下几点:
- 字段映射的值为对象的路径字符串, 如:
abc
、abc.ed
、abc.ed.cf[0].g
. 使用时注意路径是存在的, 否则将映射失败, 最终导致该方法报错 - 可使用代表原始响应结果的特殊的字符串
$root
, 在填写字段映射时$root.abc
与abc
效果一致, 一般可以省略$root
- 在需要对对象数组进行映射时, 可使用代表映射时循环的元素自身
$item
(可使用 $item.xxx 来指定对象某个字段) 及 循环索引序号$index
(从0开始). 注意, 数组字段需映射到原始响应结果中对应的对象数组
#
常见问题#
如何在云函数中使用第三方依赖目前在云函数中可以使用 request
和 @cloudbase/js-sdk
这两个内置库. 暂时未提供使用其他依赖的方式.
直接使用 node 函数 require
引入相关依赖即可, 如:
const request = require('request');
module.exports = function (params, context) { return new Promise(function (resolve, reject) { request('xxxxx', ...) })};
#
如何在云函数中发起 HTTP 请求可借助npm 包 request
来实现, 具体使用可参考下边代码:
/*** 使用 npm 包 request 发送HTTP请求, 详细使用文档可以参考* https://github.com/request/request#readme*/const request = require('request');
/** 依据 http状态码 判断请求是否成功 */function isSuccessStatusCode(code) { return code >= 200 && code < 300;}
module.exports = function (params, context) { // params 即为入参定义的结构, 可以在 request 的请求配置中使用 params return new Promise(function (resolve, reject) { request( { url: 'https://reqres.in/api/users?page=1&per_page=3', method: 'GET', // 将 json 为 true, 响应结果的 body 会被自动转换为对象, // 在POST请求中, 也会自动设置将 Content-Type 设置为 application/json json: true }, function (err, response, body) { if (err) return reject(err); if (!isSuccessStatusCode(response.statusCode)) return reject(new Error('request failed: ' + response.statusCode)); return resolve(body); } ); });};
#
如何为使用模版创建的第三方数据源集成新的方法在创建外部数据源时, 若使用非空白模版来创建, 则在该数据源云函数的 context
上会多出 httpAuth
对象. 通过 context.httpAuth
对象, 可以访问该类型外部数据源的认证信息.
context.httpAuth
类型定义如下:
参数 | 类型 | 必须 | 说明 |
---|---|---|---|
getAuthData | 函数 | 是 | 获取认证认证信息函数, 定义为 (requestConfig: IRequestConfig) => Promise<IAuthData> , requestConfig 即为 request 库请求的参数配置, 至少应当包含 uri, method 字段. IAuthData 认证信息 |
transformResponse | 函数 | 否 | 转换响应结果, 定义为 (body: any, response: RequestResponse) => Promise<Record<string, any>> . 该方法可能不存在, 使用时应做判断. 部分第三方数据源请求的结果会采用类似 {code, data, message} 结构(如腾讯文档) 将最终业务结果(包括业务成功/失败)包裹一层或返回的结构不够规范, 此时即可使用 transformResponse 对返回的结果进行统一转换, 业务调用失败则抛出错误, 业务调用成功则返回最终结果 |
#
IAuthData下述除 authInfo
外的字段均使用 authInfo 构造而来, 为方便使用而封装, 亦可自行阅读第三方官方接口文档来构造.
参数 | 类型 | 必须 | 说明 |
---|---|---|---|
authInfo | object | 是 | 后台token接口返回的原始认证信息, 不同类型的外部数据源拿到认证信息结构并不一致, 具体请参考 腾讯会议认证信息, 腾讯文档认证信息 |
uri | string | 否 | 转换后的请求地址 uri, 如果有返回该字段, 应使用该字段作为最终请求的URL |
headers | object | 否 | 公共请求头, 包含认证需要 accessToken 等其他公共请求头(不同数据源会有所差异). 若返回该字段, 则应与原有header 合并 |
body | object | 否 | 公共的body参数, 比如腾讯会议的 userid 字段. 若返回该字段, 则应与原有 body 合并 |
qs | object | 否 | 公共查询字符串. 若返回该字段, 则应与原有 qs 合并 |
#
腾讯会议认证信息参数 | 类型 | 必须 | 说明 |
---|---|---|---|
AccessToken | string | 是 | 腾讯会议access_token |
OpenId | string | 是 | 腾讯会议openid, 即接口中需要使用的 userid |
#
腾讯文档认证信息参数 | 类型 | 必须 | 说明 |
---|---|---|---|
AccessToken | string | 是 | 腾讯文档 access_token |
ClientId | string | 是 | 腾讯文档应用 id |
OpenId | string | 是 | 用户openid |
TokenType | string | 是 | 用户token类型 |
Scope | string | 是 | 用户授权范围 |
自定义云函数示例:
/** * 创建腾讯会议, 官方接口文档 https://cloud.tencent.com/document/product/1095/42417 */const request = require('request');
/** 依据 http状态码 判断请求是否成功 */function isSuccessStatusCode(code) { return code >= 200 && code < 300;}
module.exports = async function (params, context){ const { subject, type, start_time, end_time, password } = params; // request 请求配置信息 const requestConfig = { uri: 'https://api.meeting.qq.com/v1/meetings', method: 'POST', body: { instanceid: 1, subject, type, start_time: `${Math.floor(start_time / 1000)}`, end_time: `${Math.floor(end_time / 1000)}`, password, }, // 将 json 为 true, 响应结果的 body 会被自动转换为对象, // 在POST请求中, 也会自动设置将 Content-Type 设置为 application/json json: true }; // 获取认证信息 const authData = await context.httpAuth.getAuthData(requestConfig); // 合并配置信息 const finalRequestConfig = mergeAuthData(requestConfig, authData); // params 即为入参定义的结构, 可以在 request 的请求配置中使用 params return new Promise(function (resolve, reject) { // 注意: request 的回掉函数为 async 函数, 因函数内有一个 await 调用 request(finalRequestConfig, async function (err, response, body) { if (err) return reject(err); if (!isSuccessStatusCode(response.statusCode)) { // 失败时, body 中会包含详细错误信息 return reject(new Error(`request failed: code: ${response.statusCode}, msg: ${JSON.stringify(body)}`)); } let result = body // 数据源实际未针对 腾讯会议 提供 transformResponse, 下边这段代码亦可以去掉 if (context.httpAuth.transformResponse) { result = await context.httpAuth.transformResponse(body, response); } // 成功时 body 为空 return resolve(result); } ); });}
/** * 将request 请求配置与 IAuthData 中的进行合并 */function mergeAuthData(requestConfig, authData) { const newConfig = Object.assign({}, requestConfig); if (authData.uri) { newConfig.uri = authData.uri; } if (authData.body) { if (requestConfig.form) { newConfig.form = Object.assign(newConfig.form || {}, authData.body); } else if (!newConfig.body || typeof newConfig.body === 'object') { newConfig.body = Object.assign(newConfig.body || {}, authData.body); // 再次设置 contentType 为json, 避免 requestConfig.body 为空时未设置 contentType 的问题 newConfig.json = true; } } if (authData.headers) { newConfig.headers = Object.assign(newConfig.headers || {}, authData.headers); }
if (authData.qs) { newConfig.qs = Object.assign(newConfig.qs || {}, authData.qs); } return newConfig;}
#
内置第三方数据源的官方API文档腾讯文档:
目前微搭仅接入了 sheet 相关的接口, 可根据接口文档接入其他其他API.
腾讯会议:
微搭采用 OAuth2.0 方式接入的腾讯会议, 目前只有会议管理相关API支持 OAuth2.0 方式调用, 会议管理相关API也已全部内置进会议的数据源模版中.
会议管理相关API的参数非常丰富, 为方便用户使用, 微搭侧封装时仅提供了必填的常用参数. 若需要使用微搭模版中未支持的参数, 可参考腾讯会议官方API文档来修改会议数据源中对应方法的封装.