带缓存和防止重复请求,及最大请求次数的axios封装

247 阅读1分钟

基于axios的请求封装

功能点

  • 带有缓存功能
  • 连续点击,能防止重复请求
  • 能支持请求失败后的重试次数
  • 声明式的调用方式

例子

import Axios from 'axios'
import { message } from 'ant-design-vue'
import { HttpRespData, RequestMethod } from './const'
import { getHttpBaseUrl, parseToken, getRequestIdentify } from './utils'
import router from '@/router'
import { handleHttp, getToken, getUid, getVueVersion } from '@/utils'
import { NoNeedTokenRequestMap } from '@/utils/models'

const instance = Axios.create({
  timeout: 30000,
})

const CancelToken = Axios.CancelToken
const source = CancelToken.source()

// map用来保存所有正在请求的id
const requestMap = new Map()

export class Request {
  public maxRetries = 1
  private retries = 0
  public headers = {}
  public requestMethod = 'get'
  public path = ''
  public params = {}
  private resolve = null as any
  private reject = null as any

  constructor() {
    this.addAuthorizationHeader()
    this.interceptorsResponse()
  }

  public send(): Promise<any> {
    // const CancelToken = Axios.CancelToken
    // const source = CancelToken.source()
    // return handleHttp(
    //   instance[this.requestMethod as RequestMethod](this.path, this.params),
    // )
    return new Promise((resolve, reject) => {
      if (!this.resolve && !this.reject) {
        this.resolve = resolve
        this.reject = reject
      }
      return handleHttp(
        instance[this.requestMethod as RequestMethod](this.path, this.params, {
          cancelToken: source.token,
        }),
      )
    })
  }

  public method(method: string) {
    this.requestMethod = method
    return this
  }

  public setPath(path: string) {
    this.path = path
    return this
  }

  public setRetries(times: number) {
    this.maxRetries = times
    return this
  }

  public setHeaders(headers = {}) {
    this.headers = { ...headers, ...this.headers }
    return this
  }

  public setParams(params: any) {
    this.params = params
    return this
  }

  addAuthorizationHeader() {
    let token = getToken() || ''
    const uid = getUid() || ''
    const vueVersion = getVueVersion() || '1'
    if (!token || !uid) {
      router
        .replace({ name: 'login' })
        .then(() => Promise.reject('认证失败, 请重新登录'))
    }
    token = parseToken(token)
    this.headers = { Authorization: `${token};${vueVersion}`, uid }
  }

  static interceptorsRequest() {
    instance.interceptors.request.use(
      (config) => {
        const requestData = getRequestIdentify(config)
        // 取消重复请求
        if (requestMap.has(requestData)) {
          config.cancelToken = new CancelToken((c) => c('重复的请求被主动拦截'))
        }
        requestMap.set(requestData, true)
        config.baseURL = getHttpBaseUrl()
        if (NoNeedTokenRequestMap.get(config.url!)) {
          return config
        }
        let token = getToken() || ''
        const uid = getUid() || ''
        const vueVersion = getVueVersion() || '1'
        if (!token || !uid) {
          router
            .replace({ name: 'login' })
            .then(() => Promise.reject('认证失败, 请重新登录'))
        }
        token = parseToken(token)
        config.headers = {
          ...config.headers,
          ...{ Authorization: `${token};${vueVersion}`, uid },
        }
        return config
      },
      (error) => {
        return Promise.reject(error)
      },
    )
  }

  interceptorsResponse() {
    instance.interceptors.response.use(
      (res) => {
        const respData: HttpRespData = res.data
        const requestData = getRequestIdentify(res.config)
        requestMap.delete(requestData)

        if (respData.result === 0) {
          this.resolve(respData.data)
          return respData.data
        }

        // 请求失败次数加一
        this.retries += 1
        // 请求失败的次数小于maxRetries,则再次发送请求
        if (this.retries < this.maxRetries) return this.send()
        this.retries = 0
        // handle error
        const errData: string = respData.data
        const errMsg: string = errData

        if (errMsg) {
          message.error(errMsg)
        }
        return this.reject(respData)
      },
      (error) => {
        console.log(error)
        if (error.response) return Promise.reject(error.response.data)
        return Promise.reject(error.message)
      },
    )
  }
}

Request.interceptorsRequest()

使用方式

(new Request()).method('get').path('/login').setHeaders({content-type" 'asdad'}).setRetries(8).setParams({password: '1111',username: 'xxxx'}).send()

注意:现在新版本的axios使用AbortController取消请求,示例如下

    const controllMap = new Map();
    
    const commonRequest = (key, url, data) => {
        if (controllMap.has(key)) {
            controllMap.get(key).abort();
        }
        const controll = new AbortControll();
        controllMap.set(key, controll)
        return axios.post(url, {
            ...data,
            signal: controll.signal,
        })
    }