news 2026/9/14 8:33:02

NocoBase 前端 SDK APIClient 完全指南:HTTP 请求、资源操作与认证存储

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NocoBase 前端 SDK APIClient 完全指南:HTTP 请求、资源操作与认证存储

NocoBase 前端 SDK APIClient 完全指南: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

APIClient是 NocoBase 前端 SDK(packages/core/sdk)的核心客户端类,它基于 axios 封装,为插件和业务代码提供了统一的资源操作(Resource Action)调用、鉴权信息管理与本地存储能力。通过本文,你将掌握APIClient的构造与配置、request()/resource()两种请求范式、Auth登录认证流程以及Storage存储抽象,并能在自己的插件中直接落地使用。

概览:APIClient 是什么

APIClient基于 axios 封装,用于在客户端(浏览器)通过 HTTP 请求 NocoBase 的资源操作接口。它承担了三类职责:

  • 发起 HTTP 请求:既支持原生 axios 请求配置,也支持面向 NocoBase 资源操作(resource/action)的专用配置;
  • 管理鉴权状态:通过内部持有的Auth实例维护 token、角色、语言、认证器,并在每次请求中自动注入对应请求头;
  • 统一客户端存储:通过内部持有的Storage实例读写 token 等状态,默认使用localStorage

在插件中,最典型的用法是直接在插件的load()生命周期里通过this.app.apiClient发起请求:

class PluginSampleAPIClient extends Plugin { async load() { const res = await this.app.apiClient.request({ // ... }); } }

这里的this.app.apiClient即当前应用(@nocobase/clientApplication)在启动时创建的APIClient实例。

实例属性

APIClient暴露三个核心实例属性(定义见 APIClient.ts):

属性类型说明
axiosAxiosInstance内部持有的 axios 实例,可以直接访问 axios API,例如apiClient.axios.interceptors注册自定义拦截器
authAuth客户端鉴权类,负责 token、角色、语言的存取与请求头注入,详见 Auth
storageBaseStorage客户端存储类,默认封装localStorage,详见 Storage

例如,需要为所有请求追加自定义请求头时,可直接操作底层 axios 实例:

apiClient.axios.interceptors.request.use((config) => { config.headers['X-Custom'] = 'custom-value'; return config; });

构造函数与配置选项

签名

  • constructor(instance?: APIClientOptions)

类型

interface ExtendedOptions { authClass?: any; storageType?: 'localStorage' | 'sessionStorage' | 'memory'; storageClass?: any; storagePrefix?: string; appName?: string; // 共享 token shareToken?: boolean; } export type APIClientOptions = AxiosInstance | (AxiosRequestConfig & ExtendedOptions);

从源码看,APIClientOptions是一个联合类型(APIClient.ts):

  • 直接传入一个axios 实例AxiosInstance):此时APIClient直接复用该实例,不再新建;
  • 传入axios 请求配置 + 扩展选项AxiosRequestConfig & ExtendedOptions):内部通过axios.create(others)创建新实例,并初始化存储与鉴权。

扩展选项说明如下:

选项类型默认值说明
authClassanyAuth自定义鉴权类,必须继承Auth,用于替换默认登录/注册逻辑
storageType'localStorage' \| 'sessionStorage' \| 'memory''localStorage'存储后端类型,memory表示纯内存存储(不持久化)
storageClassany自定义存储类,传入后将优先于storageType使用
storagePrefixstring'NOCOBASE_'存储 key 前缀,默认值在源码中通过storagePrefix = 'NOCOBASE_'声明
appNamestring应用名称;设置后存储前缀变为storagePrefix + appName.toUpperCase() + '_'
shareTokenbooleanfalse是否跨应用共享 token;为true时 token 使用基础前缀存储(见 Storage 一节)

storagePrefix 的生成规则

源码(APIClient.ts)展示了前缀的具体拼接逻辑:

this.baseStoragePrefix = storagePrefix; this.storagePrefix = appName ? `${storagePrefix}${appName.toUpperCase()}_` : storagePrefix;

即:设置appName: 'myApp'storagePrefix: 'NOCOBASE_'时,实际存储前缀为NOCOBASE_MYAPP_,对应测试 api-client.test.ts 中断言 token 被写入N2_MYAPP_TOKEN(前缀N2_+MYAPP)。这种按应用命名空间隔离存储的设计,是为了支持多应用(Multi-App)场景下各自维护登录态。

创建实例示例

import { APIClient } from '@nocobase/sdk'; const api = new APIClient({ baseURL: 'https://localhost:8000/api', storagePrefix: 'NOCOBASE_', appName: 'myApp', }); await api.auth.signIn({ email: 'admin@nocobase.com', password: 'admin123' }, 'basic');

request():发起 HTTP 请求

签名

  • request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D> | ResourceActionOptions): Promise<R>

类型

type ResourceActionOptions<P = any> = { resource?: string; resourceOf?: any; action?: string; params?: P; };

request()接受两种配置:

1. AxiosRequestConfig:通用 axios 请求参数

直接透传给 axios 的请求配置,参考 axios 的 Request Config:

const res = await apiClient.request({ url: '' });

例如发起一个带查询参数的 GET 请求:

const res = await apiClient.request({ url: 'users:list', method: 'get', params: { pageSize: 10, page: 1 }, });
2. ResourceActionOptions:NocoBase 资源操作请求参数
const res = await apiClient.request({ resource: 'users', action: 'list', params: { pageSize: 10, }, });

参数说明:

属性类型描述
resourcestring1. 资源名称,比如a
2. 资源的关联对象名称,比如a.b
resourceOfanyresource为资源的关联对象名称时,资源的主键值。比如a.b时,代表a的主键值
actionstring操作名称
paramsany请求参数对象,主要是 URL 参数,请求体放到params.values
params.valuesany请求体对象

底层分发逻辑

从源码(APIClient.ts)可以看到request()的分发实现:

request(config): Promise<R> { const { resource, resourceOf, action, params, headers } = config as any; if (resource) { return this.resource(resource, resourceOf, headers)action; } return this.axios.request(config); }

也就是说:一旦配置中带有resource字段,请求会转交给resource()方法生成的操作对象执行;否则走原生axios.request()

resource():获取资源操作方法对象

resource()返回一个资源操作对象,可以链式调用任意操作名(createlistupdatedestroy以及自定义操作等):

const resource = apiClient.resource('users'); await resource.create({ values: { username: 'admin', }, }); const res = await resource.list({ page: 2, pageSize: 20, });

签名

  • resource(name: string, of?: any, headers?: AxiosRequestHeaders): IResource

类型

export interface ActionParams { filterByTk?: any; [key: string]: any; } type ResourceAction = (params?: ActionParams) => Promise<any>; export type IResource = { [key: string]: ResourceAction; };

IResource是一个索引签名类型,任何操作名都会被解析为(params) => Promise<any>的函数。

参数说明

参数名类型描述
namestring1. 资源名称,比如a
2. 资源的关联对象名称,比如a.b
ofanyresource为资源的关联对象名称时,资源的主键值。比如a.b时,代表a的主键值
headersAxiosRequestHeaders后续要发起资源操作请求时,携带的 HTTP 请求头

关联对象(关联资源)请求

当资源是关联对象时,通过of指定主资源的主键值。例如「获取 id 为 1 的用户的角色列表」:

const res = await apiClient.resource('users.roles', 1).list({ pageSize: 20, });

底层会构造出形如users/1/roles:list的请求 URL。

Proxy 实现原理

resource()使用 JavaScriptProxy实现(APIClient.ts),其关键行为如下:

  • URL 构造name.split('.').join('/{of}/')a.b展开为a/{of}/b,再追加:actionName,得到users/1/roles:list这样的资源操作 URL;
  • HTTP 方法映射:操作名为getlist时使用 GET 方法,其余操作一律使用 POST:
    if (['get', 'list'].includes(actionName)) { config['method'] = 'get'; } else { config['method'] = 'post'; }
  • 参数拆分:调用操作函数时,params会被拆分为三部分:
    • values→ 请求体(仅非 GET 方法时写入config.data);
    • filter→ 转为 JSON 字符串放入 URL 参数filter(若已传字符串则原样透传,且会剔除通配键*);
    • 其余字段 → 直接作为 URL 查询参数。

例如:

await resource.list({ filter: { status: 'published' }, page: 1, pageSize: 10, sort: '-createdAt', });

最终请求为GET users:list?filter={"status":"published"}&page=1&pageSize=10&sort=-createdAt

内置拦截器与参数序列化

APIClient在构造函数末尾调用this.interceptors()(APIClient.ts),为 axios 实例注册了一个请求拦截器,统一配置 URL 参数序列化方式:

config.paramsSerializer = (params) => { return qs.stringify(params, { strictNullHandling: true, arrayFormat: 'brackets', }); };

这意味着:

  • null值参数会被保留(strictNullHandling: true);
  • 数组参数使用brackets格式序列化,例如ids[]=1&ids[]=2
  • 上文中filter的 JSON 字符串也是通过该序列化器编码到 URL 中的。

此外,request()还支持两个额外开关(定义于 APIClient.ts):

选项类型说明
skipNotifyboolean \| ((error) => boolean)跳过全局错误提示(如auth:syncCookies内部使用)
skipAuthboolean跳过鉴权相关逻辑

Auth:客户端鉴权

Auth类(Auth.ts)负责在客户端存取用户信息、请求用户认证相关接口。它是APIClientauth属性,相关完整文档见 Auth。

实例属性

属性说明
locale当前用户使用的语言
role当前用户使用的角色
tokenAPI 接口token
authenticator当前用户认证时所用的认证器,参考 用户认证

从源码看,这四个属性都是基于Storage的读写 getter/setter(Auth.ts):读取时通过api.storage.getItem(key)获取,写入时通过api.storage.setItem(key, value)持久化。其中authenticator实际存储的 key 是auth(见getAuthenticator()中的getOption('auth'))。

请求头自动注入(middleware)

Auth构造函数为 axios 实例注册了请求拦截器middleware(Auth.ts),每次请求自动注入以下请求头:

请求头触发条件
X-Locale设置了locale语言标识
X-Role设置了role角色标识
X-Authenticator设置了authenticator且未手动传该请求头认证器标识
Authorization设置了token且未手动传该请求头Bearer ${token}
X-CSRF-Token非安全方法(非get/head/options)且有 CSRF token从 cookie 读取

CSRF 防护逻辑值得注意:SAFE_METHODS = new Set(['get', 'head', 'options']),只有写操作(POST 等)才会携带X-CSRF-Token,CSRF token 从 cookie 中读取(getAuthCookieName('csrfToken', appName))。

登录 / 注册 / 注销

signIn():用户登录
  • 签名:async signIn(values: any, authenticator?: string): Promise<AxiosResponse<any>>
参数名类型描述
valuesany登录接口请求参数
authenticatorstring登录使用的认证器标识

登录成功后会通过setAuthenticator()/setToken()将认证器与 token 写入存储(Auth.ts):

async signIn(values: any, authenticator?: string) { const response = await this.api.request({ method: 'post', url: 'auth:signIn', data: values, headers: { 'X-Authenticator': authenticator }, }); const data = response?.data?.data; this.setAuthenticator(authenticator); this.setToken(data?.token); return response; }
await apiClient.auth.signIn( { email: 'admin@nocobase.com', password: 'admin123' }, 'basic', );
signUp():用户注册
  • 签名:async signUp(values: any, authenticator?: string): Promise<AxiosResponse<any>>
参数名类型描述
valuesany注册接口请求参数
authenticatorstring注册使用的认证器标识
await apiClient.auth.signUp( { email: 'user@example.com', password: 'pass123' }, 'basic', );
signOut():注销登录
  • 签名:async signOut(values: any, authenticator?: string): Promise<AxiosResponse<any>>
参数名类型描述
valuesany注销接口请求参数
authenticatorstring注销使用的认证器标识

注销时会调用auth:signOut接口,并清除 token、角色与认证器(Auth.ts):

async signOut() { const response = await this.api.request({ method: 'post', url: 'auth:signOut', }); this.setToken(null); this.setRole(null); this.setAuthenticator(null); return response; }

其他认证方法

Auth还封装了若干补充方法(Auth.ts):

方法请求地址说明
syncCookies()auth:syncCookies已有 token 时同步 Cookie(带skipNotify
lostPassword()auth:lostPassword忘记密码,自动携带当前页面 URL 信息
resetPassword()auth:resetPassword重置密码
checkResetToken()auth:checkResetToken校验重置密码 token

另外,setToken()在应用上下文存在时还会派发auth:tokenChanged事件(Auth.ts),供其他模块监听 token 变化。

自定义 Auth 类

通过authClass选项可以替换默认的认证逻辑,自定义类需继承Auth并覆写signIn等方法。测试 api-client.test.ts 演示了这一用法:

class TestAuth extends Auth { async signIn(values: any) { const response = await this.api.request({ method: 'post', url: 'auth:test', data: values, }); const data = response?.data?.data; this.setAuthenticator('test'); this.setToken(data?.token); return response; } } const api = new APIClient({ baseURL: 'https://localhost:8000/api', authClass: TestAuth, }); expect(api.auth).toBeInstanceOf(TestAuth);

Storage:客户端存储抽象

Storage类(Storage.ts)用于客户端信息存储,默认使用localStorage,完整文档见 Storage。

抽象基类

export abstract class Storage { abstract clear(): void; abstract getItem(key: string): string | null; abstract removeItem(key: string): void; abstract setItem(key: string, value: string): void; } export class CustomStorage extends Storage { // ... }

从源码看,实际抽象基类名为BaseStorage(Storage.ts),它额外提供toUpperCase(prefix, ...arr)工具方法,用于将前缀与 key 统一转为大写并用下划线连接,例如toUpperCase('app_', 'token')得到APP_TOKEN

四种存储实现

实现类存储后端说明
MemoryStorageMap纯内存存储,不持久化;刷新页面即丢失
LocalStoragewindow.localStorage默认实现,key 统一按PREFIX_KEY大写格式写入
SessionStoragewindow.sessionStorage继承LocalStorage,仅替换存储后端为 sessionStorage
自定义类任意继承BaseStorage实现四个抽象方法,通过storageClass注入

APIClient.createStorage()(APIClient.ts)根据storageType决定实例化哪种实现,且当localStorage/sessionStorage不可用(如 SSR 环境)时自动回退到MemoryStorage

类方法

方法签名说明
setItem()setItem(key: string, value: string): void存储内容
getItem()getItem(key: string): string \| null获取内容
removeItem()removeItem(key: string): void删除内容
clear()clear(): void清除所有内容

shareToken:跨应用共享 token

shareToken: true时,LocalStoragetoken这个 key 使用baseStoragePrefix(基础前缀)而非应用专属前缀进行读写(Storage.ts),从而让多个应用共享同一登录态。测试 api-client.test.ts 验证了该行为:

const api1 = new APIClient({ baseURL, storagePrefix: 'N2_', shareToken: true }); api1.auth.setToken('123'); const api = new APIClient({ baseURL, appName: 'myApp', storagePrefix: 'N2_', shareToken: true }); expect(api.auth.getToken()).toBe('123'); // 跨应用读到共享 token

测试验证:SDK 行为如何被保障

SDK 配套了两个测试文件,可以作为行为契约参考:

  • api-client.test.ts 覆盖:实例创建与baseURL、显式withCredentialssignIn后 token 写入存储、syncCookies携带Authorization: Bearer头、appName命名空间存储(N2_MYAPP_TOKEN)、shareToken共享、resource().test()自定义操作、自定义authClass等;
  • Storage.test.ts 覆盖:MemoryStorage的增删改查、LocalStorage/SessionStorage的前缀大写化(TESTPREFIX_KEY1)、多前缀隔离、clear()行为等。

这些测试同时演示了如何在 Node 环境中 mocklocalStorage/window/document.cookie,并用axios-mock-adapter拦截请求来验证APIClient的行为,是编写 SDK 相关单测的很好范例。

在 React 客户端中获取 APIClient

在 NocoBase 客户端插件中,除了this.app.apiClient,还可以通过useAPIClient()钩子在组件内获取当前应用的APIClient实例(useAPIClient.ts):

import { useAPIClient } from '@nocobase/client'; const MyComponent = () => { const apiClient = useAPIClient(); const handleClick = async () => { const res = await apiClient.resource('users').list({ pageSize: 10 }); console.log(res.data); }; return <button onClick={handleClick}>加载用户</button>; };

该钩子优先从APIClientContext上下文取值,取不到时回退到app.apiClient,保证在应用初始化完成后的任意组件中都能拿到同一个实例。

总结

APIClient是 NocoBase 前端调用后端资源的统一入口,其设计可以概括为三层:

  1. 请求层request()同时支持原生 axios 配置与ResourceActionOptions资源操作配置,resource()基于 Proxy 提供users:listusers/1/roles:list等资源操作调用,并自动完成 HTTP 方法映射、values/filter/ 查询参数拆分与序列化;
  2. 鉴权层Auth维护 token、角色、语言与认证器,通过请求拦截器自动注入AuthorizationX-LocaleX-RoleX-AuthenticatorX-CSRF-Token等请求头,并提供signIn/signUp/signOut/syncCookies等完整认证方法;
  3. 存储层Storage抽象出localStorage/sessionStorage/memory三种后端,支持按应用命名空间隔离与shareToken跨应用共享,也允许通过storageClass注入自定义实现。

对于需要深度定制请求行为的开发者,还可以基于authClass替换认证逻辑、基于storageClass替换存储实现,或直接操作apiClient.axios.interceptors扩展全局拦截器。理解这三层结构,是编写健壮的 NocoBase 客户端插件与二次开发的基础。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 8:32:56

Cilium eBPF 数据面 IP 分片跟踪(Fragment Handling)完整指南

Cilium eBPF 数据面 IP 分片跟踪&#xff08;Fragment Handling&#xff09;完整指南 【免费下载链接】cilium eBPF-based Networking, Security, and Observability 项目地址: https://gitcode.com/GitHub_Trending/ci/cilium Cilium 的 eBPF 数据面默认启用 IP 分片跟…

作者头像 李华
网站建设 2026/9/14 8:32:36

AI语言引擎:破解游戏出海本地化与买量增长脱节的钥匙

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 8:32:02

MATLAB实现OFDM信道编码:卷积码、Turbo与LDPC完整链路

简介&#xff1a;面向通信工程学生与研究人员的OFDM完整MATLAB仿真资源&#xff0c;聚焦信道估计、调制与信道编码三大核心模块&#xff0c;覆盖正交频分复用系统的关键知识点&#xff0c;帮助理解OFDM从发射到接收的完整链路以及不同传输策略对系统性能的影响。压缩包共21个文…

作者头像 李华
网站建设 2026/9/14 8:29:16

WPS JSA实现Excel多Sheet数据合并与自动化处理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华