axios响应拦截处理(二次封装)搭配await更适合

axios请求拦截:添加额外参数:

平时接口封装:

import request from '@/utils/request';

// 
export const getList = async (data, otherConfig) => {
  return await request({
    url: `getList`,
    method: 'post',
    data,
    otherConfig
  });
};

// 平时使用,特殊页面
const res = await getList(data,{loading:true,...})


场景1:如果确定该接口为长接口,比较耗时等,其他相关配置baseURL等
export const getList = async (data, otherConfig) => {
  return await request({
    url: `getList`,
    method: 'post',
    data,
    otherConfig:{
      ...otherConfig,
      timeout:60000,
      baseURL:'/api'
    }
  });
};

// response 拦截器
service.interceptors.response.use(
  (response) => {
    const res = response.data;
    // 其他处理省略...
    return res;
  },
  async (error) => {
    // 根据实际情况设置
    // 如果是返回reject则会被catch捕获,await之后的代码不会执行
    // return Promise.reject(error);
    //  如果是以下,则不会被catch捕获,await后代码会执行
    return Promise.resolve(error)
  
  }
);

普通情况:接口error错误时返回 Promise.reject(err) 不再推荐

接口响应拦截处理
错误时返回 return Promise.reject(error) ===  中断拦截

const funA = async () => {
  try{
    const res = await 异常接口()
    console.log('AAA:',res);
  }catch(err){
    console.log('AAA:err',err);  
  }

}

const funB = async () => {
  try{
    const res = await 正常接口()
    console.log('BBB:',res);
  }catch(err){
    console.log('BBB:err',err);  
  }
}



需求一:需要等A执行完再执行B
// 生命周期调用
onMounted(async () => {
  await funA();
  await funB();
});

场景1:代码习惯使用  try{} catch(err){}  捕获
// 执行结果
!被catch捕获,打印 AAA:err 跟 BBB



场景2:代码习惯不使用  try{} catch(err){}  捕获
// 执行结果
直接报错,什么都不打印,==>>> funB会被funA拦截,也就是说A接口异常都会影响其他的

场景3:funA、funB 都正常,但是A接口比较慢,使用  try catch 捕获
// 执行结果
打印顺序正常

场景4:funA、funB 都正常,但是A接口比较慢,使用  try catch 捕获,生命周期不使用 await
onMounted(async () => {
   funA();
   funB();
}); 
// 执行结果
先打印BBB,再打印AAA,无法做到异步效果

综上所述:
想要防止一个接口异常影响其他接口,需要每个接口都要用 try catch 去捕获异常并写逻辑,或者使用 await 接口().catch(err=>{ ... }) 
需要做到异步效果,也是等一个接口处理完再去处理其他逻辑,则需要在生命周期使用 async await


特殊情况:错误时返回 Promise.resolve(err) 推荐写法

接口响应拦截处理
错误时返回 return Promise.resolve(error) === 放行

const funA = async () => {
  try{
    const res = await 异常接口()
    console.log('AAA:',res);
  }catch(err){
    console.log('AAA:err',err);  
  }

}

const funB = async () => {
  try{
    const res = await 正常接口()
    console.log('BBB:',res);
  }catch(err){
    console.log('BBB:err',err);  
  }
}



需求一:需要等A执行完再执行B
// 生命周期调用
onMounted(async () => {
  await funA();
  await funB();
});

场景1:代码习惯使用  try{} catch(err){}  捕获
// 执行结果
打印 AAA 跟 BBB ===>>> 无法被 catch 捕获! 无论正常异常都会进入 try

  
场景2:代码习惯不使用  try{} catch(err){}  捕获
// 执行结果
打印 AAA 跟 BBB ===>>> 打印顺序正常! 


场景3:funA、funB 都正常, 生命周期使用 await ,但是A接口比较慢,
// 执行结果
先后打印顺序正常,异步正常

场景4:funA、funB 都正常,但是A接口比较慢,生命周期不使用 await
onMounted(async () => {
   funA();
   funB();
}); 
// 执行结果
先打印BBB,再打印AAA,无法做到异步效果


综上所述,如果想要捕获接口异常并做出处理,只能通过 res.code === 0/200 (根据后端状态码)  去进行判断,或者只做成功判断(日常接口使用)
要达到 异步效果则需要在生命周期/方法 使用 async await 

最终接口axios封装文件

import axios from 'axios';
import { getToken } from '@/utils/token';
import { ElNotification, ElMessageBox, ElMessage, ElLoading } from 'element-plus';
import router from '@/router';
import { useUserStore } from '@/stores/user';

/**
 * @description: request作用域内的全局变量和函数
 * @param TIMEOUT 请求超时时间 ms
 * @param TimeoutMsg 请求超时时间 提示
 * @param reqWithLoadingNum 开启loading的请求数
 * @param duration 保证toast显示3s 才被关闭  避免提示过短
 * @param timeoutTimer 超时提醒定时器
 * @param loadingInstance loading实例
 */

const TIMEOUT = 30000;
const TimeoutMsg = '网络异常,请求超时';
let reqWithLoadingNum = 0;
let duration = 3000;
let timeoutTimer = null;
let loadingInstance = null;

/**
 * @description: 开启loading
 */
const showLoading = (params) => {
  // 同一个页面仅第一次loading展示 避免页面多次出现loading
  if (reqWithLoadingNum <= 0) {
    // showloading
    loadingInstance = ElLoading.service({
      fullscreen: true,
      lock: true,
      text: params || '接口请求中~',
      background: 'rgba(0, 0, 0, 0.7)'
    });

    // 做超时处理
    timeoutTimer && clearTimeout(timeoutTimer);
    timeoutTimer = setTimeout(() => {
      loadingInstance.close();
      clearTimeout(timeoutTimer);
      if (reqWithLoadingNum > 0) {
        // 存在请求超时
        ElMessage({
          type: 'error',
          message: TimeoutMsg,
          duration
        });
      }
    }, TIMEOUT + 1000);
  }
  reqWithLoadingNum += 1;
};

/**
 * @description: 关闭loading
 */
const hideLoading = (force = false) => {
  reqWithLoadingNum -= 1;
  if (reqWithLoadingNum <= 0 || force) {
    reqWithLoadingNum = 0;
    loadingInstance.close();
  }
};

const service = axios.create({
  // api 的 base_url
  baseURL: import.meta.env.VITE_APP_BASE_API,
  timeout: 30000
});

/**
 * @description: request拦截器
 * @param extendParam <Object> 自定义config,自定义timeout、loading
 * 特殊接口timeout需要加长
 */
service.interceptors.request.use(
  (config) => {
    if (config?.otherConfig) {
      config = {
        ...config,
        ...config.otherConfig
      };
    }
   
    if (getToken()) {
      // 让每个请求携带自定义token 请根据实际情况自行修改
      config.headers['x-auth-token'] = getToken();
    }

    // 全局AXIOS请求加载
    if (config?.otherConfig?.loading === true) {
      showLoading();
    }

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

// response 拦截器
service.interceptors.response.use(
  (response) => {
    if (response?.config?.otherConfig?.loading === true) {
      hideLoading();
    }

    const res = response.data;

    if (res.code) {
      // 不能在头部引入,调用顺序问题,pinia还未被创建
      const userStore = useUserStore();
      switch (res.code) {
        case 70403:
        case 71402:
        case 401:
          ElMessageBox.alert('登录状态已过期,请您重新登录!', '系统提示', {
            confirmButtonText: '重新登录',
            type: 'warning'
          }).then(() => {
            userStore.logout().then(() => {
              router.push('/login');
            });
          });

          break;
        case 70401:
        case 403:
          ElNotification.error({
            title: '错误',
            message: res.message || '您无权进行此操作,请求执行已拒绝'
          });

          break;
        case 0:
        case 200:
          break;

        case 201:
        case 202:
        case 203:
          ElNotification.success({
            title: '成功',
            message: res.message
          });

          break;

        default:
          ElNotification.error({
            title: '错误',
            message: res.message || '请求失败'
          });

          break;
      }
    }

    return res;
  },
  async (err) => {
    
    if (err?.config?.otherConfig?.loading === true) {
      hideLoading();
    }
    ElNotification.error({
      title: '错误',
      message: err?.message || '请求失败'
    });

    // 根据实际情况设置
    // 如果是返回reject则会被catch捕获,await之后的代码不会执行
    // return Promise.reject(error);

    //  如果是以下两种,则不会被catch捕获,await后代码会执行
    return Promise.resolve(err);
    // return error
  }
);
export default service;

后续会不断更新

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue2封装Axios接口请求的二次封装可以让我们在项目中加方便地使用Axios进行数据请求,同时也可以提高代码的可维护性和复用性。下面是一个简单的Vue2封装Axios接口请求的示例: 1. 安装Axios 在项目中安装Axios,可以使用npm或者yarn进行安装: ```bash npm install axios --save ``` 或者 ```bash yarn add axios ``` 2. 封装Axios 在src目录下新建一个api文件夹,用于存放Axios封装代码。在api目录下新建一个request.js文件,用于封装Axios的请求方法。 ```javascript import axios from 'axios' // 创建实例 const service = axios.create({ baseURL: process.env.BASE_URL, // 接口的基础路径 timeout: 5000 // 请求超时时间 }) // 请求拦截器 service.interceptors.request.use( config => { // 在请求发送之前做一些处理,比如添加token等 return config }, error => { // 请求错误时做些事 return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( response => { // 对响应数据做一些处理,比如说判断返回的状态码是否正确等 return response.data }, error => { // 响应错误时做些事 return Promise.reject(error) } ) export default service ``` 在上面的代码中,我们使用了Axios的interceptors来拦截请求和响应,并做一些处理。比如说,在请求拦截器中,我们可以在发送请求之前添加token等信息;在响应拦截器中,我们可以对返回的数据进行处理,判断响应状态码是否正确等。 3. 封装API请求 在api目录下新建一个index.js文件,用于封装API请求的方法。我们可以根据实际的业务需求,封装不同的请求方法。 ```javascript import request from './request' export function getUsers () { return request({ url: '/users', method: 'get' }) } export function getUserById (id) { return request({ url: `/users/${id}`, method: 'get' }) } export function createUser (data) { return request({ url: '/users', method: 'post', data }) } // 其他API请求方法... ``` 在上面的代码中,我们使用了封装的request方法来发送请求,同时根据业务需求,定义了不同的请求方法,比如获取用户列表、根据ID获取用户信息、创建用户等。 4. 使用API请求 在Vue组件中使用API请求非常简单,只需要在组件中引入封装的API请求,然后调用对应的方法即可。 ```javascript import { getUsers } from '@/api' export default { mounted () { this.getUsers() }, methods: { async getUsers () { const { data } = await getUsers() console.log(data) } } } ``` 在上面的代码中,我们在组件中引入了封装的getUsers方法,并在mounted钩子函数中调用该方法来获取用户列表,并将返回的数据打印到控制台上。 这样,我们就完成了Vue2封装Axios接口请求的二次封装。通过对Axios进行二次封装,我们可以加方便地使用Axios进行数据请求,并且也可以提高代码的可维护性和复用性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值