@tni/http
v2.0.1
Published
基于 Axios 的 HTTP 客户端:拦截器、上传下载、SSE、预设响应处理
Downloads
40
Readme
@tni/http
基于 axios 的模块化 HTTP 客户端封装,面向前端应用的 API 层统一收敛请求实例、拦截器、上传下载、SSE、业务响应解包和结构化错误。
设计目标
- 保持 axios 原生配置兼容:
HttpClientOptions继承CreateAxiosDefaults,单次请求使用HttpClientConfig。 - 让能力模块边界清晰:主客户端只负责编排,上传、下载、SSE、拦截器管理分别放在
modules/*。 - 保持可维护可扩展:默认配置、工厂、错误模型、header 合并、params 序列化工具均独立维护。
- 不绑定 UI 框架:错误提示通过
makeErrorMessage回调交给业务层实现 Toast、Message 或日志。 - axios 作为 peer dependency:宿主项目统一控制 axios 版本,包本身只封装项目协议。
目录结构
src/
index.ts # npm 包统一入口
core/
client-contract.ts # 模块依赖的最小客户端契约
defaults.ts # DEFAULT_HTTP_CLIENT_OPTIONS
factory.ts # createHttpClient / defHttp
http-client.ts # 主客户端编排
interceptors/
interceptor-manager.ts # 拦截器注册、移除、复用
preset-interceptors.ts # 常用响应拦截器预设
modules/
file-downloader.ts # 下载能力
file-uploader.ts # 上传能力
sse.ts # fetch 版 SSE
shared/
axios-augment.ts # axios 模块扩展
errors.ts # HttpResponseError
types.ts # 对外类型
utils/
headers.ts # header 归一化与合并
params-serializer.ts # qs 数组序列化策略
merge-options.ts # 默认配置合并快速开始
import {
createHttpClient,
defaultResponseInterceptor,
errorMessageResponseInterceptor,
} from "@tni/http";
export const http = createHttpClient(
{
baseURL: "/api",
timeout: 10_000,
},
(client) => {
client.addRequestInterceptor({
fulfilled: (config) => {
const token = localStorage.getItem("token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
});
client.addResponseInterceptor(
defaultResponseInterceptor({
codeField: "code",
dataField: "data",
successCode: 0,
}),
);
client.addResponseInterceptor(errorMessageResponseInterceptor((msg) => console.error(msg)));
},
);const user = await http.get<{ id: number; name: string }>("/users/me", {
responseReturn: "data",
});核心 API
createHttpClient(options?, setup?):推荐的实例创建方式,适合在应用 API 层统一注册 baseURL、鉴权和响应协议。new HttpClient(options?):类方式创建实例,适合需要继承或测试时精细控制。defHttp:默认实例,适合轻量场景;复杂应用建议自行创建实例。client.instance:底层 axios 实例,保留原生扩展能力。client.addRequestInterceptor(config)/client.addResponseInterceptor(config):注册拦截器并返回 id。client.ejectRequestInterceptor(id)/client.ejectResponseInterceptor(id):按 id 移除拦截器。client.clearRequestInterceptors()/client.clearResponseInterceptors():清空当前实例拦截器,主要用于测试或实例销毁。client.get/post/put/delete/request:标准 HTTP 请求入口。client.upload(url, data, config?):multipart 上传,内部构造FormData。client.download(url, config?):Blob 下载,默认responseType: "blob"。client.requestSSE(url, data?, options?)/client.postSSE(url, data?, options?):fetch 版流式读取,同时复用请求拦截器注入的 header。
响应处理
默认 HttpClient 不做业务解包,返回完整 AxiosResponse。如需按 { code, data, message } 协议返回业务数据,注册 defaultResponseInterceptor 并在单次请求里声明 responseReturn: "data"。
client.addResponseInterceptor(
defaultResponseInterceptor({
codeField: "code",
dataField: "data",
successCode: 0,
}),
);
const list = await client.get("/users", { responseReturn: "data" });如果业务响应不满足成功协议,会抛出 HttpResponseError,可读取 code、data 和 response。
import { HttpResponseError } from "@tni/http";
try {
await client.get("/users", { responseReturn: "data" });
} catch (error) {
if (error instanceof HttpResponseError) {
console.error(error.code, error.message, error.data);
}
}参数序列化
paramsSerializer 支持 axios 原生函数,也支持内置策略:
brackets:ids[]=1&ids[]=2indices:ids[0]=1&ids[1]=2repeat:ids=1&ids=2comma:ids=1,2
await client.get("/users", {
params: { ids: [1, 2, 3] },
paramsSerializer: "brackets",
});推荐接入方式
- 在业务应用的
src/api/client.ts创建并导出唯一或少量HttpClient实例。 - 请求鉴权、业务码解包、错误提示统一放在实例拦截器,不分散写到页面组件。
- 页面和 store 只调用
src/api/*的业务函数,不直接依赖 axios 或拼接基础 URL。 - 上传、下载、SSE 优先复用本包模块方法,确有特殊协议时再访问
client.instance。
验证
vp run http#typecheck
vp test packages/http
vp check packages/http