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/nocobaseAPIClient是 NocoBase 前端 SDKpackages/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/client的Application在启动时创建的APIClient实例。实例属性APIClient暴露三个核心实例属性定义见 APIClient.ts属性类型说明axiosAxiosInstance内部持有的 axios 实例可以直接访问 axios API例如apiClient.axios.interceptors注册自定义拦截器authAuth客户端鉴权类负责 token、角色、语言的存取与请求头注入详见 AuthstorageBaseStorage客户端存储类默认封装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用于替换默认登录/注册逻辑storageTypelocalStorage \| sessionStorage \| memorylocalStorage存储后端类型memory表示纯内存存储不持久化storageClassany无自定义存储类传入后将优先于storageType使用storagePrefixstringNOCOBASE_存储 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: adminnocobase.com, password: admin123 }, basic);request()发起 HTTP 请求签名requestT any, R AxiosResponseT, D any(config: AxiosRequestConfigD | ResourceActionOptions): PromiseR类型type ResourceActionOptionsP any { resource?: string; resourceOf?: any; action?: string; params?: P; };request()接受两种配置1. AxiosRequestConfig通用 axios 请求参数直接透传给 axios 的请求配置参考 axios 的 Request Configconst res await apiClient.request({ url: });例如发起一个带查询参数的 GET 请求const res await apiClient.request({ url: users:list, method: get, params: { pageSize: 10, page: 1 }, });2. ResourceActionOptionsNocoBase 资源操作请求参数const res await apiClient.request({ resource: users, action: list, params: { pageSize: 10, }, });参数说明属性类型描述resourcestring1. 资源名称比如a2. 资源的关联对象名称比如a.bresourceOfany当resource为资源的关联对象名称时资源的主键值。比如a.b时代表a的主键值actionstring操作名称paramsany请求参数对象主要是 URL 参数请求体放到params.values中params.valuesany请求体对象底层分发逻辑从源码APIClient.ts可以看到request()的分发实现request(config): PromiseR { 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()返回一个资源操作对象可以链式调用任意操作名create、list、update、destroy以及自定义操作等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) Promiseany; export type IResource { [key: string]: ResourceAction; };IResource是一个索引签名类型任何操作名都会被解析为(params) Promiseany的函数。参数说明参数名类型描述namestring1. 资源名称比如a2. 资源的关联对象名称比如a.bofany当resource为资源的关联对象名称时资源的主键值。比如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这样的资源操作 URLHTTP 方法映射操作名为get、list时使用 GET 方法其余操作一律使用 POSTif ([get, list].includes(actionName)) { config[method] get; } else { config[method] post; }参数拆分调用操作函数时params会被拆分为三部分values→ 请求体仅非 GET 方法时写入config.datafilter→ 转为 JSON 字符串放入 URL 参数filter若已传字符串则原样透传且会剔除通配键*其余字段 → 直接作为 URL 查询参数。例如await resource.list({ filter: { status: published }, page: 1, pageSize: 10, sort: -createdAt, });最终请求为GET users:list?filter{status:published}page1pageSize10sort-createdAt。内置拦截器与参数序列化APIClient在构造函数末尾调用this.interceptors()APIClient.ts为 axios 实例注册了一个请求拦截器统一配置 URL 参数序列化方式config.paramsSerializer (params) { return qs.stringify(params, { strictNullHandling: true, arrayFormat: brackets, }); };这意味着null值参数会被保留strictNullHandling: true数组参数使用brackets格式序列化例如ids[]1ids[]2上文中filter的 JSON 字符串也是通过该序列化器编码到 URL 中的。此外request()还支持两个额外开关定义于 APIClient.ts选项类型说明skipNotifyboolean \| ((error) boolean)跳过全局错误提示如auth:syncCookies内部使用skipAuthboolean跳过鉴权相关逻辑Auth客户端鉴权Auth类Auth.ts负责在客户端存取用户信息、请求用户认证相关接口。它是APIClient的auth属性相关完整文档见 Auth。实例属性属性说明locale当前用户使用的语言role当前用户使用的角色tokenAPI 接口tokenauthenticator当前用户认证时所用的认证器参考 用户认证从源码看这四个属性都是基于Storage的读写 getter/setterAuth.ts读取时通过api.storage.getItem(key)获取写入时通过api.storage.setItem(key, value)持久化。其中authenticator实际存储的 key 是auth见getAuthenticator()中的getOption(auth)。请求头自动注入middlewareAuth构造函数为 axios 实例注册了请求拦截器middlewareAuth.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-TokenCSRF token 从 cookie 中读取getAuthCookieName(csrfToken, appName)。登录 / 注册 / 注销signIn()用户登录签名async signIn(values: any, authenticator?: string): PromiseAxiosResponseany参数名类型描述valuesany登录接口请求参数authenticatorstring登录使用的认证器标识登录成功后会通过setAuthenticator()/setToken()将认证器与 token 写入存储Auth.tsasync 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: adminnocobase.com, password: admin123 }, basic, );signUp()用户注册签名async signUp(values: any, authenticator?: string): PromiseAxiosResponseany参数名类型描述valuesany注册接口请求参数authenticatorstring注册使用的认证器标识await apiClient.auth.signUp( { email: userexample.com, password: pass123 }, basic, );signOut()注销登录签名async signOut(values: any, authenticator?: string): PromiseAxiosResponseany参数名类型描述valuesany注销接口请求参数authenticatorstring注销使用的认证器标识注销时会调用auth:signOut接口并清除 token、角色与认证器Auth.tsasync 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带skipNotifylostPassword()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 { // ... }从源码看实际抽象基类名为BaseStorageStorage.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时LocalStorage对token这个 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、显式withCredentials、signIn后 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.tsimport { 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 前端调用后端资源的统一入口其设计可以概括为三层请求层request()同时支持原生 axios 配置与ResourceActionOptions资源操作配置resource()基于 Proxy 提供users:list、users/1/roles:list等资源操作调用并自动完成 HTTP 方法映射、values/filter/ 查询参数拆分与序列化鉴权层Auth维护 token、角色、语言与认证器通过请求拦截器自动注入Authorization、X-Locale、X-Role、X-Authenticator、X-CSRF-Token等请求头并提供signIn/signUp/signOut/syncCookies等完整认证方法存储层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),仅供参考