新版 Axios 封装
typescript
import axios, {AxiosRequestConfig, Method} from 'axios';
import {message as AntdMessage} from 'antd';
import _, {debounce} from 'lodash';
import {HEADER, HTTP_REQUEST_URL, TOKEN_NAME, CODE_STATUS} from '@/constants/constants';
import {commonSlice} from '@/store/common';
import store from '@/store/index';
import {getTicketFromUrl, reLogin} from './utils';
export interface RequestConfig {
token?: boolean;
message?: boolean;
}
const defaultConfig: RequestConfig = {
token: true,
message: true,
};
// const dispatch = useDispatch();
const debouncedShowErrorMessage = debounce((message = '网络连接异常') => AntdMessage.error(message), 500);
const {updateGLoading} = commonSlice.actions;
// 忽略key大小写检查
function checkKeyIgnoreUpCase(target: Record<string, string>, key: string) {
let res = false;
if (typeof target !== 'object' || target === null) {
return res;
}
const keys = Object.keys(target);
res = keys.some(k => {
return k.toLocaleLowerCase() === key.toLocaleLowerCase();
});
return res;
}
// 创建一个 axios 实例
const instance = axios.create({
baseURL: HTTP_REQUEST_URL, // 公共的接口地址
timeout: 20000, // 请求超时时间设置
withCredentials: true, // 跨域时使用凭证,默认带上cookies
});
type InterceptorsConfig = AxiosRequestConfig & {requestConfig: RequestConfig};
// 请求拦截器,config 是发送请求的参数信息
instance.interceptors.request.use(
config => {
config.url = `${config.url}`;
// 是否请求需要带上 token
if ((config as InterceptorsConfig).requestConfig.token) {
config.headers[TOKEN_NAME] = localStorage.getItem('ticket') || getTicketFromUrl('ticket');
}
store.dispatch(updateGLoading(true));
return config;
},
// 请求拦截的错误回调
error => {
store.dispatch(updateGLoading(false));
return Promise.reject(error);
}
);
// 响应拦截器,response为响应的结构组织,包括data,headers,status,statusText,config,request
instance.interceptors.response.use(
response => {
const shouldMessage = (response.config as InterceptorsConfig).requestConfig.message;
const {status, message} = response.data;
// 根据 status 进行判断,与后端约定某些状态该返回什么数据
switch (status) {
case CODE_STATUS.SUCCESS:
store.dispatch(updateGLoading(false));
return response;
case CODE_STATUS.FAILED:
shouldMessage && AntdMessage.error(_.get(response, 'data.message.global', ''));
store.dispatch(updateGLoading(false));
break;
case CODE_STATUS.EXPIRE:
response.data.result = _.get(response, 'data.result', []);
store.dispatch(updateGLoading(false));
shouldMessage && AntdMessage.error('登录凭证失效,请重新登录');
// setTimeout(()=>{
// window.location = SPDB_LOGIN_URL as any;
// },1000)
localStorage.removeItem('ticket');
break;
default:
store.dispatch(updateGLoading(false));
shouldMessage && AntdMessage.error(message);
return;
}
return response;
},
// 响应拦截的报错信息,协议报错
error => {
if (error && error.response) {
const status = error.response.status;
status === 403 && reLogin();
AntdMessage.error(error.message);
} else {
debouncedShowErrorMessage();
}
store.dispatch(updateGLoading(false));
return Promise.reject(error);
}
);
// 封装 axios 实例
function request(config: AxiosRequestConfig, requestConfig = defaultConfig) {
const GET_CONTENT_TYPE = 'application/x-www-form-urlencoded;charset=UTF-8';
const POST_CONTENT_TYPE = 'application/json;charset=utf-8';
// 获取请求方法
const method = String.prototype.toUpperCase.call(config.method) || 'GET';
// 合并headers
const headers = Object.assign({}, HEADER, config.headers);
// Content-Type 忽略大小写拼写key是否在对象是已定义
if (!checkKeyIgnoreUpCase(headers, 'content-type')) {
if (method === 'GET' || method === 'DELETE') {
headers['Content-Type'] = GET_CONTENT_TYPE;
}
if (method === 'POST' || method === 'PUT') {
headers['Content-Type'] = POST_CONTENT_TYPE;
}
}
// 给config上面绑定字段,方便在拦截器中使用
(config as any).requestConfig = Object.assign(defaultConfig, requestConfig);
return instance(config).then(res => res.data);
}
const requestMethod: Method[] = ['options', 'get', 'post', 'put', 'head', 'delete'];
export type AjaxFunc = <T = any, K = any>(
url: string,
data?: T,
requestConfig?: RequestConfig,
options?: AxiosRequestConfig
) => Promise<K>;
// 导出一个更简便的方法
export function fetch<T = any, K = any>(
url: string,
method: Method = 'GET',
data?: T,
requestConfig = defaultConfig,
extraConfig?: AxiosRequestConfig
): Promise<K> {
// 获取请求类型
const casedMethod = method.toUpperCase() as Method;
const config = {
url,
method: casedMethod,
} as AxiosRequestConfig;
// data处理,get和post的data传输不一样
if (casedMethod === 'GET') {
config.params = data;
} else {
config.data = data;
}
return request({...extraConfig, ...config}, requestConfig);
}
// ajax.get()/ajax.post()等等类似这样调用
const ajax = {} as {
[k in Method]: AjaxFunc;
};
requestMethod.forEach(method => {
ajax[method] = <T, K>(api: string, data: T, requestConfig = defaultConfig, opt?: AxiosRequestConfig) =>
fetch<T, K>(api, method, data, requestConfig, opt || {});
});
// 导出
export default ajax;
typescript
// 使用
import ajax from '@/utils/request';
import capi from './api';
// example
export const getDataSource = async () => {
const result = await ajax.get(capi.sourceData);
return result.data;
};
export const getDataList = async (params: any) => {
const result = await ajax.post(capi.dataList, params);
return result.data;
};