NocoBase RunJS APIResource:基于 URL 发起 HTTP 请求的通用资源深度解析
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
在 NocoBase 的 RunJS 运行时中,APIResource 是一个面向任意 HTTP 接口的通用请求资源。本文以 RunJS 文档为骨架,结合 flow-engine 包中的真实源码 与单元测试,完整讲清 APIResource 的适用场景、请求配置方法、URL 格式、refresh()数据拉取流程与错误处理机制。读完本文,你能够熟练使用ctx.makeResource('APIResource')对接自定义接口与第三方 API,并理解其响应式数据、事件与错误状态在底层是如何实现的。
什么是 APIResource
APIResource 是一个基于 URL 发起请求的通用 API 资源,适用于任意 HTTP 接口。它继承自 FlowResource 基类,并扩展了请求配置与refresh()方法。
与同目录下的 MultiRecordResource、SingleRecordResource 不同,APIResource不依赖资源名,直接按 URL 请求,因此特别适合自定义接口、第三方 API 等场景。
创建方式:ctx.makeResource('APIResource')或ctx.initResource('APIResource')。使用前需设置setURL();RunJS 上下文中会自动注入ctx.api(APIClient),无需手动setAPIClient。
从源码结构看,资源实例的创建入口在 FlowContext:flowContext.ts 中makeResource/initResource的签名明确将'APIResource' | 'SingleRecordResource' | 'MultiRecordResource' | 'SQLResource'列为支持的资源类名,其中initResource会把资源绑定到ctx.resource(若上下文中已有 resource 则直接跳过),makeResource则新建实例不绑定。所有可用资源类在 FlowEngine 构造函数 中通过registerResources统一注册,APIResource 即其中之一。
适用场景
| 场景 | 说明 |
|---|---|
| 自定义接口 | 调用非标准资源 API(如/api/custom/stats、/api/reports/summary) |
| 第三方 API | 通过完整 URL 请求外部服务(需目标支持 CORS) |
| 一次性查询 | 临时拉取数据,用完即弃,无需绑定到ctx.resource |
| 与 ctx.request 的取舍 | 需要响应式数据、事件、错误状态时用 APIResource;简单一次性请求可用ctx.request() |
这个取舍在源码中能得到印证:APIResource 内部将_data、_meta、_error三个状态全部用 formily 的observable.ref包裹(见 flowResource.ts),因此数据变化可以驱动 UI 响应式更新;而ctx.request()只是一次普通请求,没有任何状态容器。
基类能力(FlowResource)
所有 Resource 均具备 FlowResource 提供的通用能力:
| 方法 | 说明 |
|---|---|
getData() | 获取当前数据 |
setData(value) | 设置数据(仅本地) |
hasData() | 是否有数据 |
getMeta(key?)/setMeta(meta) | 读写元数据 |
getError()/setError(err)/clearError() | 错误状态 |
on(event, callback)/once/off/emit | 事件订阅与触发 |
几个源码层面的细节值得注意:
- 响应式存储:
_data、_meta、_error均为observable.ref,对数据、元数据、错误状态的读写都可被响应式系统追踪; - 错误模型:基类定义了 ResourceError,它会从原始异常的
response.data中解析出可读错误信息(error、errors、messages字段),并暴露code(缺省为'UNKNOWN_ERROR')与message两个 getter,供 RunJS 脚本统一取用; - 事件机制:
on/once/off/emit是轻量的自实现事件总线(once通过包装回调并在触发后off实现自动移除),refresh成功事件即由emit('refresh')触发。
请求配置
APIResource 在 apiResource.ts 中维护一个内部request配置对象,初始结构为{ headers: {}, params: {}, method: 'get', data: null },并围绕它提供完整的配置方法:
| 方法 | 说明 |
|---|---|
setAPIClient(api) | 设置 APIClient 实例(RunJS 中通常由上下文自动注入) |
getURL()/setURL(url) | 请求 URL |
loading | 读写加载状态(get/set) |
clearRequestParameters() | 清空请求参数 |
setRequestParameters(params) | 合并设置请求参数 |
setRequestMethod(method) | 设置请求方法(如'get'、'post',默认'get') |
addRequestHeader(key, value)/removeRequestHeader(key) | 请求头 |
addRequestParameter(key, value)/getRequestParameter(key)/removeRequestParameter(key) | 单参数增删查 |
setRequestBody(data) | 请求体(POST/PUT/PATCH 时使用) |
setRequestOptions(key, value)/getRequestOptions() | 通用请求选项 |
链式调用:上述配置方法几乎全部返回this,因此可以像 单元测试 中那样连写:
r.setURL('/api/x') .setRequestMethod('post') .addRequestHeader('X-Token', 'abc') .setRequestParameters({ a: 1 }) .setRequestBody({ k: 'v' }) .setRequestOptions('timeout', 1000);两个关键行为(均有测试用例验证):
- 参数是合并而非覆盖:
setRequestParameters的实现为this.request.params = { ...this.request.params, ...params },连续调用{ a: 1, b: 2 }后{ b: 3, c: 4 },最终得到{ a: 1, b: 3, c: 4 }(见 测试第 56-61 行)。需要彻底重置时应调用clearRequestParameters()。 getRequestOptions()返回内部对象的引用:测试 第 171-200 行 特意验证了对外部返回对象的原生修改会同步反映到资源内部状态。也就是说,getRequestOptions()返回的是“活”的配置,setRequestOptions('timeout', 1000)这类通用选项最终也会随请求一起发出。setAPIClient并非简单赋值:源码中它调用getDirtyAwareApiClient(api, this.context)对传入的 APIClient 做了包装(见 apiResource.ts 第 36-39 行),因此即便手动注入 client,也能感知上下文脏状态。RunJS 场景下构造函数会直接从context.api取用(第 29-34 行),一般无需手动设置。
另外,loading是一个 getter/setter 对,底层读取/写入 meta 中的loading键(默认false),测试 第 86-100 行 确认了r.loading = true与r.getMeta('loading')的联动。
URL 格式
- 资源风格:支持 NocoBase 资源简写,如
users:list、posts:get,会与 baseURL 拼接 - 相对路径:如
/api/custom/endpoint,与应用的 baseURL 拼接 - 完整 URL:跨域时使用完整地址,目标需配置 CORS
资源风格的 URL 意味着你可以用 APIResource 以“通用方式”调用 NocoBase 标准资源接口(资源名:动作),同时保留 URL 级别的参数、请求头定制能力——这是对 MultiRecordResource 等面向模型的资源的一种补充视角。
数据拉取:refresh() 的完整流程
refresh()是 APIResource 的核心方法,按当前 URL、method、params、headers、data 发起请求,将响应data写入setData(data)并触发'refresh'事件;失败时设置setError(err)并抛出ResourceError,不触发refresh事件。调用前必须已设置api与 URL。
对照 refresh() 源码,其执行流程为:
- 前置检查:
this.api未设置时直接抛出Error('API client not set')(测试用例 验证); - 清空旧错误:调用
clearError(),保证每次请求从干净的错误状态开始; - 发起请求:
this.api.request({ url: this.getURL(), ...this.getRefreshRequestOptions() }),其中getRefreshRequestOptions()返回整个内部request配置的展开副本,即 url、method、headers、params、data 及自定义选项一次性传给 APIClient; - 成功路径:
setData(data)后emit('refresh')——注意写入的是响应体中的data字段,而非整个响应; - 失败路径:将原始异常包装为
ResourceError,setError(error)后重新throw,且不触发refresh事件。
单元测试 对这三条路径都有覆盖:成功时断言getData()更新、getError()为null、onRefresh恰好触发一次、api.request收到合并后的配置;失败时断言预置的 data保持不变、refresh 事件未触发、抛出的是ResourceError实例。
示例
基础 GET 请求
const res = ctx.makeResource('APIResource'); res.setURL('/api/custom/endpoint'); res.setRequestParameters({ page: 1, pageSize: 10 }); await res.refresh(); const data = res.getData();资源风格 URL
const res = ctx.makeResource('APIResource'); res.setURL('users:list'); res.setRequestParameters({ pageSize: 20, sort: ['-createdAt'] }); await res.refresh(); const rows = res.getData()?.data ?? [];这里users:list对应 NocoBase 标准的资源风格接口,返回结构为{ data: [...] },故取getData()?.data。
POST 请求(带请求体)
const res = ctx.makeResource('APIResource'); res.setURL('/api/custom/submit'); res.setRequestMethod('post'); res.setRequestBody({ name: '测试', type: 'report' }); await res.refresh(); const result = res.getData();监听 refresh 事件
const res = ctx.makeResource('APIResource'); res.setURL('/api/stats'); res.on('refresh', () => { const data = res.getData(); ctx.render(<div>统计: {JSON.stringify(data)}</div>); }); await res.refresh();由于_data是响应式的,refresh事件适合与ctx.render组合:每次数据刷新后驱动 UI 更新。
错误处理
const res = ctx.makeResource('APIResource'); res.setURL('/api/may-fail'); try { await res.refresh(); const data = res.getData(); } catch (e) { const err = res.getError(); ctx.message.error(err?.message ?? '请求失败'); }失败时getData()保持原值(源码中 catch 分支不会调用setData,测试亦验证了这一点),错误信息应通过getError()获取;ResourceError自带解析好的message与code,可直接用于提示。
自定义请求头
const res = ctx.makeResource('APIResource'); res.setURL('https://api.example.com/data'); res.addRequestHeader('X-Custom-Header', 'value'); res.addRequestParameter('key', 'xxx'); await res.refresh();完整 URL 用于跨域请求外部服务,目标端需配置 CORS。
注意事项
- ctx.api 依赖:RunJS 中
ctx.api由运行环境注入,通常无需手动setAPIClient;若在无上下文的场景使用(如直接new FlowEngine().createResource(APIResource)),需自行设置,否则refresh()会抛出'API client not set'。 - refresh 即请求:
refresh()会按当前配置发起一次请求,method、params、data 等需在调用前配置好;配置是“快照式”的——refresh内部对内部request对象做展开拷贝传入 APIClient,因此请求发出前对配置的修改才会生效。 - 错误不更新 data:请求失败时
getData()保持原值,可通过getError()获取错误信息,且refresh事件不会触发。 - 与 ctx.request 的取舍:简单一次性请求可用
ctx.request();需要响应式数据、事件、错误状态管理时用 APIResource。
相关资源
- ctx.resource — 当前上下文中的 resource 实例
- ctx.initResource() — 初始化并绑定到 ctx.resource
- ctx.makeResource() — 新建 resource 实例,不绑定
- ctx.request() — 通用 HTTP 请求,适合简单一次性调用
- MultiRecordResource — 面向数据表/列表,支持 CRUD、分页
- SingleRecordResource — 面向单条记录
关键源码与测试路径:
| 内容 | 路径 |
|---|---|
| APIResource 实现 | packages/core/flow-engine/src/resources/apiResource.ts |
| FlowResource 基类与 ResourceError | packages/core/flow-engine/src/resources/flowResource.ts |
| 资源注册 | packages/core/flow-engine/src/flowEngine.ts |
| 单元测试 | packages/core/flow-engine/src/resources/tests/apiResource.test.ts |
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考