axios 拦截器与取消 pending 状态请求

/**
 * axios 拦截器配置
 */

import axios from 'axios'
import { Notification } from 'element-ui'
import router from '../router/index.js'

// 跳转到登录页面
const gotoLoginPage = function () {
  // 使用 setTimeout 是为了让 Notification 提示显示出来再跳转
  setTimeout(() => {
    router.replace(`/login?redirect=${encodeURIComponent(location.pathname + location.search)}`)
  }, 1000)
}

axios.defaults.timeout = 5000 // 默认的超时时间
axios.defaults.withCredentials = true // 表示跨域请求时是否需要使用凭证

/**
 * 正在进行(pending 状态)的请求记录
 */
// 记录所有正在进行(pending 状态)的请求的 "唯一值" 和 "取消请求方法"
const allPendingRequestsRecord = []
/**
 * 通过请求的 url 和 method 来标示请求唯一值
 * @param {*} config 配置信息
 * @returns
 */
function getUniqueId (config) {
  return `url=${config.url}&method=${config.method}`
}
/**
 * 取消请求,并移除记录
 * @param {*} config 配置信息
 */
function removePendingRequestRecord (config) {
  allPendingRequestsRecord.some((item, index) => {
    if (item.id === getUniqueId(config)) {
      // console.log('-- cancel id:', item.id)
      item.cancel() // 取消请求
      allPendingRequestsRecord.splice(index, 1) // 移除记录
      return true
    }
  })
  // console.log('-- allPendingRequestsRecord:', allPendingRequestsRecord.length, JSON.stringify(allPendingRequestsRecord))
}
/**
 * 取消所有请求并移除所有记录
 *
 * 页面切换时,取消所有请求并移除所有记录 useAge:
 *    import {removeAllPendingRequestsRecord} from './helper/axios';
 *    router.beforeEach((to, from, next) => {
 *      removeAllPendingRequestsRecord();
 *    }
 */
export function removeAllPendingRequestsRecord () {
  allPendingRequestsRecord.forEach((item) => {
    item.cancel('page changes') // 取消请求
  })
  allPendingRequestsRecord.splice(0) // 移除所有记录
}

// 添加请求拦截器
axios.interceptors.request.use(
  config => {
    // 在发送请求之前做些什么

    // 在 get 请求上带上时间戳
    if (config.method === 'get') {
      config.url = config.url + '?' + new Date().getTime()
    }

    // 通过添加随机 uniqueCancel 值,确保每个请求具有唯一标示
    config.url = `${config.url}?uniqueCancel_${Math.random().toString(36).substr(2)}`

    // 在请求发送前执行一下取消操作,防止重复发送请求(dashboard 类似页面具有重复多次的相同请求,所有不能在全局做防止重复)
    // removePendingRequestRecord(config)

    // 设置请求的 cancelToken
    config.cancelToken = new axios.CancelToken(function executor (cancel) {
      // 添加记录,记录请求的唯一值和取消方法
      allPendingRequestsRecord.push({ id: getUniqueId(config), cancel })
    })
    // console.log('-- request config:', config)

    return config
  },
  error => {
    // 对请求错误做些什么
    Promise.reject(error)
  }
)
// 添加响应拦截器
axios.interceptors.response.use(
  res => {
    // 对响应数据做点什么

    // 307 表示 session 过期,需要重新登录
    if (res.status === 200 && res.data.code === 307) {
      removeAllPendingRequestsRecord()
      Notification.info({
        title: '消息',
        message: '登录失效,请重新登录'
      })

      // 移除菜单和权限信息
      localStorage.removeItem('menus')
      localStorage.removeItem('buttons')
      sessionStorage.removeItem('isLoadNodes')
      // window.location.href = (res.data.content && res.data.content.loginPath) || '/enmoLogin'
      gotoLoginPage()

      return Promise.reject(res)
    }

    // 请求成功后移除记录
    removePendingRequestRecord(res.config)

    if (!res.data) {
      return Promise.reject(res)
    }

    return res
  },
  error => {
    // 对响应错误做点什么

    if (error && error.response) {
      switch (error.response.status) {
        case 400:
          error.message = '错误信息:' + '请求参数错误'
          break
        case 401:
          // 401 说明登录验证失败,需要重新验证
          error.message = '未登录'
          removeAllPendingRequestsRecord()
          gotoLoginPage()

          break
        case 402:
          error.message = '错误信息:您还没有该路径的访问权限'
          break
        case 404:
          error.message = '错误信息:请求地址出错'
          break
        case 500:
          error.message = 'message:' + error.response.data.message + ',exception:' + error.response.data.exception
          break
        case 502:
          error.message = '错误信息:网关错误'
          break
        case 504:
          error.message = '错误信息:网关超时'
          break
        default:
      }

      Notification({
        title: '错误码:' + error.response.status,
        dangerouslyUseHTMLString: true,
        message: error.message,
        type: 'error'
      })
    }

    // 请求失败,移除记录
    removePendingRequestRecord(error.response.config)

    return Promise.reject(error)
  }
)

// Plugin 包装
const axiosPlugin = {
  install (Vue) {
    Vue.prototype.$http = axios
    Vue.prototype.$base = '/commonApi'
    Vue.prototype.$mock = '/mockApi'
  }
}

export default axiosPlugin

// 测试 Plugin 包装:
//   this.$http.get(`${ this.$base }/dbaasDbManage/instance`, {params: {
//     pageSize: 15,
//     pageNum: 1,
//     queryFiled: ''
//   }}).then(function(res){
//     console.log('-- res.data field:', res.data);
//   },function(res){
//     console.log('-- res.status field:', res.status);
//   });

// 测试 Mock 接口:
// this.$http.get(`${this.$mock}/dbaas/getMockInfo`).then(function (res) {
//   console.log('-- res.data field:', res.data)
// }, function (res) {
//   console.log('-- res.status field:', res.status)
// })

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐