axios+ts 使用类class的二次封装(全面封装)

axios+ts 使用class类二次封装axios

此次封装代码取自开源项目GeekerAdmin,感谢作者。

一、interface类型提取

几个接口类型可以直接从axios的ts库里直接提取,以下是请求和响应配置项接口

// aixos请求配置项接口
export interface AxiosRequestConfig<D = any> {
  url?: string;
  method?: Method | string;
  baseURL?: string;
  transformRequest?: AxiosRequestTransformer | AxiosRequestTransformer[];
  transformResponse?: AxiosResponseTransformer | AxiosResponseTransformer[];
  headers?: RawAxiosRequestHeaders;
  params?: any;
  paramsSerializer?: ParamsSerializerOptions;
  data?: D;
  timeout?: Milliseconds;
  timeoutErrorMessage?: string;
  withCredentials?: boolean;
  adapter?: AxiosAdapter;
  auth?: AxiosBasicCredentials;
  responseType?: ResponseType;
  responseEncoding?: responseEncoding | string;
  xsrfCookieName?: string;
  xsrfHeaderName?: string;
  onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;
  onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void;
  maxContentLength?: number;
  validateStatus?: ((status: number) => boolean) | null;
  maxBodyLength?: number;
  maxRedirects?: number;
  maxRate?: number | [MaxUploadRate, MaxDownloadRate];
  beforeRedirect?: (options: Record<string, any>, responseDetails: {headers: Record<string, string>}) => void;
  socketPath?: string | null;
  httpAgent?: any;
  httpsAgent?: any;
  proxy?: AxiosProxyConfig | false;
  cancelToken?: CancelToken;
  decompress?: boolean;
  transitional?: TransitionalOptions;
  signal?: GenericAbortSignal;
  insecureHTTPParser?: boolean;
  env?: {
    FormData?: new (...args: any[]) => object;
  };
  formSerializer?: FormSerializerOptions;
}

// axios响应接口格式
export interface AxiosResponse<T = any, D = any>  {
  data: T;
  status: number;
  statusText: string;
  headers: RawAxiosResponseHeaders | AxiosResponseHeaders;
  config: AxiosRequestConfig<D>;
  request?: any;
}

二、二次封装axios的代码

import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";  // 引入axios和提取interface接口类型
import { showFullScreenLoading, tryHideFullScreenLoading } from "@/config/serviceLoading";  // 引入封装的服务方式loading
import { ResultData } from "@/api/interface";  // 引入封装的接口返回类型
import { ResultEnum } from "@/enums/httpEnum";  // 引入封装的请求配置枚举
import { checkStatus } from "./helper/checkStatus";  // 引入封装的状态码message提示
import { ElMessage } from "element-plus";  // el+ 的message
import { GlobalStore } from "@/store";  // 引入pinia的全局仓局配置 ,用以设置token
import { LOGIN_URL } from "@/config/config";  // 引入登录页地址(默认)
import router from "@/routers";  // 引入router跳转

const config = {
	// 默认地址请求地址,可在 .env 开头文件中修改,这里无需断言,vite-env.d.ts里已经作了判断
	baseURL: import.meta.env.VITE_API_URL,
	// 设置超时时间(10s)
	timeout: ResultEnum.TIMEOUT as number,
	// 跨域时候允许携带凭证
	withCredentials: true
};

class RequestHttp {
	service: AxiosInstance;
	public constructor(config: AxiosRequestConfig) {
		// 实例化axios
		this.service = axios.create(config);

		/**
		 * @description 请求拦截器
		 * 客户端发送请求 -> [请求拦截器] -> 服务器
		 * token校验(JWT) : 接受服务器返回的token,存储到vuex/pinia/本地储存当中
		 */
		this.service.interceptors.request.use(
			(config: AxiosRequestConfig) => {
				const globalStore = GlobalStore();
				// * 如果当前请求不需要显示 loading,在 api 服务中通过指定的第三个参数: { headers: { noLoading: true } }来控制不显示loading,参见loginApi
				config.headers!.noLoading || showFullScreenLoading();
				const token: string = globalStore.token;
				return { ...config, headers: { ...config.headers, "x-access-token": token } };
			},
			(error: AxiosError) => {
				return Promise.reject(error);
			}
		);

		/**
		 * @description 响应拦截器
		 *  服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
		 */
		this.service.interceptors.response.use(
			(response: AxiosResponse) => {
				const { data } = response;
				const globalStore = GlobalStore();
				// * 在请求结束后,并关闭请求 loading
				tryHideFullScreenLoading();
				// * 登陆失效(code == 401)
				if (data.code == ResultEnum.OVERDUE) {
					ElMessage.error(data.msg);
					globalStore.setToken("");
					router.replace(LOGIN_URL);
					return Promise.reject(data);
				}
				// * 全局错误信息拦截(防止下载文件得时候返回数据流,没有code,直接报错)
				if (data.code && data.code !== ResultEnum.SUCCESS) {
					ElMessage.error(data.msg);
					return Promise.reject(data);
				}
				// * 成功请求(在页面上除非特殊情况,否则不用处理失败逻辑)
				return data;
			},
			async (error: AxiosError) => {
				const { response } = error;
				tryHideFullScreenLoading();
				// 请求超时单独判断,因为请求超时没有 response
				if (error.message.indexOf("timeout") !== -1) ElMessage.error("请求超时!请您稍后重试");
				// 根据响应的错误状态码,做不同的处理
				if (response) checkStatus(response.status);
				// 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面
				if (!window.navigator.onLine) router.replace("/500");
				return Promise.reject(error);
			}
		);
	}

	// * 常用请求方法封装
	get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
		return this.service.get(url, { params, ..._object });
	}
	post<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
		return this.service.post(url, params, _object);
	}
	put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
		return this.service.put(url, params, _object);
	}
	delete<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
		return this.service.delete(url, { params, ..._object });
	}
}

export default new RequestHttp(config);
posted @ 2023-01-25 21:46  wanglei1900  阅读(1326)  评论(0编辑  收藏  举报