数据请求工具(Request/axios)
这个请求工具基于 Axios 封装,提供了更加简洁、功能完善的 HTTP 请求能力,包括请求/响应拦截、取消请求、统一错误处理等功能。
基本用法
创建请求实例
javascript
import { createHttpInstance } from '@pt/utils/modules/request';
import type { RequestHttp } from '@pt/utils/modules/request';
// 基础配置
const http: RequestHttp = createHttpInstance({
axiosConfig: { baseURL: '/api' },
onUnauthorized: () => (window.location.href = '/login')
});
// 完整配置(包含自定义token键名和请求头)
const http: RequestHttp = createHttpInstance({
axiosConfig: {
baseURL: '/api',
timeout: 5000
},
onUnauthorized: () => (window.location.href = '/login'),
tokenKey: 'Authorization', // 自定义token键名,默认为 'satoken'
customHeaders: {
'X-Custom-Header': 'value',
'App-Version': '1.0.0'
}
});注意:旧版本的参数传递方式已废弃,建议使用对象参数方式进行配置。
发起请求
javascript
// GET 请求
http.get<UserInfo>('/user/info', { id: 1 }).then(res => {
console.log(res.data);
});
// POST 请求
http.post<ResponseType>('/user/create', {
name: '张三',
age: 25
}).then(res => {
console.log(res);
});
// PUT 请求
http.put<ResponseType>('/user/update', { id: 1, name: '李四' });
// DELETE 请求 - 参数作为 query string
http.delete<ResponseType>('/user/delete', { id: 1 });
// DELETE 请求 - 参数作为 request body(通过 data 传递)
http.delete<any>('/api/kbFile', null, { data: { id: 1, name: 'test' } });
// DELETE 请求 - 带额外配置(如自定义headers)
http.delete<ResponseType>('/user/delete', { id: 1 }, {
headers: { 'X-Custom': 'value' }
});
// PATCH 请求
http.patch<ResponseType>('/user/patch', { id: 1, status: 'active' });高级配置
自定义 Token 获取方式
如果您需要从不同的存储位置获取 token,或需要自定义获取 token 的逻辑,可以使用 setTokenProvider 方法:
javascript
http.setTokenProvider(() => {
// 自定义获取token的逻辑
return (
localStorage.getItem('custom_token_key') ||
sessionStorage.getItem('token')
);
});添加自定义请求头
您可以使用 useCustomHeaders 方法添加多个自定义请求头:
javascript
http.useCustomHeaders(() => ({
version: storage.get('version') || '',
officeId: storage.get('office_id') || '',
'Content-Type': 'application/json',
'App-Client': 'web',
}));链式调用
支持链式调用进行多项配置:
javascript
http
.setTokenProvider(() => localStorage.getItem('admin_token'))
.useCustomHeaders(() => ({
platform: 'admin',
version: '1.0.0'
}));核心功能
请求拦截
- 自动添加 token 到请求头
- 支持请求防重复提交
- 可自定义请求头
响应拦截
- 自动处理 token 过期情况
- 统一错误处理
- 支持请求取消
Token 处理
- 默认使用 'satoken' 作为 token 键名
- 支持自定义 token 键名
- 灵活的 token 获取方式
类型支持
所有请求方法都支持泛型,便于类型推导:
typescript
interface UserInfo {
id: number;
name: string;
age: number;
}
// 类型安全的请求
const userInfo = await http.get<UserInfo>('/user/info', { id: 1 });
console.log(userInfo.data.name); // TypeScript 会提供类型提示异常处理
统一响应结构
typescript
interface ResultData<T> {
code: number;
message: string;
data: T;
}401 未授权处理
当服务器返回 401 或其他未授权状态码时,会自动触发创建实例时提供的回调函数:
javascript
createHttpInstance(
{ baseURL: '/api' },
() => {
// 这里处理未授权情况
window.location.href = '/login';
// 或者清除本地存储
localStorage.removeItem('token');
}
);请求错误处理
javascript
http.get('/api/data')
.then(res => {
// 成功处理
})
.catch(error => {
// 统一错误处理
if (error.response) {
// 服务器返回错误状态码
console.error(`状态码: ${error.response.status}`);
console.error(`错误信息: ${error.response.data.message}`);
} else if (error.request) {
// 请求发送但没有收到响应
console.error('网络异常,请检查您的网络连接');
} else {
// 请求配置出错
console.error(`请求异常: ${error.message}`);
}
});技术实现
这个请求工具的核心是 RequestHttp 类,它封装了 Axios 实例并提供了以下功能:
- 请求拦截器:添加 token 和自定义头信息
- 响应拦截器:处理通用错误和 token 过期情况
- 请求取消机制:防止重复请求
- 方便的 HTTP 方法封装:get、post、put、delete、patch
通过工厂函数 createHttpInstance 创建实例,使得配置更加灵活和简洁。
配置选项
RequestHttpOptions
typescript
interface RequestHttpOptions {
axiosConfig: AxiosRequestConfig; // Axios 配置对象
onUnauthorized?: () => void; // 401 未授权时的回调函数
tokenKey?: string; // Token 的请求头键名,默认 'satoken'
customHeaders?: Record<string, any>; // 固定的自定义请求头
}最佳实践
1. 创建统一的请求实例
建议在项目的 /src/api 目录下新建一个 index.ts 文件,创建统一的请求实例:
typescript
// src/api/index.ts
import { getLoginUrl } from '@/config/config'
import { createHttpInstance } from '@pt/utils/modules/request'
import type { RequestHttp } from '@pt/utils/modules/request'
const request: RequestHttp = createHttpInstance({
axiosConfig: {
baseURL: import.meta.env.VITE_API_URL || ''
},
onUnauthorized: () => {
window.location.href = getLoginUrl() // 401 处理逻辑
}
})
export default request2. 按模块组织 API 接口
在 src/api/modules/ 目录下按业务模块创建 API 文件:
typescript
// src/api/modules/knowledge.ts
import http from '@/api'
/*
// src/api/config/servicePort
export const KNOW_SOURCE_V1 = '/api-know-source/v1' // 知识库v1版本
export const KNOW_SOURCE_V3 = '/api-know-source/v3' // 知识库v3版本
*/
import { KNOW_SOURCE_V1 } from '../config/servicePort'
// ==================== 知识库管理 (KbBase) ====================
// * 获取知识库分页列表
export const getKbBasePageApi = (params: any) => {
return http.post<any>(`${KNOW_SOURCE_V1}/api/kbBase/page`, params)
}
// * 获取知识库详情
export const getKbBaseDetailApi = (id: string) => {
return http.get<any>(`${KNOW_SOURCE_V1}/api/kbBase/${id}`)
}
// * 创建知识库
export const createKbBaseApi = (params: any) => {
return http.post<any>(`${KNOW_SOURCE_V1}/api/kbBase`, params)
}
// * 更新知识库
export const updateKbBaseApi = (params: any) => {
return http.patch<any>(`${KNOW_SOURCE_V1}/api/kbBase`, params)
}
// * 删除知识库
export const deleteKbBaseApi = (params: any) => {
return http.delete<any>(`${KNOW_SOURCE_V1}/api/kbBase`, null, { data: params })
}3. 其他最佳实践建议
- 使用泛型获得更好的类型支持
- 根据项目需求自定义 token 处理逻辑
- 利用链式调用简化配置
- 在创建实例时配置固定的请求头,使用方法动态添加可变的请求头
typescript
// 为特定模块创建独立实例(如需要)
const adminHttp = createHttpInstance({
axiosConfig: {
baseURL: '/api/admin',
timeout: 10000
},
onUnauthorized: () => (window.location.href = '/admin/login'),
tokenKey: 'Authorization',
customHeaders: {
'X-Client-Type': 'admin',
'X-App-Version': '2.0.0'
}
})
.setTokenProvider(() => localStorage.getItem('admin_token'))
.useCustomHeaders(() => ({
role: 'admin',
timestamp: Date.now()
}))迁移指南
如果您正在使用旧版本的参数传递方式,请按以下方式迁移:
旧版本(已废弃):
javascript
const http = createHttpInstance(
{ baseURL: '/api' },
() => (window.location.href = '/login'),
'Authorization',
{ 'X-Custom': 'value' }
);新版本(推荐):
javascript
const http = createHttpInstance({
axiosConfig: { baseURL: '/api' },
onUnauthorized: () => (window.location.href = '/login'),
tokenKey: 'Authorization',
customHeaders: { 'X-Custom': 'value' }
});