axios-easy
axios-easy 是一个模块化的 TypeScript 库,它通过一系列可组合的拦截器和实用程序扩展了 axios ,以标准化 HTTP 请求/响应处理。该库提供了即插即用的拦截器,用于处理常见的 Web 应用程序问题,包括身份验证、错误处理、负载规范化、参数序列化和文件处理。
目录
✨ 特性
- 🔌 高度可组合: 提供独立的拦截器,你可以像乐高积木一样按需组合,只添加你需要的功能。
- 🌳 Tree-Shakable: 所有工具和拦截器都支持按需加载,确保最终打包体积最小化。
- 🚀 功能强大: 内置认证、请求参数格式化、响应格式化、错误处理、请求重试(集成的第三方)、文件下载等常用场景的最佳实践。
- 🌐 国际化支持: 错误信息拦截器内置中英文国际化支持,支持全局语言管理和动态切换。
- 💧 类型友好: 使用 TypeScript 编写,提供完整的类型定义,带来卓越的开发体验。
- 👌 使用简单: API 设计简洁直观,只需几行代码即可集成到你的项目中,没有其他黑科技,只是通过
axios 拦截器来实现,源码简单易懂。
- 🧪 单元测试: 所有功能都有单元测试覆盖,确保功能稳定可靠。
📦 安装
pnpm install axios-easy
npm install axios-easy
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';
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 = axios.create({
baseURL: 'https://api.example.com',
responseReturn: 'body',
errorMessageMode: 'message',
timeout: 30 * 1000,
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
});
axiosInstance.interceptors.request.use((config) => {
const token = localStorage.getItem('access_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
createDefaultRequestInterceptor(axiosInstance, {
extendTimeoutWhenDownload: true,
normalizePayload: {
trim: true,
dropUndefined: true,
emptyStringToNull: true,
},
});
createDefaultResponseInterceptor(axiosInstance, {
codeField: 'resultCode',
dataField: 'data',
successCode: 'SUCCESS',
isThrowWhenFail: true,
});
createAuthenticateInterceptor(axiosInstance, {
isAuthenticateFailed: (error: AxiosError<ApiResponse<any>>) => {
return error.response?.status === 401 || AUTH_ERROR_CODES.includes(errorCode!)
},
doReAuthenticate: async (error: AxiosError<Response<any>, any>) => {
const { errorCode } = error.response?.data || {}
window.location.href = '/login';
},
enableRefreshToken: true,
doRefreshToken: async () => {
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);
},
});
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 || '未知错误';
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',
});
async function getUserInfo() {
try {
const userInfo = await axiosInstance.get<ApiResponse<{ id: number; name: string }>>('/user/info', {
errorMessageMode: 'modal',
});
console.log(userInfo);
} 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,
},
paramsSerializer: false,
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) => {
client.interceptors.request.use((config) => {
const token = localStorage.getItem('access_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
},
});
async function getUserInfo() {
try {
const userInfo = await axiosInstance.get<ApiResponse<{ id: number; name: string }>>('/user/info', {
errorMessageMode: 'modal',
});
console.log(userInfo);
} catch (error) {
console.error('获取用户信息失败');
}
}
async function getPetInfo() {
const petInfo = await request('/api/pet/1', {
errorMessageMode: 'modal',
});
console.log(petInfo);
}
📚 API 文档
axios-easy/default-request-interceptor
source
此拦截器用于优化请求行为。
功能:
- 下载场景下延长 timeout: 当检测到请求是用于下载文件时(
responseType 为 'blob' 或 'arraybuffer'),会自动延长该请求的超时时间(默认为基础超时时间的 10 倍),防止因文件过大导致下载超时;如需更灵活的策略,可传入函数接收默认超时时间并返回定制值。
- 请求参数规范化: 支持在发送请求前对
data 和 params 进行统一的数据清洗,包括字符串 trim、删除 undefined 值、空字符串转 null 等操作。
配置选项 (DefaultRequestInterceptorOptions):
import type { InternalAxiosRequestConfig } from 'axios';
export type DefaultRequestInterceptorOptions = {
extendTimeoutWhenDownload?: boolean | ((defaultTimeout: number, config: InternalAxiosRequestConfig) => number);
normalizePayload?: {
trim?: boolean;
dropUndefined?: boolean;
emptyStringToNull?: boolean;
};
};
类型扩展:
此拦截器会为 AxiosRequestConfig 扩展一个新的属性:
interface AxiosRequestConfig {
normalizePayload?: {
trim?: boolean;
dropUndefined?: boolean;
emptyStringToNull?: boolean;
};
}
使用:
import { createDefaultRequestInterceptor } from 'axios-easy/default-request-interceptor';
createDefaultRequestInterceptor(axiosInstance, {
extendTimeoutWhenDownload: true,
normalizePayload: {
trim: true,
dropUndefined: true,
emptyStringToNull: true,
},
});
axiosInstance.post('/api/users',
{
name: ' Alice ',
age: undefined,
email: ' '
},
{
normalizePayload: {
trim: true,
dropUndefined: true,
emptyStringToNull: true,
}
}
);
axios-easy/createRequestClient
source
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 使用默认行为;传入对象或工厂函数则进入细粒度自定义,使默认体验与灵活拓展兼容。
axios-easy/default-response-interceptor
source
此拦截器用于标准化响应数据结构,让你在业务代码中只关心核心数据。
功能:
- 自动解包: 根据后端返回的结构,自动提取核心业务数据。
- 业务成功判断: 根据你定义的成功码(
successCode),判断业务请求是否成功。如果失败,则抛出错误,交由后续的错误拦截器处理。
- 灵活的返回类型: 通过在请求
config 中设置 responseReturn,可以控制返回的数据格式:
'raw': 返回原始的 Axios 响应对象。
'body': 返回响应体 response.data。
'data': 返回响应体中的核心数据字段 response.data[dataField]。
配置选项 (DefaultResponseInterceptorOptions):
export type DefaultResponseInterceptorOptions = {
codeField: string;
dataField: ((response: any) => any) | string;
successCode: ((code: any) => boolean) | number | string;
isThrowWhenFail?: boolean;
};
类型扩展:
此拦截器会为 AxiosRequestConfig 扩展一个新的属性:
interface AxiosRequestConfig {
responseReturn?: 'body' | 'data' | 'raw';
}
使用:
import { createDefaultResponseInterceptor } from 'axios-easy/default-response-interceptor';
createDefaultResponseInterceptor(axiosInstance, {
codeField: 'resultCode',
dataField: 'body',
successCode: 'SUCCESS',
isThrowWhenFail: true,
});
注意
默认的配置(isThrowWhenFail: true),接口的业务错误(resultCode: 'FAIL')会被 throw,交由后续的错误拦截器处理,你无需手动处理。
优点:
简化业务代码:业务层面只需要关注成功的逻辑
统一错误处理:所有错误都在拦截器中统一处理
提高可维护性:错误处理逻辑集中,易于修改和扩展
符合开发规范:遵循了单一职责和关注点分离原则
简单的使用示例:
try {
const res = await axiosInstance.get('/api/user/info');
console.log(res.data);
} catch (err) {
console.error(err);
}
如果个别接口你不使用默认的错误处理,可以在请求配置中设置 errorMessageMode: 'none'
try {
const res = await axiosInstance.get('/api/user/info', {
errorMessageMode: 'none',
});
console.log(res.data);
} catch (err) {
if (isServerError(err) && err.errorCode === 'Expired') {
console.log('操作已过期');
} else {
console.error(err);
}
}
为了 ts 类型友好,判断是否是后端错误,可以参考如下示例封装一个 utils 函数:
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')
);
}
axios-easy/error-message-interceptor
source
此拦截器用于统一捕获和处理所有请求错误,并提供友好的错误提示。
功能:
- 标准化错误信息: 将网络错误、超时、HTTP 错误(4xx, 5xx)等转化为用户易于理解的提示信息。
- 国际化支持: 支持中英文错误信息,提供全局语言管理和请求级别语言设置。
- 自定义处理: 你需要提供一个处理函数,来自定义如何显示错误信息(例如使用
Message 或 Modal 组件)。
配置选项 (ErrorMessageInterceptorOptions):
export type ErrorMessageInterceptorOptions = {
handler: HandleErrorMessage;
defaultLanguage?: SupportedLanguage;
}
类型扩展:
此拦截器会为 AxiosRequestConfig 扩展新的属性:
interface AxiosRequestConfig {
errorMessageMode?: 'message' | 'modal' | 'none';
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 || '未知错误';
console.error(errorMessage);
if (error.config?.errorMessageMode === 'modal') {
} else {
}
}
});
国际化使用:
import { createErrorMessageInterceptor, setGlobalLanguage } from 'axios-easy/error-message-interceptor';
setGlobalLanguage('en');
createErrorMessageInterceptor(axiosInstance, {
handler: (error, networkErrMsg) => {
console.error(networkErrMsg);
}
});
try {
const response = await axiosInstance.get('/api/data', {
errorMessageLanguage: 'zh'
});
} catch (error) {
}
function switchLanguage(newLanguage: 'zh' | 'en') {
setGlobalLanguage(newLanguage);
}
axios-easy/authenticate-interceptor
source
这是一个认证处理拦截器,专门用于处理登录状态失效(如 401)和 Token 自动续期。
功能:
- 认证失败处理: 当
isAuthenticateFailed 函数返回 true 时,执行 doReAuthenticate 操作,例如强制用户登出或跳转到登录页。
- 无感刷新 Token:
- 当认证失败时,如果
enableRefreshToken 为 true,它会暂停所有新的请求。
- 调用你提供的
doRefreshToken 函数来获取新的 Token。
- 刷新成功后,自动用新 Token 重放刚才失败的请求以及所有被暂停的请求。
- 如果刷新失败,则执行
doReAuthenticate。
配置选项 (AuthenticateInterceptorOptions):
export type AuthenticateInterceptorOptions = {
isAuthenticateFailed: (error: AxiosError) => boolean;
doReAuthenticate: (error: AxiosError) => Promise<void>;
enableRefreshToken: boolean;
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', { ... });
localStorage.setItem('token', res.data.token);
},
});
axios-easy/params-serializer-interceptor
source
参数序列化请求拦截器,内部使用 qs 库对请求参数进行序列化,特别适用于需要发送 application/x-www-form-urlencoded 格式数据的场景。一般不需要使用。
类型扩展:
此拦截器会为 AxiosRequestConfig 扩展一个新的属性:
interface AxiosRequestConfig {
qsStringifyArrayFormat?: 'brackets' | 'indices' | 'repeat' | 'comma';
}
配置选项 (ParamsSerializerInterceptorOptions):
export type ParamsSerializerInterceptorOptions = {
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';
const interceptorId = createParamsSerializerInterceptor(axiosInstance, {
qsStringifyArrayFormat: 'brackets'
});
axiosInstance.get('/api/users', {
params: {
tags: ['frontend', 'backend'],
active: true,
page: 1
}
});
axiosInstance.get('/api/search', {
params: {
categories: ['tech', 'news']
},
qsStringifyArrayFormat: 'comma'
});
createParamsSerializerInterceptor(axiosInstance);
axiosInstance.interceptors.request.eject(interceptorId);
使用场景:
- 与传统后端接口对接,需要发送
application/x-www-form-urlencoded 格式数据
- 后端对数组参数有特定格式要求
- 需要序列化复杂的嵌套对象参数
- 不同接口需要不同的参数序列化格式
axios-easy/utils
提供一些在网络请求中非常实用的辅助函数。
processFileStream(response, options) source
处理文件下载流的核心函数。它能智能判断响应是文件流还是包含错误信息的 JSON。
- 如果成功 (文件流): 自动从
content-disposition 头获取文件名,并调用 file-saver 库的 saveAs 触发浏览器下载(比简单的通过 a 标签下载兼容性更好)。
- 如果失败 (JSON): 解析 JSON 中的错误信息并返回。
配置选项 (ProcessFileStreamOptions):
export type ProcessFileStreamOptions = {
fileName?: string;
errorMessageField?: string;
};
使用:
import { processFileStream } from 'axios-easy/utils';
async function handleExport() {
try {
const response = await axiosInstance.get('/api/export-file', {
responseType: 'blob',
responseReturn: 'raw',
});
const errMsg = await processFileStream(response, { errorMessageField: 'errorCodeDes' });
if (errMsg) {
console.error(errMsg);
} else {
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";
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)}`);
console.log(`Header 2: ${getFilenameFromContentDisposition(header2)}`);
console.log(`Header 3: ${getFilenameFromContentDisposition(header3)}`);
console.log(`Header 4: ${getFilenameFromContentDisposition(header4)}`);
console.log(`Header 5: ${getFilenameFromContentDisposition(header5)}`);
saveAs(blob, fileName) source
重新导出了 file-saver 库的 saveAs 函数,方便实现文件下载,比简单的通过 a 标签下载兼容性更好。
import { saveAs } from 'axios-easy/utils';
const blob = new Blob(["Hello, world!"], {type: "text/plain;charset=utf-8"});
saveAs(blob, "hello world.txt");
saveAs("https://httpbin.org/image", "image.jpg");
const canvas = document.getElementById("my-canvas");
canvas.toBlob(function(blob) {
saveAs(blob, "pretty image.png");
});
normalizeRequestPayload(payload, options) source
规范化请求负载(对象/数组),便于在发起请求前统一清洗数据。
- 字符串 trim:默认启用,去除首尾空白。
- 删除 undefined:可选,删除对象中值为
undefined 的键,且会从数组中移除 undefined 元素。
- 空串转 null:可选,将空字符串(trim 后为空)转换为
null。
- 安全类型:不会修改非普通对象,如
Date、FormData、Blob、File、URLSearchParams、Buffer、Stream 等。
配置选项 (NormalizeRequestPayloadOptions):
export type NormalizeRequestPayloadOptions = {
trim?: boolean;
dropUndefined?: boolean;
emptyStringToNull?: boolean;
};
使用示例:
import { normalizeRequestPayload } from 'axios-easy/utils';
const input1 = { name: ' Alice ', nick: undefined, arr: [' a ', 'b', undefined] };
normalizeRequestPayload(input1);
normalizeRequestPayload(input1, { dropUndefined: true });
normalizeRequestPayload({ a: ' ', b: '', c: ' x ' }, { emptyStringToNull: true });
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;
});
个人特定工作场景使用,无需参考。
感谢
参考了 vue-vben-admin 的 request 实现。
🤝 贡献
欢迎提交 PR 和 Issue!
本仓库使用 pnpm 管理 node 和 pnpm 版本,请确保你使用的是 pnpm v10 以上
📄 许可证
MIT