javaScript

请求封装

import axios from 'axios'
import { Toast } from 'vant'
import store from '@/store'
import qs from 'qs'
let userInfo:string = (<any>window).localStorage.getItem('vuex')
let parseToken =  userInfo ? JSON.parse(userInfo).me?.info?.clientToken :''
const token:string = (<any>store) ? (<any>store).state.me.info.clientToken : parseToken

const pendingRequest = new Map() // 请求对象
const CancelToken = axios.CancelToken

const service = axios.create({
  baseURL: process.env.NODE_ENV === 'production' ? '' : process.env.VUE_APP_BASE_API,
  timeout: 60000,
  headers: {
    'Content-Type': 'application/json;charset=UTF-8',
    'x-chat-terminal': 'Client',
    'x-chat-machine': 'Client',
    'x-chat-version': '1.0.0',
  }
})

service.interceptors.request.use(
  config => {
    if (token) {
      if (!config.headers.customize) {
        config.headers = {
          'x-chat-token': token,
          ...config.headers
        }
      } else {
        config.headers = {
          'x-chat-token': token,
          'Content-Type': 'application/json',
        }
      }
    }

    let requestKey = getReqKey(config)
    // 判断是否是重复请求
    if (pendingRequest.has(requestKey)) { // 是重复请求
      removeReqKey(requestKey); // 取消
    } else {
      // 设置cancelToken
      config.cancelToken = new CancelToken(function executor(cancel) {
        pendingRequest.set(requestKey, cancel) // 设置
      })
    }
    return config
  }, error => {
    Promise.reject(error)
  }
)

service.interceptors.response.use(
  response => {
    let requestKey = getReqKey(response.config);
    removeReqKey(requestKey)
    const res = response.data

    // 这里可以判断登录状态 ……


    if (response.status === 200 && res.code === 0) {
      return res
    } else {
      if (res.code != '-95' && res.code !== -145){
        Toast(res?.msg || '服务器错误')
      }
      return Promise.reject(res)
    }
  },
  error => {
    let status:number = 200
    try {
      status = error.response.status
    } catch (e) {
      if (error.toString().indexOf('Error: timeout') !== -1) {
        Toast( '网络请求超时')
        return Promise.reject(error)
      }
      if (error.toString().indexOf('Error: Network Error') !== -1) {
        Toast( '网络请求错误')
        return Promise.reject(error)
      }
      if (status === 401) {
        Toast( '登录状态已过期')
        return Promise.reject(error)
      }
    }

    const statusMap = new Map([
      [400, '参数错误'],
      [401, '需要身份认证'],
      [403, '拒绝访问'],
      [404, '请求错误,未找到该资源'],
      [405, '请求方法被禁止'],
      [406, '请求无法完成'],
      [407, '代理请求需要授权'],
      [408, '服务器等待客户端发送的请求时间过长'],
      [409, '服务器处理请求时发生了冲突'],
      [410, '请求资源被永久删除了'],
      [413, '请求数据太大'],
      [414, '请求url过长'],
      [415, '服务器无法处理请求附带的媒体格式'],
      [500, '服务器端出错!'],
      [501, '不支持的请求'],
      [502, '线路繁忙'],
      [503, '系统升级维护中'],
      [504, '请求超时!请尝试刷新页面重试']
    ])
    if(statusMap.get(status)) {
      Toast(statusMap.get(status))
    }

    let requestKey = getReqKey(error.config)
    removeReqKey(requestKey)
    return Promise.reject(error)
  }
)
export default service


// 获取请求key
function getReqKey(config:any) {
  console.log('config:',config)
  // 请求方式、请求地址、请求参数生成的字符串来作为是否重复请求的依据
  const { method, url, params = {}, data } = config // 解构出来这些参数
  // GET ---> params  POST ---> data
  const requestKey =  [ method, url, qs.stringify(params), qs.stringify(data)].join('&')
  console.log('requestKey:',requestKey)
  return requestKey
}

// 取消重复请求
function removeReqKey(key:string) {
  if( pendingRequest.has(key) ) {
    const cancelToken = pendingRequest.get(key)
    cancelToken(key) // 取消之前发送的请求
    pendingRequest.delete(key) // 请求对象中删除requestKey
  }
}
import axios from 'axios'
const cloneDeep = require('clone-deep')

const instance = axios.create()

//定义缓存对象
//因为需要频繁读写数据,所以这里使用性能更高的map对象
const cacheMap = new Map()

//手动刷新刷新缓存
export function clearCache() {
  cache.clear()
}

//生成请求标识
function generateRequestKey(config, isRes = false) {
  let requestKey = config.url
  // 响应返回的请求配置中的data是字符串,请求发送的请求配置中的data是对象
  if (isRes) {
    requestKey += (config.data ?? '') + (JSON.stringify(config.params) ?? '')
  } else {
    requestKey +=
      (JSON.stringify(config.data) ?? '') +
      (JSON.stringify(config.params) ?? '')
  }
  return requestKey
}

//缓存时效
const cacheExpirationTime = 1000 * 60 * 60 //1小时更新一次

//请求拦截
instance.interceptors.request.use(
  config => {
    //获取当前请求的请求标识
    const key = generateRequestKey(config)
    //读取请求缓存
    const requestCache = cacheMap.get(key)
    //缓存存在并且缓存并未过期,返回缓存,否则正常发送请求
    if (
      requestCache &&
      new Date() - requestCache.cacheDate < cacheExpirationTime
    ) {
      return Promise.reject({
        cached: true,
        key
      })
    }
    //如果当前请求未被缓存,则正常发送请求
    return config
  },
  error => {
    // 对请求错误做些什么
    return Promise.reject(error)
  }
)

//响应拦截
instance.interceptors.response.use(
  res => {
    if (res.data.code === '00') {
      const key = generateRequestKey(res.config, true)
      if (!cacheMap.has(key)) {
        cacheMap.set(key, { cacheDate: new Date(), res: cloneDeep(res.data) })
      } else {
        // 若已存在缓存,并且缓存已过期,则更新缓存
        const requestCache = cacheMap.get(key)
        if (new Date() - requestCache.cacheDate > cacheExpirationTime) {
          cacheMap.set(key, {
            cacheDate: new Date(),
            res: cloneDeep(res.data)
          })
        }
      }
    }
    return res.data
  },
  err => {
    if (err.cached) {
      //返回缓存
      return Promise.resolve(cloneDeep(cacheMap.get(err.key)))
    } else {
      // 对响应错误做点什么
      return Promise.reject(err)
    }
  }
)

export default instance
上次更新: