news 2026/9/11 3:18:57

Halo 前端 API 客户端 @halo-dev/api-client 完全指南:从插件接入到外部项目集成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Halo 前端 API 客户端 @halo-dev/api-client 完全指南:从插件接入到外部项目集成

Halo 前端 API 客户端 @halo-dev/api-client 完全指南:从插件接入到外部项目集成

【免费下载链接】haloHalo 是一款强大易用的开源建站工具,从个人博客、知识库,到企业官网、在线商城,Halo 都能助您轻松实现,一站式满足您的多样化建站需求。项目地址: https://gitcode.com/GitHub_Trending/ha/halo

本指南以 Halo 开源仓库中的 api-client 官方文档 为主体,结合 api-client 包源码、生成配置 与 Console 侧的拦截器实现,系统讲解@halo-dev/api-client的四个预置客户端、四个工厂函数、默认 Axios 实例的底层行为,以及它在 Halo 插件开发与外部第三方项目中的完整接入方案。读完本文,你将掌握如何用一行pnpm install加几个 import 语句,在插件或独立应用中直接调用 Halo 的 CRUD、Console、UC 与公开 API。

一、什么是 @halo-dev/api-client

@halo-dev/api-client是 Halo 2.x 的官方 JavaScript API 客户端请求库,位于仓库 ui/packages/api-client 目录。它本身不是手写的封装,而是基于 OpenAPI 规范、使用 OpenAPI Generator 以typescript-axios模板自动生成的一层类型安全的 HTTP 客户端。

从 package.json 可以看到它的完整元信息:

  • 包名:@halo-dev/api-client,当前仓库版本为2.26.0
  • 许可证:GPL-3.0
  • 模块格式:"type": "module"(ESM),入口为./dist/index.js,类型声明为./dist/index.d.ts
  • 运行时依赖仅qs(用于查询参数序列化),axios被声明为peerDependencies(要求^1.16.0),意味着 axios 由使用方(Console、UC 或插件宿主)统一提供,避免多实例冲突;
  • sideEffects: false,可安全地被摇树优化(tree-shaking)。

1.1 生成链路:从 OpenAPI 到 TypeScript

客户端代码不是手工维护的,其生成命令定义在 package.json 的gen脚本中:

rimraf --glob './src/**' && openapi-generator-cli generate \ -i ../../../api-docs/openapi/v3_0/aggregated.json \ -g typescript-axios \ -c ./.openapi_config.yaml \ -o ./src \ --type-mappings='set=Array' \ --global-property apiDocs=false,modelDocs=false

其中:

  • -i ../../../api-docs/openapi/v3_0/aggregated.json:输入文件是仓库 api-docs/openapi/v3_0/aggregated.json,即 Halo 后端所有分组(apis_consoleapis_extensionapis_publicapis_uc)聚合后的 OpenAPI 描述文件;
  • -g typescript-axios:生成基于 Axios 的 TypeScript 客户端;
  • --type-mappings='set=Array':将 OpenAPI 中的set类型映射为Array
  • --global-property apiDocs=false,modelDocs=false:跳过 API 与模型文档的生成,只产出代码。

生成器版本由 openapitools.json 锁定为7.17.0。生成结果落在src/目录:src/api下是按资源划分的 API 类(如 post-v1alpha1-api.ts、user-v1alpha1-api.ts),src/models下是请求/响应模型,src/index.ts统一导出apiconfigurationmodels

export * from "./api"; export * from "./configuration"; export * from "./models";

1.2 构建产物

构建由 vite.config.ts 驱动(vp pack),关键配置:

  • 入口为./entry/index.ts
  • neverBundle: ["axios"]:axios 不打入产物,交给宿主提供;
  • alwaysBundle: ["qs"]qs序列化逻辑直接打进产物;
  • 同时产出esm与压缩后的iife两种格式,IIFE 全局名为HaloApiClient,方便 CDN 场景直接以<script>引入。

二、九大导出:预置客户端与工厂函数

包的主入口(entry/index.ts)最终导出以下内容:

import { coreApiClient, consoleApiClient, ucApiClient, publicApiClient, createCoreApiClient, createConsoleApiClient, createUcApiClient, createPublicApiClient, axiosInstance, } from "@halo-dev/api-client";
导出说明
coreApiClient为 Halo 所有自定义模型(extension)自动生成的 CRUD 接口封装的 api client
consoleApiClient为 Halo 针对 Console(管理端)提供的接口封装的 api client
ucApiClient为 Halo 针对 UC(用户中心)提供的接口封装的 api client
publicApiClient为 Halo 所有公开访问的接口封装的 api client
createCoreApiClient创建自定义模型 CRUD 接口的 api client,需要传入 axios 实例
createConsoleApiClient创建 Console 接口的 api client,需要传入 axios 实例
createUcApiClient创建 UC 接口的 api client,需要传入 axios 实例
createPublicApiClient创建公开访问接口的 api client,需要传入 axios 实例
axiosInstance包内部默认创建的 axios 实例

其中coreApiClientconsoleApiClientucApiClientpublicApiClient四个预置实例,本质上是createXxxApiClient(defaultAxiosInstance)的结果(见 entry/api-client.ts),它们共享同一个默认 Axios 实例,开箱即用。

2.1 默认 Axios 实例的底层配置

默认实例在 entry/api-client.ts 中创建,理解它对排查请求问题至关重要:

const defaultAxiosInstance = axios.create({ baseURL: "", withCredentials: true, paramsSerializer: (params) => { return QueryString.stringify(params, { arrayFormat: "repeat" }); }, }); defaultAxiosInstance.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
  • baseURL: "":默认实例不写死后端地址。在 Console / UC 中,请求走同源路径,由部署环境决定实际地址;
  • withCredentials: true:跨域请求携带 Cookie,这是登录态(Session)能够生效的关键;
  • paramsSerializer使用qsarrayFormat: "repeat":数组参数会序列化为?a=1&a=2&a=3而不是?a[]=1&a[]=2&a[]=3,与 Halo 后端对列表参数(如分类、标签 ID 集合)的解析方式对齐;
  • 统一附带X-Requested-With: XMLHttpRequest请求头,用于区分 Ajax 请求。

注意:entry/index.ts中还通过export * from "./patch/thumbnail"兼容性地导出了ThumbnailSpecSizeEnum。该枚举已被标记@deprecated(参见 entry/patch/thumbnail.ts),请改用生成产物中的GetThumbnailByUriSizeEnum

三、四类客户端的职责边界与组织结构

四类客户端对应 Halo 后端四组 OpenAPI 定义(可对照 api-docs/openapi/v3_0 下的apis_consoleapis_extensionapis_publicapis_uc四个文件),覆盖四种完全不同的访问场景。

3.1 coreApiClient:自定义模型的 CRUD

createCoreApiClient的实现见 entry/api-client.ts,它按领域分组组织:

  • contentcategorycommentpostreplysinglePagesnapshottag
  • authauthProvideruserConnection
  • storageattachmentgrouppolicypolicyTemplate
  • pluginextensionDefinitionextensionPointDefinitionpluginreverseProxy
  • metricscounter
  • themetheme
  • notificationnotificationnotificationTemplatenotifierDescriptorreasonreasonTypesubscription
  • migrationbackup
  • securitypersonalAccessToken
  • 顶层另有annotationSettingmenumenuItemsettingconfigMapsecretuserroleroleBinding

从源码结构看,contentauthstoragepluginmetricsthemenotificationmigrationsecurity这些命名对应 Halo 的 API 分组前缀(content.halo.runauth.halo.runstorage.halo.runplugin.halo.run等),每个字段对应的类均以V1alpha1Api结尾(例如文章对应PostV1alpha1Api)。这类接口通常面向插件开发者与内部模块,用于对自定义模型做增删改查。

3.2 consoleApiClient:管理端专用接口

createConsoleApiClient见 entry/api-client.ts,它封装了 Console 管理后台独有的能力,例如:

  • systemmigrationuiPlugin
  • storage.attachmentstorage.policy(注意 Console 侧策略类是PolicyAlpha1ConsoleApi);
  • content下的categorycommentreplyindicespostsinglePagetag
  • notification.notifierplugin.plugintheme.themeconfigMap.system等。

这些接口通常依赖管理员权限,返回带权限过滤的数据形态(如列表分页、发布状态管理、系统配置读写)。

3.3 ucApiClient:用户中心专用接口

createUcApiClient见 entry/api-client.ts,服务登录用户本人的内容管理,例如:

  • content.post(如listMyPosts)、content.snapshot
  • security.twoFactorsecurity.personalAccessTokensecurity.device
  • notification.notification
  • user.preferenceuser.currentUser
  • storage.attachment

3.4 publicApiClient:免认证公开接口

createPublicApiClient见 entry/api-client.ts,无需登录即可访问,典型用于门户站点、主题渲染与 SEO 场景:

  • menustatsSystemV1alpha1PublicApi);
  • content.categorycontent.tagcontent.singlePagecontent.post
  • metrics.metricsnotificationindex
  • 顶层comment(原先content.comment已被标记@deprecated,统一改用顶层comment)。

四、在 Halo 插件中使用(推荐方式)

插件是@halo-dev/api-client最主要的使用场景。安装依赖:

pnpm install @halo-dev/api-client axios

由于 Halo 的 Console 与 UC 项目已经引入该包并设置好了 Axios 拦截器(详见下文"拦截器链路"),插件内直接使用预置客户端即可,无需自行创建实例

import { coreApiClient } from "@halo-dev/api-client"; coreApiClient.content.post.listPost().then((response) => { // handle response });

listPost()对应PostV1alpha1Api的列表方法,response.data即分页列表数据(含itemshasNext等字段)。

4.1 产物体积:依赖自动外置

README 特别强调:在@halo-dev/ui-plugin-bundler-kit@2.17.0及以上版本中,打包器已把@halo-dev/api-clientaxios列入排除名单,插件最终产物中的这两项依赖会自动复用 Halo 宿主本身提供的版本,插件作者无需关心产物大小,也不存在多份 axios 实例导致的拦截器失效问题。这与 vite.config.ts 中neverBundle: ["axios"]的设计一脉相承。

五、在外部项目中使用(自定义实例)

如果你在 Halo 之外的独立应用(如自建的管理后台、数据看板、移动端配套服务)中调用 Halo API,需要手动创建 axios 实例并指定后端地址:

pnpm install @halo-dev/api-client axios
import axios from "axios"; const axiosInstance = axios.create({ baseURL: "http://localhost:8090", }); const coreApiClient = createCoreApiClient(axiosInstance); coreApiClient.content.post.listPost().then((response) => { // handle response });

几个关键点:

  • createCoreApiClient(以及其他三个工厂函数)会从传入的axiosInstance.defaults.baseURL中读取地址(见 entry/api-client.ts),因此务必在创建实例时配置baseURL
  • 外部项目通常需要自己处理认证:可通过axiosInstance.interceptors.request.use(...)附加 Token(如 Personal Access Token),或配合withCredentials: true走 Halo 的 Session 认证;
  • 推荐结合拦截器统一处理 401、网络错误与后端ProblemDetail错误体(Halo 后端错误响应遵循 RFC 7807 格式,包含titledetailstatus等字段)。

5.1 分页工具 paginate

包内还提供了一个实用的分页聚合工具,导出自 entry/utils/paginate.ts(由 entry/index.ts 一并导出):

export async function paginate<TParams extends { page?: number }, TItem>( listFn: (params: TParams) => Promise<AxiosResponse<ListResponse<TItem>>>, params?: Omit<TParams, "page"> ): Promise<TItem[]> { const result: TItem[] = []; let page = 1; let hasNext = true; while (hasNext) { const { data } = await listFn({ ...params, page } as TParams); result.push(...data.items); page += 1; hasNext = data.hasNext; } return result; }

它基于 Halo 列表接口统一的响应结构(items+hasNext)逐页拉取全部数据,适合导出、统计等需要全量数据的场景:

import { paginate } from "@halo-dev/api-client"; import { coreApiClient } from "@halo-dev/api-client"; const allPosts = await paginate((params) => coreApiClient.content.post.listPost(params) );

六、拦截器链路:Console/UC 已内置的错误处理

README 提到"已经在 Console 和 UC 项目中引入并设置好了 Axios 拦截器",其真实实现位于 ui/src/setup/setupApiClient.ts。该文件通过setupApiClient()在应用启动时注册响应拦截器,处理策略包括:

  • 请求取消error.code === "ERR_CANCELED"直接透传,不弹任何提示;
  • 网络错误:匹配Network Error或没有error.response时,弹出国际化后的网络错误 Toast;
  • 静默请求:若请求配置了mute标记(errorResponse.config.mute),直接 reject 而不提示,用于后台静默刷新等场景;
  • 401 未授权:弹出"登录已过期"对话框,确认后跳转/login?redirect_uri=...并在登录后回跳原路径;
  • HTML 响应:当响应头content-typetext/html(典型来自反向代理或 WAF 的拦截页)时,用 iframe 弹窗展示原始内容,避免误解析;
  • ProblemDetail 错误体:从errorResponse.data读取title/detail并以 Toast 展示;
  • 兜底:以上都不命中时,展示status: statusText或通用未知错误提示。

因此,插件在 Console/UC 环境内调用coreApiClient等预置客户端时,认证失效、网络异常、后端错误都会得到统一且友好的 UI 反馈,这是"直接使用即可"的底层保障。在外部项目中,建议参照该实现自行注册拦截器,以获得一致的错误体验。

七、常见问题与最佳实践

  1. 何时用预置客户端,何时用工厂函数?在 Console/UC 插件内一律使用coreApiClientconsoleApiClient等预置实例;在外部独立项目中,用createXxxApiClient(你的axios实例)绑定自己的baseURL与认证逻辑。
  2. 为什么数组参数要用arrayFormat: "repeat"因为 Halo 后端对多值查询参数(如?category=1&category=2)按重复键解析,默认实例已内置该序列化行为;自定义实例时建议保持同样的paramsSerializer配置,避免传参被序列化成category[]=1导致后端取不到值。
  3. 升级后ThumbnailSpecSizeEnum失效?它是为兼容旧版本保留的废弃导出(见 entry/patch/thumbnail.ts),请改用GetThumbnailByUriSizeEnum
  4. 保持 axios 单例:由于包将 axios 声明为 peerDependency 且打包时排除,项目中不要重复安装不同版本的 axios,否则拦截器可能注册在"另一份"实例上而失效。
  5. 跟进 API 变更:客户端代码由 OpenAPI 聚合文件生成,后端接口变更后需重新执行pnpm gen(生成命令见 package.json),并同步升级包版本。

八、深入阅读指引

  • 官方使用文档:ui/packages/api-client/README.md
  • 客户端工厂函数与默认实例实现:ui/packages/api-client/entry/api-client.ts
  • 包入口导出(含兼容性导出):ui/packages/api-client/entry/index.ts
  • 分页聚合工具:ui/packages/api-client/entry/utils/paginate.ts
  • 生成配置与构建配置:package.json、openapitools.json、vite.config.ts
  • OpenAPI 聚合描述文件:api-docs/openapi/v3_0/aggregated.json(以及同目录下apis_consoleapis_extensionapis_publicapis_uc分组文件)
  • Console 侧拦截器实现(错误处理参考):ui/src/setup/setupApiClient.ts
  • 插件 UI 资源与打包(依赖外置的配套机制):openspec/specs/ui-plugin-bundler-provider/spec.md

从插件内的一行import,到外部项目的自定义实例,@halo-dev/api-client通过"生成代码 + 预置实例 + 工厂函数"的组合,把 Halo 全部后端能力以类型安全、开箱即用的方式暴露给前端生态。掌握它的导出体系与默认实例行为,是高效开发 Halo 插件与周边应用的第一步。

【免费下载链接】haloHalo 是一款强大易用的开源建站工具,从个人博客、知识库,到企业官网、在线商城,Halo 都能助您轻松实现,一站式满足您的多样化建站需求。项目地址: https://gitcode.com/GitHub_Trending/ha/halo

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

工业PCB视觉检测:YOLO26+大模型融合落地实践

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

作者头像 李华
网站建设 2026/9/11 3:15:43

虚拟机忘记密码?VMware、Linux、Windows重置全攻略

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

作者头像 李华
网站建设 2026/9/11 3:14:58

YOLOv8-seg中文车牌识别:12类全制式实战部署指南

简介&#xff1a;本资源是一套基于YOLOv8的高兼容性中文车牌识别系统&#xff0c;面向人工智能、自动化、电子信息等专业的高校学生及初阶开发者&#xff0c;解决多类型车牌&#xff08;含单双层蓝牌、新能源绿牌、警用车牌、军牌等12类&#xff09;的端到端检测与识别问题&…

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

Redis数据安全加固实战:访问控制、持久化与分布式锁

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

作者头像 李华
网站建设 2026/9/11 3:14:00

容器里跑安卓模拟器:一条命令完成 Docker 部署

容器里跑安卓模拟器&#xff1a;一条命令完成 Docker 部署 【免费下载链接】docker-android Android in docker solution with noVNC supported, video recording and mcp server 项目地址: https://gitcode.com/GitHub_Trending/do/docker-android 不想在宿主机装整套 …

作者头像 李华