Backstage 前端插件 HTTP 客户端实战从 fetchApi 到 OpenAPI 生成客户端【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage导读本文聚焦 Backstage 前端插件如何与后端 API 通信围绕脚手架生成的TodoPage讲解fetchApiRef的两大核心能力自动注入认证凭证与解析plugin://pluginId协议。你将学会如何阅读与扩展脚手架代码、把散落的请求逻辑提取为独立客户端类并掌握基于 OpenAPI 自动生成类型安全客户端、防止前后端漂移的进阶方案。所有示例均来自仓库中的 Golden Path 教程与真实源码实现可直接套用到你的插件开发中。前置脚手架生成的前端插件在阅读本文之前你需要先通过以下命令创建一个名为todo的前端插件详见 001 - Scaffolding the pluginyarn new --select frontend-plugin --option pluginIdtodo --option owner命令执行完成后plugins/todo/目录下会生成如下关键结构plugins/todo/ ├── dev/ # 独立开发服务器配置 ├── src/ │ ├── components/ │ │ ├── TodoList/ │ │ └── TodoPage/ │ └── ... # 插件定义、路由、测试 └── package.json其中src/components/TodoPage/是主要页面组件它负责从后端获取 todo 数据并通过TodoList组件渲染。同时你需要一个后端 todo 插件来提供真实数据后端插件的创建与调试方式见 Golden Path 后端教程 与 002 - Poking around。脚手架代码是如何工作的打开plugins/todo/src/components/TodoPage/TodoPage.tsx可以看到useTodos这个 hookfunction useTodos() { const { fetch } useApi(fetchApiRef); return useAsync(async (): PromiseTodoItem[] { const response await fetch(plugin://todo/todos); if (!response.ok) { throw new Error( Failed to fetch todos: ${response.status} ${response.statusText}, ); } const data await response.json(); return data.items; }); }这段代码虽短却体现了 Backstage 前端数据获取的两个关键设计。fetchApi包装过的浏览器 fetchuseApi(fetchApiRef)从 Backstage 的应用上下文中取得fetchApiRef所对应的FetchApi实现。在 packages/frontend-plugin-api/src/apis/definitions/FetchApi.ts 中它的定义是export type FetchApi { fetch: typeof fetch; }; export const fetchApiRef createApiRefFetchApi().with({ id: core.fetch, pluginId: app, });它本质上是一个包装了浏览器fetch的 API签名与原生fetch完全一致但额外带上了两个默认行为自动注入认证凭证——你不需要手动附加任何Authorization请求头框架会在用户已登录的前提下自动注入身份令牌解析plugin://pluginIdURL 协议——把plugin://todo/todos这样的地址解析成你当前实例中该插件对应的真实 HTTP(S) 地址。需要注意的一点是默认实现要求用户已经完成登录才能注入认证信息。因此在SignInPage等特殊场景下使用默认的fetchApiRef反而会引发问题此时可以退回使用原生的系统fetch。这一点在FetchApi.ts的类型注释中有明确说明。useAsync请求生命周期管理useAsync来自react-hookz/web它在组件挂载时执行异步函数并返回[{ status, result, error }, { execute }]。TodoPage组件正是利用这个元组来在status loading时展示加载动画spinner在请求失败时回退展示示例 todo 数据在请求成功后渲染真实获取到的 todo 列表。它把“加载中 / 成功 / 失败”三种状态统一建模让组件代码保持简洁。实际跑一遍确保前端和后端都在运行——在仓库根目录执行yarn start会同时启动两者。然后在浏览器中访问http://localhost:3000/todo你应该能看到从后端获取到的 todos。小技巧你可以像 后端 Golden Path 教程 中演示的那样用curl创建 todos然后刷新前端页面查看它们是否出现curl -X POST http://localhost:7007/api/todo/todos \ -H Content-Type: application/json; charsetutf-8 \ -H Authorization: Bearer $(curl -s http://localhost:7007/api/auth/guest/refresh | jq -r .backstageIdentity.token) \ --data-binary - EOF { title: My Todo } EOF注意POST 创建接口会校验创建者身份未携带Authorization: Bearer token的请求会返回 401。前端通过fetchApi请求时会自动完成这一步凭证注入这正是它省心的地方。将请求逻辑提取为客户端类当插件拥有多个后端端点时把所有请求逻辑堆在组件里会让组件逐渐臃肿。提取一个专门的客户端类能让组件只专注于渲染。创建plugins/todo/src/api/TodoClient.tsimport { FetchApi } from backstage/frontend-plugin-api; import type { TodoItem } from ../components/TodoList; export class TodoClient { readonly #fetchApi: FetchApi; constructor(options: { fetchApi: FetchApi }) { this.#fetchApi options.fetchApi; } async listTodos(): PromiseTodoItem[] { const response await this.#fetchApi.fetch(plugin://todo/todos); if (!response.ok) { throw new Error( Failed to fetch todos: ${response.status} ${response.statusText}, ); } const data await response.json(); return data.items; } async createTodo(title: string): PromiseTodoItem { const response await this.#fetchApi.fetch(plugin://todo/todos, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ title }), }); if (!response.ok) { throw new Error( Failed to create todo: ${response.status} ${response.statusText}, ); } return response.json(); } }这个类值得注意的细节通过构造函数注入FetchApi依赖注入而非在类内部自行fetch这保证了认证注入与plugin://解析能力被统一复用使用 ES 私有字段#fetchApi封装依赖避免外部意外访问每个方法都显式检查response.ok并在失败时抛出带状态码和状态文本的错误信息便于排查问题createTodo展示了写操作的标准写法method: POST、Content-Type: application/json、序列化的body。对于脚手架的示例插件来说这一步是可选的但随着插件规模增长它会变得非常有价值——所有端点的调用契约都集中在一处组件的可读性和可测试性都会明显提升。底层原理plugin:// 是如何被解析的plugin://todo/todos之所以能工作是因为默认的FetchApi实现内部串联了若干FetchMiddleware中间件。在 packages/core-app-api/src/apis/implementations/FetchApi/FetchMiddlewares.ts 中可以看到三类常用中间件resolvePluginProtocol负责plugin://到真实 HTTP(S) 地址的翻译injectIdentityAuth负责在用户登录时注入Authorization: Bearer token请求头clarifyFailures把笼统的TypeError: Failed to fetch替换为包含请求细节、更易排查的错误信息。plugin://的解析逻辑在 PluginProtocolResolverFetchMiddleware.ts 中实现。它的大致流程是检查请求 URL 是否以plugin://开头不是则原样透传将plugin://todo/todos按 URL 规则拆解出hostname即todo与pathname即/todos调用discoveryApi.getBaseUrl(todo)查询该插件在当前实例中的基地址例如https://backstage.example.net/api/todo把基地址与路径拼接最终请求变为https://backstage.example.net/api/todo/todos。正如 FetchMiddlewares.ts 注释中给出的示例请求plugin://catalog/entities?filterxy时discovery API 会为catalog返回https://backstage.example.net/api/catalog最终请求即为https://backstage.example.net/api/catalog/entities?filterxy。这也解释了为什么你不需要在插件代码中硬编码任何后端地址——实例的部署环境差异端口、路径前缀、跨域反向代理等全部由 discovery 机制透明消化。OpenAPI 生成客户端让前后端契约自动同步如果你希望前后端长期保持一致可以在后端插件暴露 OpenAPI 规范详见 后端 Golden Path 001的前提下直接从该规范生成前端客户端。这样每当 API 发生变化客户端也会自动更新从源头降低前后端随时间漂移drifting apart的风险。仓库的 generate-client.md 描述了标准生成流程要点如下前提条件将 OpenAPI 文件的info.title设置为你的插件 ID例如info: # 你的 pluginId title: catalog准备一个用于承载生成客户端的插件包当前工具只生成客户端文件不支持直接生成全新插件包。生成客户端在仓库根目录执行yarn backstage-repo-tools package schema openapi generate --client-package directory该命令会在directory/src/schema/openapi/generated下生成新的内容其中包括DefaultApiClient——访问具体 OpenAPI 规范的标准客户端各类请求/响应类型——从src/schema/openapi/generated/index.ts统一导出命名与规范中的定义保持一致。实践中你不需要从generated父目录的子文件夹中单独导入任何内容index.ts已经暴露了全部所需。把生成的客户端与前面提到的TodoClient思路结合就可以得到类型安全、自动同步且仍可统一注入认证与协议解析的完整数据访问层。总结与延伸围绕一条fetch调用Backstage 实际上沉淀了一整套约定统一入口fetchApiRef包装浏览器fetch自动完成认证注入与plugin://协议解析FetchApi.ts透明寻址plugin://pluginId通过 discovery 机制解析为实例真实地址插件代码无需感知部署细节PluginProtocolResolverFetchMiddleware.ts渐进增强从组件内联 hook → 独立客户端类 → OpenAPI 自动生成客户端复杂度随插件规模平滑演进。当你继续深入时可以接着阅读 005 - Testing 了解如何用 Jest、React Testing Library 和 MSWMock Service Worker对使用了fetchApi的组件进行单元测试与请求拦截让这套 HTTP 层在测试中同样可靠。【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考