axios 个人使用二次封装(包括取消请求, 错误重试)

129 阅读2分钟

a48ee16636d2c430571b03687a90cdff.png

axios 封装

# Axios 封装详解 
为了创建一个功能强大且易于管理的HTTP请求库,我们基于`axios`进行了扩展和优化。以下是对这段代码的详细解释,帮助你一步步了解其工作原理。

## 1. 引入依赖 
首先,我们从`axios`库导入必要的模块:
import axios from 'axios';

2. 定义全局常量

定义了一个全局常量MAX_RETRY用于设置重试次数的最大值:

const MAX_RETRY = 3;

3. 创建 Axios 实例

通过axios.create()创建了一个自定义配置的Axios实例,并设置了基础URL、超时时间和默认头部信息:

const service = axios.create({  baseURL: process.env.NEXT_PUBLIC_SCRATCH_API_URL, // API 的 base_url  timeout: 5000, // 请求超时时间  headers: {  'Content-Type': 'application/json',     }
});

4. 初始化 Custom 属性

确保service.defaults.custom属性存在,以便后续存储与特定请求关联的AbortController实例:

if (!service.defaults.custom) service.defaults.custom = {};

5. 请求拦截器

在发送请求之前,通过请求拦截器为每个请求添加AbortController信号,并检查是否需要附加认证令牌:

service.interceptors.request.use(  config => {  if (!config.signal) {  const controller = new AbortController();             config.controller = controller;
            config.signal = controller.signal;
        }

 if (config.useToken !== false) {  const token = localStorage.getItem('access_token');  if (token) {  config.headers.Authorization = `Bearer ${token}`;             }
        }

 return config;     },
 error => Promise.reject(error) );

6. 响应拦截器

响应拦截器负责处理请求成功后的数据返回以及错误处理逻辑,包括超时、网络错误和其他状态码(如500、408)导致的重试机制:

service.interceptors.response.use(  response => response.data,  async error => {  const originalRequest = error.config; 
 // 处理超时错误和其他类型的网络错误  if ((error.code === 'ECONNABORTED' && error.message.includes('timeout')) || !error.response) {  return Promise.reject(new Error('请求超时或网络错误,请稍后重试'));         }

 // 实现重试逻辑,最多重试3次  if ([500, 408].includes(error.response?.status)) {  if (!originalRequest._retry) {  originalRequest._retry = true;  originalRequest._retryCount = originalRequest._retryCount || 0; 
 if (originalRequest._retryCount < MAX_RETRY) {                     originalRequest._retryCount++;
 // 使用指数退避算法添加延迟  await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, originalRequest._retryCount)));  return service(originalRequest);                 }
            }
        }

 return Promise.reject(error);     }
);

7. 取消请求函数

提供了一个cancelRequest函数来取消特定请求。它通过提供的键查找对应的AbortController并调用其abort方法:

export const cancelRequest = (key) => {  if (service.defaults.custom[key] && service.defaults.custom[key].controller) {         service.defaults.custom[key].controller.abort();
 delete service.defaults.custom[key];     }
};

8. HTTP 方法抽象

为了简化代码并提高一致性,我们将GET、POST、PUT和DELETE方法的创建抽象成了一个通用函数createHttpMethod。这个函数接受HTTP方法名称作为参数,并根据传入的数据类型(查询参数或请求体)正确地构造请求:

const createHttpMethod = (method) => (url, dataOrParams, config = {}) => {  const controller = new AbortController();     config.signal = controller.signal;
    config.controller = controller;

 if (config.requestKey) {         service.defaults.custom[config.requestKey] = { controller };
    }

 switch (method.toLowerCase()) {  case 'get':  return service.get(url, { params: dataOrParams, ...config });  case 'post':  return service.post(url, dataOrParams, config);  case 'put':  return service.put(url, dataOrParams, config);  case 'delete':  return service.delete(url, { data: dataOrParams, ...config });  default:  throw new Error(`Unsupported HTTP method: ${method}`);     }
};

export const GET = createHttpMethod('GET'); export const POST = createHttpMethod('POST'); export const PUT = createHttpMethod('PUT'); export const DELETE = createHttpMethod('DELETE');

9. 文件上传方法

专门定义了UPLOAD方法用于文件上传,该方法允许指定进度回调函数以跟踪上传进度:

export const UPLOAD = (url, data, onUploadProgress) => {  return service.post(url, data, {  headers: { 'Content-Type': 'multipart/form-data' },         onUploadProgress
    });
};

通过上述步骤,我们构建了一个灵活且强大的axios封装,它不仅能够处理常见的HTTP操作,还支持请求取消、错误处理和自动重试等功能,从而提高了应用程序的健壮性和用户体验。

import axios from 'axios'; 
// 错误重试次数 const MAX_RETRY = 3; 
// 创建axios实例 const service = axios.create({  baseURL: process.env.NEXT_PUBLIC_SCRATCH_API_URL, // API 的 base_url  timeout: 5000, // 请求超时时间  headers: {  'Content-Type': 'application/json',     }
});

// 初始化custom属性用于存储AbortController if (!service.defaults.custom) service.defaults.custom = {}; 
// 请求拦截器 service.interceptors.request.use(
 config => {  if (!config.signal) {  const controller = new AbortController();             config.controller = controller;
            config.signal = controller.signal;
        }

 if (config.useToken !== false) {  const token = localStorage.getItem('access_token');  if (token) {  config.headers.Authorization = `Bearer ${token}`;             }
        }

 return config;     },
 error => Promise.reject(error) );

// 响应拦截器 service.interceptors.response.use(
 response => response.data,  async error => {  const originalRequest = error.config; 
 // 处理超时错误和其他类型的网络错误  if ((error.code === 'ECONNABORTED' && error.message.includes('timeout')) || !error.response) {  return Promise.reject(new Error('请求超时或网络错误,请稍后重试'));         }

 // 实现重试逻辑,最多重试3次  if ([500, 408].includes(error.response?.status)) {  if (!originalRequest._retry) {  originalRequest._retry = true;  originalRequest._retryCount = originalRequest._retryCount || 0; 
 if (originalRequest._retryCount < MAX_RETRY) {                     originalRequest._retryCount++;
 // 使用指数退避算法添加延迟  await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, originalRequest._retryCount)));  return service(originalRequest);                 }
            }
        }

 return Promise.reject(error);     }
);

// 导出取消请求函数 export const cancelRequest = (key) => {  if (service.defaults.custom[key] && service.defaults.custom[key].controller) {         service.defaults.custom[key].controller.abort();
 delete service.defaults.custom[key];     }
};

// 定义HTTP方法 const createHttpMethod = (method) => (url, dataOrParams, config = {}) => {  const controller = new AbortController();     config.signal = controller.signal;
    config.controller = controller;

 if (config.requestKey) {         service.defaults.custom[config.requestKey] = { controller };
    }

 switch (method.toLowerCase()) {  case 'get':  return service.get(url, { params: dataOrParams, ...config });  case 'post':  return service.post(url, dataOrParams, config);  case 'put':  return service.put(url, dataOrParams, config);  case 'delete':  return service.delete(url, { data: dataOrParams, ...config });  default:  throw new Error(`Unsupported HTTP method: ${method}`);     }
};

export const GET = createHttpMethod('GET'); export const POST = createHttpMethod('POST'); export const PUT = createHttpMethod('PUT'); export const DELETE = createHttpMethod('DELETE'); 
// 文件上传方法 export const UPLOAD = (url, data, onUploadProgress) => {  return service.post(url, data, {  headers: { 'Content-Type': 'multipart/form-data' },         onUploadProgress
    });
};