#Axios 通用封装精简版
#代码(可直接用)
src/utils/request.ts
import axios, {
AxiosError,
AxiosRequestConfig,
InternalAxiosRequestConfig,
} from 'axios'
/** 后端统一响应结构(按项目实际调整) */
export interface ApiResponse<T = unknown> {
code: number
message: string
data: T
}
/** 只保留一个扩展字段:是否跳过 token */
export interface RequestConfig extends AxiosRequestConfig {
ignoreToken?: boolean
}
const TOKEN_KEY = 'token'
const SUCCESS_CODES = new Set([0, 200])
const HTTP_ERROR_MAP: Record<number, string> = {
400: '请求参数错误',
401: '登录状态已失效',
403: '无权限访问',
404: '请求资源不存在',
409: '请求冲突',
422: '参数校验失败',
429: '请求过于频繁',
500: '服务器内部错误',
502: '网关错误',
503: '服务不可用',
504: '网关超时',
}
const isApiResponse = (data: unknown): data is ApiResponse => {
return (
!!data &&
typeof data === 'object' &&
'code' in data &&
'message' in data &&
'data' in data
)
}
export const getToken = (): string | null =>
localStorage.getItem(TOKEN_KEY) || sessionStorage.getItem(TOKEN_KEY)
export const setToken = (token: string, remember = false): void => {
if (remember) {
localStorage.setItem(TOKEN_KEY, token)
} else {
sessionStorage.setItem(TOKEN_KEY, token)
}
}
export const clearToken = (): void => {
localStorage.removeItem(TOKEN_KEY)
sessionStorage.removeItem(TOKEN_KEY)
}
export const request = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
timeout: 15_000,
})
request.interceptors.request.use((config: InternalAxiosRequestConfig & RequestConfig) => {
if (!config.ignoreToken) {
const token = getToken()
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
}
return config
})
request.interceptors.response.use(
(response) => {
// 文件流等场景直接透传
if (
response.config.responseType === 'blob' ||
response.config.responseType === 'arraybuffer'
) {
return response.data
}
const data = response.data
// 非统一响应结构,直接透传(兼容第三方接口)
if (!isApiResponse(data)) return data
// 统一成功:只返回 data,业务层更干净
if (SUCCESS_CODES.has(data.code)) return data.data
if (data.code === 401) clearToken()
return Promise.reject(new Error(data.message || '请求失败'))
},
(error: AxiosError) => {
const status = error.response?.status
const message = status
? HTTP_ERROR_MAP[status] || `请求失败(${status})`
: error.code === 'ECONNABORTED'
? '请求超时,请稍后再试'
: '网络异常,请检查网络连接'
return Promise.reject(new Error(message))
},
)
export const http = {
request<T = unknown>(config: RequestConfig) {
return request.request<unknown, T>(config)
},
get<T = unknown>(url: string, params?: Record<string, unknown>, config?: RequestConfig) {
return request.get<unknown, T>(url, { ...config, params })
},
post<T = unknown>(url: string, data?: unknown, config?: RequestConfig) {
return request.post<unknown, T>(url, data, config)
},
put<T = unknown>(url: string, data?: unknown, config?: RequestConfig) {
return request.put<unknown, T>(url, data, config)
},
patch<T = unknown>(url: string, data?: unknown, config?: RequestConfig) {
return request.patch<unknown, T>(url, data, config)
},
delete<T = unknown>(url: string, params?: Record<string, unknown>, config?: RequestConfig) {
return request.delete<unknown, T>(url, { ...config, params })
},
}#最小使用示例
src/api/user.ts
import { http } from '@/utils/request'
type UserInfo = { id: string; name: string }
export const getUserInfo = () => http.get<UserInfo>('/user/info')
export const login = (username: string, password: string) =>
http.post<{ token: string }>(
'/auth/login',
{ username, password },
{ ignoreToken: true },
)
