
Product
Introducing Webhook Events for Alert Changes
Add real-time Socket webhook events to your workflows to automatically receive software supply chain alert changes in real time.
axios-easy
Advanced tools
axios-easy 是一个模块化的 TypeScript 库,它通过一系列可组合的拦截器和实用程序扩展了 axios ,以标准化 HTTP 请求/响应处理。该库提供了即插即用的拦截器,用于处理常见的 Web 应用程序问题,包括身份验证、错误处理、负载规范化、参数序列化和文件处理。
axios 拦截器来实现,源码简单易懂。pnpm install axios-easy
# or
npm install axios-easy
# or
yarn add axios-easy
PS: axios 版本最好 1.12.0 以上,否则 ts 类型会报错。
graph TD
subgraph 发起请求前
modify_request_header[<i class='fa fa-edit'></i> 修改请求头]
configure_user_id[<i class='fa fa-user'></i> 配置用户标识]
serialize_params[<i class='fa fa-wrench'></i> 参数序列化,非必需]
end
subgraph 请求处理后
handle_network_error[<i class='fa fa-unlink'></i> 网络错误处理]
handle_authorization_error[<i class='fa fa-shield'></i> 授权错误处理]
retry_on_exception[<i class='fa fa-refresh'></i> 异常请求重试]
handle_general_error[<i class='fa fa-exclamation-triangle'></i> 普通错误处理]
end
call_function[<i class='fa fa-cogs'></i> 调用请求函数] --> handle_params[<i class='fa fa-sliders'></i> 请求参数处理]
handle_params --> modify_request_header
handle_params --> serialize_params
serialize_params --> configure_user_id
configure_user_id --> initiate_request[<i class='fa fa-paper-plane'></i> 发起请求]
modify_request_header --> configure_user_id
initiate_request --> handle_network_error
handle_network_error --> handle_authorization_error
handle_authorization_error --> retry_on_exception
retry_on_exception --> handle_general_error
handle_general_error --> request_completed[<i class='fa fa-check-circle'></i> 请求完成]
request_completed --> return_params[<i class='fa fa-sliders'></i> 返回参数处理]
classDef normal fill:#fff,stroke:#44b6a9,stroke-width:2px,color:#333
classDef subprocess fill:#fff,stroke:#44b6a9,stroke-width:2px,color:#333
classDef box fill:#e0f5f5,stroke:#44b6a9,stroke-width:2px,stroke-dasharray: 5 5
class call_function,handle_params,modify_request_header,configure_user_id,initiate_request,handle_network_error,handle_authorization_error,retry_on_exception,handle_general_error,request_completed,return_params normal
class 发起请求前,请求处理后 box
搭积木一样,根据你需要的拦截器按需导入。可定制程度较高,也更符合之前的编程习惯:创建 axios 实例,然后添加各种拦截器。
下面是一个集成了所有核心拦截器的示例,展示了 axios-easy 的使用方法,这是一个比较完整的示例,你简单修改后可以直接使用。
tr
import type { AxiosError, AxiosResponse } from 'axios';
import axios from 'axios';
// 从各个模块按需导入你需要的拦截器创建函数
import { createDefaultRequestInterceptor } from 'axios-easy/default-request-interceptor';
import { createDefaultResponseInterceptor } from 'axios-easy/default-response-interceptor';
import { createAuthenticateInterceptor } from 'axios-easy/authenticate-interceptor';
import { createErrorMessageInterceptor, setGlobalLanguage } from 'axios-easy/error-message-interceptor';
// 使用 qs 库对请求参数进行序列化,这个一般不需要使用,用于发送 application/x-www-form-urlencoded 格式的数据。默认的 application/json 数据(这也是现代 Web 开发中最常见的)就可以了。
// import { createParamsSerializerInterceptor } from 'axios-easy/params-serializer-interceptor';
// 请求重试功能,如果使用,请安装 axios-retry
// import axiosRetry from 'axios-retry';
/** 假设你的接口返回数据结构如下 */
type ApiResponse<T> = {
resultCode: 'SUCCESS' | 'FAIL';
data: T;
errorCode?: string;
errorCodeDes?: string;
};
/** 和后端约定好的登录失效错误码 */
const AUTH_ERROR_CODES = [
'KICK_OUT', // Token已被踢下线
'LOGIN_REPLACE', // 登录被顶下线
'NOT_TOKEN',
'TOKEN_DEFAULT_ERROR', // 当前会话未登录
'TOKEN_TIMEOUT', // Token 已过期
]
// 设置错误信息语言(可选,默认中文)
// setGlobalLanguage('en'); // 设置为英文
// 创建 Axios 实例
const axiosInstance = axios.create({
baseURL: 'https://api.example.com',
responseReturn: 'body',
errorMessageMode: 'message',
timeout: 30 * 1000,
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
});
// 添加你自己的业务请求拦截器 (例如:添加 token)
axiosInstance.interceptors.request.use((config) => {
const token = localStorage.getItem('access_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 应用参数序列化拦截器 (可选,需要发送 application/x-www-form-urlencoded 数据时使用)
// createParamsSerializerInterceptor(axiosInstance, {
// qsStringifyArrayFormat: 'brackets' // 按需选择,不传也行,默认使用 indices 格式。
// });
// 应用默认请求拦截器
createDefaultRequestInterceptor(axiosInstance, {
extendTimeoutWhenDownload: true, // 下载文件时自动延长超时时间(默认超时时间 * 10),防止因文件过大导致下载超时
normalizePayload: {
trim: true, // 去除字符串首尾空白
dropUndefined: true, // 删除 undefined 值
emptyStringToNull: true, // 空字符串转为 null
},
});
// 应用默认响应拦截器 (处理 response 数据结构)
createDefaultResponseInterceptor(axiosInstance, {
codeField: 'resultCode',
dataField: 'data',
successCode: 'SUCCESS',
isThrowWhenFail: true, // 当业务请求失败时(状态码不匹配 `successCode`),是否抛出错误。设置为 `true` 后,业务错误将进入 `catch` 块。
});
// 认证拦截器, 支持无感刷新 token。用于登录失效
createAuthenticateInterceptor(axiosInstance, {
isAuthenticateFailed: (error: AxiosError<ApiResponse<any>>) => {
// 自定义逻辑判断登录是否失效
return error.response?.status === 401 || AUTH_ERROR_CODES.includes(errorCode!)
},
doReAuthenticate: async (error: AxiosError<Response<any>, any>) => {
// 登录失效或刷新 token 失败后的行为,一般是跳转登录页面
const { errorCode } = error.response?.data || {} // 可以针对后端返回的错误码进行不同处理
window.location.href = '/login';
},
enableRefreshToken: true, // 启用无感刷新 token 功能
doRefreshToken: async () => {
// 实现刷新 token 的逻辑
const res = await axiosInstance.post('/refresh-token', {
refreshToken: localStorage.getItem('refresh_token'),
});
const { token, refreshToken } = res.data;
localStorage.setItem('access_token', token);
localStorage.setItem('refresh_token', refreshToken);
},
});
// 请求重试,如果需要使用,请安装 axios-retry, 参考:https://github.com/softonic/axios-retry
// axiosRetry(axiosInstance, {
// retries: 3
// })
// 应用错误消息拦截器 (统一错误提示, 在这里定义业务错误提示)
createErrorMessageInterceptor(axiosInstance, {
handler: (errorResponse: AxiosResponse<ApiResponse<any>>, networkErrMsg) => {
if (!errorResponse.config || !errorResponse.data) {
return;
}
const { data, config } = errorResponse;
// 如果单独配置了不提示错误信息,则直接返回
if (config?.errorMessageMode === 'none') {
return;
}
const errorMessage = data?.errorCodeDes || networkErrMsg || data?.errorCode || '未知错误';
// 这里使用你项目中的 UI 组件库来显示错误,例如 Element Plus
if (config?.errorMessageMode === 'message') {
// 如果没有错误信息,则会根据状态码进行提示
ElMessage({
message: errorMessage,
type: 'error',
plain: true,
grouping: true,
});
} else if (config?.errorMessageMode === 'modal') {
ElMessageBox({
title: '错误提示',
message: errorMessage,
type: 'error',
showCancelButton: false,
confirmButtonText: '知道了',
}).catch(() => { });
}
},
defaultLanguage: 'zh',
});
// 现在,你可以使用配置好的 axiosInstance 发起请求了
async function getUserInfo() {
try {
// 拦截器会自动处理数据,你直接拿到的就是 data 字段(axios 的原始响应对象)的内容
const userInfo = await axiosInstance.get<ApiResponse<{ id: number; name: string }>>('/user/info', {
errorMessageMode: 'modal',
});
console.log(userInfo); // { id: 1, name: 'Alice' }
} catch (error) {
console.error('获取用户信息失败');
}
}
工厂模式,一个函数搞定所有拦截器。根据你的需要开启
createRequestClient 将常用拦截器打包成可配置的工厂,开箱即可获得下载超时扩展、请求体规范化、响应结构转换、认证重试和错误提示等能力,同时又允许你按需拓展,让接入体验与定制能力兼得。
import type { AxiosError, AxiosResponse } from 'axios';
import { createRequestClient } from 'axios-easy/create-request-client';
/** 假设你的接口返回数据结构如下 */
type ApiResponse<T> = {
resultCode: 'SUCCESS' | 'FAIL';
data: T;
errorCode?: string;
errorCodeDes?: string;
};
/** 和后端约定好的登录失效错误码 */
const AUTH_ERROR_CODES = [
'KICK_OUT',
'LOGIN_REPLACE',
'NOT_TOKEN',
'TOKEN_DEFAULT_ERROR',
'TOKEN_TIMEOUT',
];
const { axiosInstance, request, setGlobalLanguage } = createRequestClient({
axiosConfig: {
baseURL: 'https://api.example.com',
responseReturn: 'body',
errorMessageMode: 'message',
timeout: 30 * 1000,
},
// 使用 qs 库对请求参数进行序列化,这个一般不需要使用,用于发送 application/x-www-form-urlencoded 格式的数据。
paramsSerializer: false,
/** 是否启用默认请求拦截器。true 表示使用默认配置(下载延长超时 & 请求体规范化-去除字符串首尾空白开启) */
defaultRequest: true,
defaultResponse: {
codeField: 'resultCode',
dataField: 'data',
successCode: 'SUCCESS',
isThrowWhenFail: true,
},
authenticate: (instance) => ({
isAuthenticateFailed: (error) => {
const errorCode = error.response?.data?.errorCode;
return error.response?.status === 401 || (errorCode ? AUTH_ERROR_CODES.includes(errorCode) : false);
},
doReAuthenticate: async (_error: AxiosError<ApiResponse<any>>) => {
window.location.href = '/login';
},
enableRefreshToken: true,
doRefreshToken: async () => {
const res = await instance.post('/refresh-token', {
refreshToken: localStorage.getItem('refresh_token'),
});
const { token, refreshToken } = res.data;
localStorage.setItem('access_token', token);
localStorage.setItem('refresh_token', refreshToken);
},
}),
errorMessage: {
handler: (errorResponse: AxiosResponse<ApiResponse<any>>, networkErrMsg) => {
if (!errorResponse.config || !errorResponse.data) {
return;
}
if (errorResponse.config.errorMessageMode === 'none') {
return;
}
const errorMessage = errorResponse.data?.errorCodeDes || networkErrMsg || errorResponse.data?.errorCode || '未知错误';
if (errorResponse.config.errorMessageMode === 'message') {
ElMessage({
message: errorMessage,
type: 'error',
plain: true,
grouping: true,
});
} else if (errorResponse.config.errorMessageMode === 'modal') {
ElMessageBox({
title: '错误提示',
message: errorMessage,
type: 'error',
showCancelButton: false,
confirmButtonText: '知道了',
}).catch(() => {});
}
},
defaultLanguage: 'zh',
},
setup: (client) => {
// 这里可以继续挂载第三方插件,例如 axios-retry
// axiosRetry(client, { retries: 3 });
client.interceptors.request.use((config) => {
const token = localStorage.getItem('access_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
},
});
// 可选:设置全局错误提示语言
// setGlobalLanguage('en');
// 现在,你可以使用配置好的 axiosInstance 发起请求了
async function getUserInfo() {
try {
const userInfo = await axiosInstance.get<ApiResponse<{ id: number; name: string }>>('/user/info', {
errorMessageMode: 'modal',
});
console.log(userInfo); // { id: 1, name: 'Alice' }
} catch (error) {
console.error('获取用户信息失败');
}
}
async function getPetInfo() {
const petInfo = await request('/api/pet/1', {
errorMessageMode: 'modal',
});
console.log(petInfo); // { id: 1, name: 'Alice' }
}
此拦截器用于优化请求行为。
功能:
responseType 为 'blob' 或 'arraybuffer'),会自动延长该请求的超时时间(默认为基础超时时间的 10 倍),防止因文件过大导致下载超时;如需更灵活的策略,可传入函数接收默认超时时间并返回定制值。data 和 params 进行统一的数据清洗,包括字符串 trim、删除 undefined 值、空字符串转 null 等操作。配置选项 (DefaultRequestInterceptorOptions):
import type { InternalAxiosRequestConfig } from 'axios';
export type DefaultRequestInterceptorOptions = {
/**
* 当请求是下载文件时(`responseType` 为 `'blob'` 或 `'arraybuffer'`),
* 是否自动延长请求的超时时间,以防止因文件过大导致下载超时。
* @default true
*/
extendTimeoutWhenDownload?: boolean | ((defaultTimeout: number, config: InternalAxiosRequestConfig) => number);
/**
* 是否在请求前规范化传参(仅处理普通对象/数组)
* - trim: 是否去除字符串首尾空白,默认 false
* - dropUndefined: 是否删除值为 undefined 的键/数组元素,默认 false
* - emptyStringToNull: 是否将空字符串转换为 null,默认 false
*/
normalizePayload?: {
trim?: boolean;
dropUndefined?: boolean;
emptyStringToNull?: boolean;
};
};
类型扩展:
此拦截器会为 AxiosRequestConfig 扩展一个新的属性:
interface AxiosRequestConfig {
/**
* 是否在请求前规范化传参(仅处理普通对象/数组)
* - trim: 是否去除字符串首尾空白,默认 false
* - dropUndefined: 是否删除值为 undefined 的键/数组元素,默认 false
* - emptyStringToNull: 是否将空字符串转换为 null,默认 false
*
* 未设置时,等价于 `{ trim: false, dropUndefined: false, emptyStringToNull: false }`
*/
normalizePayload?: {
trim?: boolean;
dropUndefined?: boolean;
emptyStringToNull?: boolean;
};
}
使用:
import { createDefaultRequestInterceptor } from 'axios-easy/default-request-interceptor';
createDefaultRequestInterceptor(axiosInstance, {
extendTimeoutWhenDownload: true, // 布尔值时沿用默认策略:下载请求且未单独设置 timeout 时,将默认超时时间放大 10 倍
normalizePayload: {
trim: true, // 去除字符串首尾空白
dropUndefined: true, // 删除 undefined 值
emptyStringToNull: true, // 空字符串转为 null
},
});
// 也可以在单个请求中配置,会覆盖拦截器的全局配置
axiosInstance.post('/api/users',
{
name: ' Alice ',
age: undefined,
email: ' '
},
{
normalizePayload: {
trim: true,
dropUndefined: true,
emptyStringToNull: true,
}
}
);
// 实际发送的数据为: { name: 'Alice', email: null }
createRequestClient 将常见拦截器组合为一体化工厂:默认开启下载延时和数据规范化,可选启用响应解析、认证刷新、错误提示、参数序列化与 token 注入,并提供 setup 钩子扩展自定义逻辑。
类型定义 (CreateRequestClientOptions):
import type { AxiosInstance, CreateAxiosDefaults } from 'axios';
import type { AuthenticateInterceptorOptions } from 'axios-easy/authenticate-interceptor';
import type { DefaultRequestInterceptorOptions } from 'axios-easy/default-request-interceptor';
import type { DefaultResponseInterceptorOptions } from 'axios-easy/default-response-interceptor';
import type { HandleErrorMessage, SupportedLanguage } from 'axios-easy/error-message-interceptor';
import type { ParamsSerializerInterceptorOptions } from 'axios-easy/params-serializer-interceptor';
type ErrorMessageOptions = {
handler: HandleErrorMessage;
defaultLanguage?: SupportedLanguage;
};
type CreateRequestClientOptions = {
axiosConfig?: CreateAxiosDefaults;
defaultRequest?: boolean | DefaultRequestInterceptorOptions;
defaultResponse?: false | DefaultResponseInterceptorOptions;
authenticate?: false | ((client: AxiosInstance) => AuthenticateInterceptorOptions);
errorMessage?: false | ErrorMessageOptions;
paramsSerializer?: boolean | ParamsSerializerInterceptorOptions;
setup?: (client: AxiosInstance) => void;
};
使用:
import { createRequestClient } from 'axios-easy';
const client = createRequestClient({
defaultRequest: true,
defaultResponse: {
codeField: 'code',
dataField: 'data',
successCode: 0,
},
authenticate: (instance) => ({
enableRefreshToken: false,
doReAuthenticate: async () => {
window.location.href = '/login';
},
}),
setup: (instance) => {
instance.interceptors.request.use((config) => {
console.log('[debug] request url:', config.url);
return config;
});
},
});
const data = await client.get('/api/example');
将配置设为
false可以彻底关闭对应模块;保持true使用默认行为;传入对象或工厂函数则进入细粒度自定义,使默认体验与灵活拓展兼容。
此拦截器用于标准化响应数据结构,让你在业务代码中只关心核心数据。
功能:
successCode),判断业务请求是否成功。如果失败,则抛出错误,交由后续的错误拦截器处理。config 中设置 responseReturn,可以控制返回的数据格式:
'raw': 返回原始的 Axios 响应对象。'body': 返回响应体 response.data。'data': 返回响应体中的核心数据字段 response.data[dataField]。配置选项 (DefaultResponseInterceptorOptions):
export type DefaultResponseInterceptorOptions = {
/**
* 响应数据中代表业务状态码的字段名。
* @default 'code'
*/
codeField: string;
/**
* 响应数据中代表核心业务数据的字段名,或一个函数,用于从响应体中提取数据。
* @default 'data'
*/
dataField: ((response: any) => any) | string;
/**
* 定义业务成功的状态码值。
* 可以是一个具体的值,或一个函数,返回 `true` 表示成功。
* @default 0
*/
successCode: ((code: any) => boolean) | number | string;
/**
* 当业务请求失败时(状态码不匹配 `successCode`),是否抛出错误。
* 设置为 `true` 后,业务错误将进入 `catch` 块。
* @default true
*/
isThrowWhenFail?: boolean;
};
类型扩展:
此拦截器会为 AxiosRequestConfig 扩展一个新的属性:
interface AxiosRequestConfig {
/**
* 响应数据的返回方式。
* - raw: 原始的 AxiosResponse,包括 headers、status 等,不做是否成功请求的检查。(返回 `axiosRes`)
* - body: 返回响应数据的 body 部分。(返回 `axiosRes.data` )
* - data: 解构响应的 body 数据,只返回其中的 dataField 节点数据。(返回 `axiosRes.data.list`)
* @default 'body'
*
* **axiosRes 是 axios 的默认响应对象**
* ```ts
* const axiosRes = {
// `data` 由服务器提供的响应
data: {
code: 0,
list: [],
errorMessage: '',
},
// `status` 来自服务器响应的 HTTP 状态码
status: 200,,
// `headers` 是服务器响应头
headers: {},
...
}
* ```
*/
responseReturn?: 'body' | 'data' | 'raw';
}
使用:
import { createDefaultResponseInterceptor } from 'axios-easy/default-response-interceptor';
// 假设后端接口结构为 { resultCode: 'SUCCESS', data: { ... } }
createDefaultResponseInterceptor(axiosInstance, {
codeField: 'resultCode',
dataField: 'body',
successCode: 'SUCCESS',
isThrowWhenFail: true,
});
注意
默认的配置(isThrowWhenFail: true),接口的业务错误(resultCode: 'FAIL')会被 throw,交由后续的错误拦截器处理,你无需手动处理。
优点: 简化业务代码:业务层面只需要关注成功的逻辑 统一错误处理:所有错误都在拦截器中统一处理 提高可维护性:错误处理逻辑集中,易于修改和扩展 符合开发规范:遵循了单一职责和关注点分离原则
简单的使用示例:
try {
const res = await axiosInstance.get('/api/user/info');
/* 这里只处理成功情况(且只有 res.resultCode === 'SUCCESS' 才会进入),代码更清晰 */
console.log(res.data);
} catch (err) {
/*
* 这里会捕获所有错误:
* 1. 网络错误(404、500等)
* 2. 业务错误(resultCode: 'FAIL')
* 3. 代码执行错误(如 res 不存在)
* 因为没有设置 options.errorMessageMode: 'none',所以错误信息已在拦截器中统一处理和展示
*/
console.error(err);
}
如果个别接口你不使用默认的错误处理,可以在请求配置中设置 errorMessageMode: 'none'
try {
const res = await axiosInstance.get('/api/user/info', {
errorMessageMode: 'none', // // 默认有错误,会在 axios 响应拦截器中直接使用 ElMessage(由 createErrorMessageInterceptor 的配置决定) 提示错误信息。这里设置为 'none',需要自己处理错误
});
/* 这里只处理成功情况(且只有 res.resultCode === 'SUCCESS' 才会进入),代码更清晰 */
console.log(res.data);
} catch (err) {
if (isServerError(err) && err.errorCode === 'Expired') {
// 因为上面设置了 errorMessageMode: 'none',所以这里需要自己处理错误。
console.log('操作已过期');
} else {
// 非后端业务报错,直接打印
console.error(err);
}
}
为了 ts 类型友好,判断是否是后端错误,可以参考如下示例封装一个 utils 函数:
/**
* 判断是否是后端错误 (公司项目约定的统一错误返回格式)
* @param error 错误对象
* @returns 是否是后端错误
*/
export function isServerError(error: any): error is ServerError {
if (typeof error !== 'object' || error === null) {
return false;
}
return (
// 这里根据和后端约定的返回数据结构来判断
Reflect.has(error, 'resultCode') &&
Reflect.has(error, 'errorCode') &&
Reflect.has(error, 'errorCodeDes')
);
}
此拦截器用于统一捕获和处理所有请求错误,并提供友好的错误提示。
功能:
Message 或 Modal 组件)。配置选项 (ErrorMessageInterceptorOptions):
export type ErrorMessageInterceptorOptions = {
/** 自定义错误提示处理函数 */
handler: HandleErrorMessage;
/** 默认语言,默认为中文。如果不提供,将使用全局语言设置 */
defaultLanguage?: SupportedLanguage;
}
类型扩展:
此拦截器会为 AxiosRequestConfig 扩展新的属性:
interface AxiosRequestConfig {
/**
* Error message prompt type。这个提示 ui 需要开发自己定义 client.addResponseInterceptor(errorMessageResponseInterceptor(...))
* - message: 使用 message 提示错误信息, 如 Element Plus 或 antdv 的 message.error
* - modal: 使用 modal 提示错误信息, 如 antdv 的 Modal.error 或 Element Plus 的 ElMessage.error
* - none: 不提示错误信息
* @default 'message'
*/
errorMessageMode?: 'message' | 'modal' | 'none';
/**
* 错误信息语言
* - zh: 中文
* - en: 英文
* 未设置时将使用全局语言设置或拦截器默认语言
*/
errorMessageLanguage?: 'zh' | 'en';
}
基本使用:
你需要传入一个回调函数,该函数接收两个参数:error (Axios 响应对象) 和 networkErrMsg (拦截器生成的标准化错误信息)。
import { createErrorMessageInterceptor } from 'axios-easy/error-message-interceptor';
createErrorMessageInterceptor(axiosInstance, {
handler: (error, networkErrMsg) => {
// 优先使用后端返回的错误描述
const errorMessage = error.data?.errorCodeDes || networkErrMsg || '未知错误';
// 使用你项目的 UI 库进行提示
// ElMessage.error(errorMessage);
console.error(errorMessage);
// 你还可以根据请求配置的 errorMessageMode 来决定提示方式
if (error.config?.errorMessageMode === 'modal') {
// ElMessageBox.alert(errorMessage, '错误');
} else {
// ...其他处理
}
}
});
国际化使用:
import { createErrorMessageInterceptor, setGlobalLanguage } from 'axios-easy/error-message-interceptor';
// 1. 设置全局语言(推荐方式)
setGlobalLanguage('en'); // 设置为英文
createErrorMessageInterceptor(axiosInstance, {
handler: (error, networkErrMsg) => {
console.error(networkErrMsg); // 自动显示英文错误信息
}
});
// 2. 单个请求设置语言
try {
const response = await axiosInstance.get('/api/data', {
errorMessageLanguage: 'zh' // 这个请求使用中文错误信息
});
} catch (error) {
// 错误信息将显示中文
}
// 3. 动态切换语言
function switchLanguage(newLanguage: 'zh' | 'en') {
setGlobalLanguage(newLanguage);
// 后续所有请求的错误信息都会使用新语言
}
这是一个认证处理拦截器,专门用于处理登录状态失效(如 401)和 Token 自动续期。
功能:
isAuthenticateFailed 函数返回 true 时,执行 doReAuthenticate 操作,例如强制用户登出或跳转到登录页。enableRefreshToken 为 true,它会暂停所有新的请求。doRefreshToken 函数来获取新的 Token。doReAuthenticate。配置选项 (AuthenticateInterceptorOptions):
export type AuthenticateInterceptorOptions = {
/**
* 判断当前错误是否为认证失败。
* @param error Axios 错误对象。
* @returns 如果是认证失败,则返回 `true`。
*/
isAuthenticateFailed: (error: AxiosError) => boolean;
/**
* 认证失败且无法恢复(或刷新 Token 失败)后执行的操作。
* 通常用于跳转到登录页。
* @param error Axios 错误对象。
*/
doReAuthenticate: (error: AxiosError) => Promise<void>;
/**
* 是否启用 Token 自动刷新功能。
* @default false
*/
enableRefreshToken: boolean;
/**
* 一个异步函数,用于执行刷新 Token 的具体逻辑。
* 如果刷新成功,函数应正常返回;如果失败,则应抛出异常,以便触发 `doReAuthenticate`。
* @param error Axios 错误对象。
*/
doRefreshToken: (error: AxiosError) => Promise<any>;
};
使用:
import { createAuthenticateInterceptor } from 'axios-easy/authenticate-interceptor';
createAuthenticateInterceptor(axiosInstance, {
isAuthenticateFailed: (error) => error.response?.status === 401,
doReAuthenticate: async () => {
window.location.href = '/login';
},
enableRefreshToken: true,
doRefreshToken: async () => {
const res = await axios.post('/api/refresh-token', { ... });
// 保存新 token
localStorage.setItem('token', res.data.token);
},
});
参数序列化请求拦截器,内部使用 qs 库对请求参数进行序列化,特别适用于需要发送 application/x-www-form-urlencoded 格式数据的场景。一般不需要使用。
类型扩展:
此拦截器会为 AxiosRequestConfig 扩展一个新的属性:
interface AxiosRequestConfig {
/**
* 格式说明:
* - 'brackets': arr[]=1&arr[]=2
* - 'indices': arr[0]=1&arr[1]=2
* - 'repeat': arr=1&arr=2
* - 'comma': arr=1,2
*/
qsStringifyArrayFormat?: 'brackets' | 'indices' | 'repeat' | 'comma';
}
配置选项 (ParamsSerializerInterceptorOptions):
export type ParamsSerializerInterceptorOptions = {
/**
* 全局数组参数序列化格式
* @default 'indices' (qs 库默认格式)
*/
qsStringifyArrayFormat?: 'brackets' | 'indices' | 'repeat' | 'comma';
};
数组序列化格式对比:
假设参数为 { tags: ['frontend', 'backend'] }:
| 格式 | 序列化结果 | 说明 |
|---|---|---|
'brackets' | tags[]=frontend&tags[]=backend | 使用空方括号 |
'indices' | tags[0]=frontend&tags[1]=backend | 使用索引方括号(qs 默认) |
'repeat' | tags=frontend&tags=backend | 重复参数名 |
'comma' | tags=frontend,backend | 逗号分隔 |
使用:
import { createParamsSerializerInterceptor } from 'axios-easy/params-serializer-interceptor';
// 1. 安装拦截器并设置全局配置
const interceptorId = createParamsSerializerInterceptor(axiosInstance, {
qsStringifyArrayFormat: 'brackets' // 全局默认使用 brackets 格式
});
// 2. 普通请求 - 使用全局配置
axiosInstance.get('/api/users', {
params: {
tags: ['frontend', 'backend'], // 序列化为: tags[]=frontend&tags[]=backend
active: true,
page: 1
}
});
// 3. 请求级别配置 - 覆盖全局配置
// qsStringifyArrayFormat 现在是 AxiosRequestConfig 的合法属性
axiosInstance.get('/api/search', {
params: {
categories: ['tech', 'news']
},
qsStringifyArrayFormat: 'comma' // 本次请求使用逗号分隔: categories=tech,news
});
// 5. 不传配置时使用 qs 默认格式 (indices)
createParamsSerializerInterceptor(axiosInstance); // 使用 indices 格式
// 6. 移除拦截器
axiosInstance.interceptors.request.eject(interceptorId);
使用场景:
application/x-www-form-urlencoded 格式数据提供一些在网络请求中非常实用的辅助函数。
processFileStream(response, options) source处理文件下载流的核心函数。它能智能判断响应是文件流还是包含错误信息的 JSON。
content-disposition 头获取文件名,并调用 file-saver 库的 saveAs 触发浏览器下载(比简单的通过 a 标签下载兼容性更好)。配置选项 (ProcessFileStreamOptions):
export type ProcessFileStreamOptions = {
/**
* 自定义文件名。
* 如果提供此选项,将优先使用该文件名,而不是从 `content-disposition` 头中解析。
*/
fileName?: string;
/**
* 当响应体是 JSON 格式的错误信息时,用于提取错误文本的字段名。
* @default 'errorCodeDes'
*/
errorMessageField?: string;
};
使用:
import { processFileStream } from 'axios-easy/utils';
async function handleExport() {
try {
const response = await axiosInstance.get('/api/export-file', {
responseType: 'blob', // 必须指定响应类型
responseReturn: 'raw', // 需要原始响应来获取 headers
});
const errMsg = await processFileStream(response, { errorMessageField: 'errorCodeDes' });
if (errMsg) {
// ElMessage.error(errMsg);
console.error(errMsg);
} else {
// ElMessage.success('导出成功');
console.log('导出成功');
}
} catch (error) {
// 网络等其他错误
}
}
getFilenameFromContentDisposition source从 content-disposition 响应头中安全地解析出文件名。支持 filename*=(RFC-5987) 和 filename= 格式。
使用
import { getFilenameFromContentDisposition } from 'axios-easy/utils';
const fileName = getFilenameFromContentDisposition(response.headers['content-disposition']);
const header1 = "attachment; filename*=UTF-8''%E6%B5%8B%E8%AF%95%E6%96%87%E4%BB%B6.zip"; // 包含中文 "测试文件.zip"
const header2 = 'attachment; filename="a simple file.txt"';
const header3 = 'inline; filename=another-file.pdf';
const header4 = 'form-data; name="file"; filename="report with spaces.docx"';
const header5 = 'attachment; filename="semicolon;.txt"'; // 包含分号的带引号文件名
console.log(`Header 1: ${getFilenameFromContentDisposition(header1)}`); // 输出: Header 1: 测试文件.zip
console.log(`Header 2: ${getFilenameFromContentDisposition(header2)}`); // 输出: Header 2: a simple file.txt
console.log(`Header 3: ${getFilenameFromContentDisposition(header3)}`); // 输出: Header 3: another-file.pdf
console.log(`Header 4: ${getFilenameFromContentDisposition(header4)}`); // 输出: Header 4: report with spaces.docx
console.log(`Header 5: ${getFilenameFromContentDisposition(header5)}`); // 输出: Header 5: semicolon;.txt
saveAs(blob, fileName) source重新导出了 file-saver 库的 saveAs 函数,方便实现文件下载,比简单的通过 a 标签下载兼容性更好。
import { saveAs } from 'axios-easy/utils';
// Saving text
const blob = new Blob(["Hello, world!"], {type: "text/plain;charset=utf-8"});
saveAs(blob, "hello world.txt");
// Saving URLs
saveAs("https://httpbin.org/image", "image.jpg");
// Saving a canvas
const canvas = document.getElementById("my-canvas");
canvas.toBlob(function(blob) {
saveAs(blob, "pretty image.png");
});
normalizeRequestPayload(payload, options) source规范化请求负载(对象/数组),便于在发起请求前统一清洗数据。
undefined 的键,且会从数组中移除 undefined 元素。null。Date、FormData、Blob、File、URLSearchParams、Buffer、Stream 等。配置选项 (NormalizeRequestPayloadOptions):
export type NormalizeRequestPayloadOptions = {
/** 是否去除字符串首尾空白字符 @default true */
trim?: boolean;
/** 是否删除值为 undefined 的键 @default false */
dropUndefined?: boolean;
/** 是否将空字符串转换为 null @default false */
emptyStringToNull?: boolean;
};
使用示例:
import { normalizeRequestPayload } from 'axios-easy/utils';
// 1) 默认行为:仅 trim 字符串,不删除 undefined
const input1 = { name: ' Alice ', nick: undefined, arr: [' a ', 'b', undefined] };
normalizeRequestPayload(input1);
// => { name: 'Alice', nick: undefined, arr: ['a', 'b', undefined] }
// 2) 删除 undefined(对象键与数组元素)
normalizeRequestPayload(input1, { dropUndefined: true });
// => { name: 'Alice', arr: ['a', 'b'] }
// 3) 空字符串转为 null(基于 trim 后判断)
normalizeRequestPayload({ a: ' ', b: '', c: ' x ' }, { emptyStringToNull: true });
// => { a: null, b: null, c: 'x' }
// 4) 结合 axios 使用(对 data 与 params 同步清洗)
axiosInstance.interceptors.request.use((config) => {
if (config.data && typeof config.data === 'object' && !(config.data instanceof FormData)) {
config.data = normalizeRequestPayload(config.data, {
trim: true,
dropUndefined: true,
emptyStringToNull: true,
});
}
if (config.params && typeof config.params === 'object') {
config.params = normalizeRequestPayload(config.params, {
trim: true,
dropUndefined: true,
emptyStringToNull: true,
});
}
return config;
});
// 提示:JSON.stringify 对象属性为 undefined 会被省略,数组中的 undefined 会被序列化为 null;
// 若希望“移除”数组中的 undefined,请使用 dropUndefined: true。
个人特定工作场景使用,无需参考。
参考了 vue-vben-admin 的 request 实现。
欢迎提交 PR 和 Issue!
本仓库使用 pnpm 管理 node 和 pnpm 版本,请确保你使用的是 pnpm v10 以上
FAQs
A simple axios library
We found that axios-easy demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Add real-time Socket webhook events to your workflows to automatically receive software supply chain alert changes in real time.

Security News
ENISA has become a CVE Program Root, giving the EU a central authority for coordinating vulnerability reporting, disclosure, and cross-border response.

Product
Socket now scans OpenVSX extensions, giving teams early detection of risky behaviors, hidden capabilities, and supply chain threats in developer tools.