再谈网络请求封装
之前封装 axios 看上去没有问题,但是改成 ts 就出现问题了。
之前的封装
js
import axios from 'axios';
import CryptoService from '@/utils/encryption';
import eventEmitter from '@/utils/eventEmitter';
const instance = axios.create({
baseURL: 'http://localhost:3000/api',
timeout: 5000,
});
instance.interceptors.request.use((config) => {
const { url } = config;
if (
!(
url.startsWith('/public') ||
url.startsWith('/patient') ||
url.startsWith('/doctor') ||
url.startsWith('/admin')
)
) {
return config;
}
const token = {};
eventEmitter.emit('TOKEN:GET', token);
if (!token.value) {
eventEmitter.emit('API:UN_AUTH');
return;
}
config.headers.Authorization = `Bearer ${token}`;
return config;
});
const errorCodeHandler = {
1: () => {
eventEmitter.emit('API:UN_AUTH');
},
2: () => {
eventEmitter.emit('API:NO_PERMISSION');
},
3: () => {
eventEmitter.emit('API:NOT_REAL_NAME');
},
};
const httpCodeHandler = {
404: () => {
eventEmitter.emit('API:NOT_FOUND');
},
500: () => {},
};
instance.interceptors.response.use(
(resp) => {
const { code } = resp.data;
if (code !== 0) {
errorCodeHandler[code]?.(resp.data);
return Promise.reject(resp.data);
}
const { data } = resp.data;
return data;
},
(error) => {
const { status } = error;
httpCodeHandler[status]?.();
return Promise.reject(error);
}
);
export default instance;
改成 ts 后:
ts
import axios, { type AxiosRequestConfig } from 'axios'
import { ApiBus } from '@/utils/eventEmitter'
import { useUserStore } from '@/store/useStore'
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8080/api'
const instance = axios.create({
baseURL: API_URL,
timeout: 5000,
})
interface Response<T = any> {
code: number
message: string
data: T
}
type ErrorHandler = (resp?: Response) => void
instance.interceptors.request.use((config) => {
const { url } = config
if (!url || url.startsWith('/public')) {
// 登录接口
return config
}
// 获取token操作
const { token } = useUserStore()
config.headers.Authorization = `Bearer ${token}`
return config
})
const errorCodeHandler: Record<number, ErrorHandler> = {
// - token无效或没有token 1 (前端需要重新登录)
1: () => {
ApiBus.emit('API:UN_AUTH')
},
}
const httpCodeHandler: Record<number, ErrorHandler> = {
404: () => {
ApiBus.emit('API:NOT_FOUND')
},
500: () => {},
}
instance.interceptors.response.use(
(resp) => {
const { code, data, message } = resp.data as Response
if (code < 100) {
// 业务错误处理
errorCodeHandler[code]?.(data)
return Promise.reject(message)
}
return data
},
(error) => {
const { status } = error
httpCodeHandler[status]?.()
return Promise.reject(error)
},
)
export default instance
之后封装的请求,比如 login
ts
import instance from '../useRequest';
export interface LoginRequest {
email: string;
password: string;
}
export interface LoginResponse {
token: string;
name: string;
avatar: string;
exp: number;
}
export const login = (req: LoginRequest) => {
return instance.post<LoginResponse>('/public/login', req);
};
这样写是不能解构出数据的,ts 会报错。这里需要再加一层
ts
const request = async <T>(config: AxiosRequestConfig): Promise<T> => {
const { data } = await instance.request<T>(config);
return data;
};
const RequestHandler = {
get: <T>(url: string, params?: Record<string, any>) => {
return request<T>({ url, method: 'get', params });
},
post: <T>(url: string, data?: Record<string, any>) => {
return request<T>({ url, method: 'post', data });
},
put: <T>(url: string, data?: Record<string, any>) => {
return request<T>({ url, method: 'put', data });
},
delete: <T>(url: string, data?: Record<string, any>) => {
return request<T>({ url, method: 'delete', data });
},
};
export default RequestHandler;
这样之后就有类型标注了。
完整代码
ts
import axios, { type AxiosRequestConfig } from 'axios'
import { ApiBus } from '@/utils/eventEmitter'
import { useUserStore } from '@/store/useStore'
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8080/api'
const instance = axios.create({
baseURL: API_URL,
timeout: 5000,
})
interface Response<T = any> {
code: number
message: string
data: T
}
type ErrorHandler = (resp?: Response) => void
instance.interceptors.request.use((config) => {
const { url } = config
if (!url || url.startsWith('/public')) {
// 登录接口
return config
}
// 获取token操作
const { token } = useUserStore()
config.headers.Authorization = `Bearer ${token}`
return config
})
const errorCodeHandler: Record<number, ErrorHandler> = {
// - token无效或没有token 1 (前端需要重新登录)
1: () => {
ApiBus.emit('API:UN_AUTH')
},
}
const httpCodeHandler: Record<number, ErrorHandler> = {
404: () => {
ApiBus.emit('API:NOT_FOUND')
},
500: () => {},
}
instance.interceptors.response.use(
(resp) => {
const { code, data, message } = resp.data as Response
if (code < 100) {
// 业务错误处理
errorCodeHandler[code]?.(data)
return Promise.reject(message)
}
return data
},
(error) => {
const { status } = error
httpCodeHandler[status]?.()
return Promise.reject(error)
},
)
const request = async <T>(config: AxiosRequestConfig): Promise<T> => {
const resp = await instance.request<T>(config)
return resp as T
}
const RequestHandler = {
get: <T>(url: string, params?: Record<string, any>) => {
return request<T>({ url, method: 'get', params })
},
post: <T>(url: string, data?: Record<string, any>) => {
return request<T>({ url, method: 'post', data })
},
put: <T>(url: string, data?: Record<string, any>) => {
return request<T>({ url, method: 'put', data })
},
delete: <T>(url: string, data?: Record<string, any>) => {
return request<T>({ url, method: 'delete', data })
},
}
export default RequestHandler