通常在开发时,后端向前端返回的数据可以如下:
- 1 使用restful api充分利用http状态码,然后在data中追加code字段,请求成功返回200,请求失败返回404,401,500等状态码,并且在code字段中给出详细的字符串信息
- 2 再包一层,所有请求不论失败还是成功状态返回均为200,然后在code中,返回实际的成功或失败的原因(可以是number,也可以是string)
以下以第二种为例:
type Content = Array<unknown> | Record<string, unknown> | null;interface CustomResponse<T extends Content = Content> {code: number;//具体的code,这里依然使用的400,401等200表示成功data: T;msg: string;
}export enum Method {/** Get请求 */Get = 'GET',/** Post请求 */Post = 'POST',/** Put请求 */Put = 'PUT',/** Delete请求 */Delete = 'DELETE',
}
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import router from '@/router';
import { ElMessage, ElMessageBox } from 'element-plus';
import { localStorage } from '@/storage';
import { Method } from '@/enum';
import useStore from '@/store';
import qs from 'qs';// 白名单列表,用于直接显示前端定义的错误
const whiteList: string[] = [];const requests: any[] = [];
const cancelRequest = (config: any, cancelAll = false) => {for (const req in requests) {if (!cancelAll) {if (requests[req].url === `${config.method}-${config.url}`) {requests[req].controller.abort();requests.splice(Number(req), 1);}} else {requests[req].controller.abort();requests.splice(Number(req), 1);}}
};// 创建 axios 实例
const service = axios.create({baseURL: import.meta.env.DEV ? 'api-dev' : 'api-prod',timeout: 60000,
});// 请求拦截器
service.interceptors.request.use((config: AxiosRequestConfig) => {const { user } = useStore();// 请求自动添加tokenif (user.token) {config.headers!.Authorization = `${localStorage.get('token')}`;}// 请求队列,用于取消请求const controller = new AbortController();config.signal = controller.signal;requests.push({url: `${config.method}-${config.url}`,controller: controller,});return config;},error => {return Promise.reject(error);}
);// 响应拦截器
service.interceptors.response.use((response: AxiosResponse) => {const { status } = response;if (status === 200) {switch (response.data.code) {case 200:return response.data;case 404:// 自定义的错误码,可以与http状态码一致,前后端约定即可// 同时根据错误码进行跳转,清空缓存等动作break;......default:break;}}return response.data;},error => {return Promise.reject(new Error(error.message || 'Error'));}
);function customRequest(method: Method
): <T extends Content>(url: string,data?: Record<string, any>,options?: AxiosRequestConfig
) => Promise<T> {return async function <T extends Content>(url: string,data?: Record<string, any>,options?: AxiosRequestConfig) {let restParams = {};if (method === Method.Get) {restParams = {params: { ...data?.params },paramsSerializer: function (params: any) {//arg: [1, 2]会被转换为不同形式: indices转换为'arg[0]=1&arg[1]=2' brackets转换为'arg[]=1&arg[]=2' repeat转换为'arg=1&arg=2'return qs.stringify(params, { arrayFormat: 'repeat' }); },};} else {restParams = {data,...options,};}const res = await service.request<T, CustomResponse<T>>({ url, method: method, ...restParams });// 为了不在每个请求后添加如下代码,所以在此统一处理if (res.code === 200 && res.data) {return res.data;}throw res.msg;};
}// axios 实例
export default service;// 自定义axios 实例
export const requestService = {get: customRequest(Method.Get),post: customRequest(Method.Post),put: customRequest(Method.Put),delete: customRequest(Method.Delete),
};
export { cancelRequest, requests };