NocoBase RunJS APIResource:基于 URL 发起 HTTP 请求的通用资源深度解析
2026/9/18 17:53:26 网站建设 项目流程

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中解析出可读错误信息(errorerrorsmessages字段),并暴露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);

两个关键行为(均有测试用例验证):

  1. 参数是合并而非覆盖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()
  2. getRequestOptions()返回内部对象的引用:测试 第 171-200 行 特意验证了对外部返回对象的原生修改会同步反映到资源内部状态。也就是说,getRequestOptions()返回的是“活”的配置,setRequestOptions('timeout', 1000)这类通用选项最终也会随请求一起发出。
  3. 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 = truer.getMeta('loading')的联动。

URL 格式

  • 资源风格:支持 NocoBase 资源简写,如users:listposts: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() 源码,其执行流程为:

  1. 前置检查this.api未设置时直接抛出Error('API client not set')(测试用例 验证);
  2. 清空旧错误:调用clearError(),保证每次请求从干净的错误状态开始;
  3. 发起请求this.api.request({ url: this.getURL(), ...this.getRefreshRequestOptions() }),其中getRefreshRequestOptions()返回整个内部request配置的展开副本,即 url、method、headers、params、data 及自定义选项一次性传给 APIClient;
  4. 成功路径setData(data)emit('refresh')——注意写入的是响应体中的data字段,而非整个响应;
  5. 失败路径:将原始异常包装为ResourceErrorsetError(error)后重新throw,且触发refresh事件。

单元测试 对这三条路径都有覆盖:成功时断言getData()更新、getError()nullonRefresh恰好触发一次、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自带解析好的messagecode,可直接用于提示。

自定义请求头

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 基类与 ResourceErrorpackages/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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询