Skip to content

request

request 请求封装 + 避免多次重复请求

request.js

js
import axios from 'axios'
import ReactDOM from 'react-dom/client'
import { Spin, Modal, notification } from 'antd'
import { Local, cacheKeys } from '@/utils/storage'
import { BASE_URL } from '@/config/url'

// 缓存请求的接口信息
let requestMap = []

/**
 * 检查是不是重复请求
 * @param {Object} config
 */
const checkRepeatRequest = config => {
  const requestInfo = getRequestInfo(config)

  return requestMap.includes(requestInfo)
}

/**
 * 添加请求
 * @param {Object} config
 */
const addRequest = config => {
  if (!config.openPreventRequest) return
  const requestInfo = getRequestInfo(config)
  requestMap.push(requestInfo)
}
/**
 * 移除请求
 * @param {Object} config
 */
const removeRequest = config => {
  if (!config.openPreventRequest) return

  config.data = config.data && JSON.parse(config.data)

  const requestInfo = getRequestInfo(config)
  const data = requestMap.filter(v => v !== requestInfo)
  requestMap = data
}

/**
 * 获取请求信息
 * @param {Object} config
 */

function getRequestInfo(config) {
  return [config.url, config.method, JSON.stringify(config.params), JSON.stringify(config.data)].join('&')
}

function clearRequestMap() {
  console.log('clear')
  requestMap = []
}

let requestCount = 0

// show loading
function showLoading() {
  if (requestCount === 0) {
    const dom = document.createElement('div')
    dom.setAttribute('id', 'loading')
    document.body.appendChild(dom)
    const root = ReactDOM.createRoot(dom)
    root.render(<Spin tip="Loading..." size="large" />)
  }
  requestCount++
}

// hide loading
function hideLoading() {
  requestCount--
  if (requestCount === 0) {
    document.body.removeChild(document.getElementById('loading'))
  }
}

function paramsToQueryString(params = {}) {
  const queryString = Object.keys(params)
    .map(key => `${key}=${params[key] ?? ''}`)
    .join('&')

  return '?' + queryString
}

const instance = axios.create({
  baseURL: BASE_URL,
  headers: { 'Content-Type': 'application/json;charset=utf-8' },
  timeout: 300 * 1000,
})

function request(options) {
  if (Object.keys(options.params || {}).length > 0) {
    options.url = options.url + paramsToQueryString(options.params || {})
    options.params = {}
  }

  if (options.openPreventRequest) {
    if (checkRepeatRequest(options)) {
      console.log('===cancelRequest===')
      return Promise.reject('cancelRequest')
    } else {
      addRequest(options)
    }
  }

  return new Promise((resolve, reject) => {
    instance
      .request({
        method: options.method,
        url: options.url,
        ...options,
      })
      .then(res => {
        resolve(res)
      })
      .catch(err => {
        reject(err)
      })
  })
}

const requestHandler = async config => {
  const token = Local.get(cacheKeys.Token)
  config.headers.token = token

  showLoading()

  return config
}

const responseHandler = response => {
  removeRequest(response.config)

  const { code } = response.data

  if (code === 401) {
    Modal.confirm({
      title: '注意',
      content: '您的登录状态已过期,是否重新登录?',
      maskClosable: false,

      onOk: () => {
        /**
         * 这里处理清空用户信息和token的逻辑,后续扩展
         */
        history.replace('/login')
      },
    })
  }

  hideLoading()

  return response?.data
}

const errorHandler = error => {
  if (error?.config) {
    removeRequest(error.config)
  }
  if (error.response) {
    const { data, status, statusText } = error.response

    if (status === 401) {
      notification.error({
        message: '401',
        description: data?.msg || statusText,
        duration: 3,
      })
      /**
       * 这里处理清空用户信息和token的逻辑,后续扩展
       */
      deleteUserInfoAndToken()
      history.replace('/login')
    } else if (status === 403) {
      notification.error({
        message: '403',
        description: data?.msg || statusText,
        duration: 3,
      })
    } else if (status === 500) {
      notification.error({
        message: '500',
        description: data?.msg || statusText,
        duration: 3,
      })
    } else {
      notification.error({
        message: '服务错误',
        description: data?.msg || statusText,
        duration: 3,
      })
    }
  }

  return Promise.reject(error)
}

instance.interceptors.request.use(requestHandler, error => Promise.reject(error))

instance.interceptors.response.use(responseHandler, errorHandler)

/**
 *
 * @param {string} url
 * @param {object} params ?customerName=Vallarta&username=admin&password=admin
 * @param {object} config
 * @returns
 */
const get = (url, params = {}, config = {}) => {
  return request({
    url,
    params,
    method: 'get',
    openPreventRequest: true,
    ...config,
  })
}

/**
 *
 * @param {string} url
 * @param {object} data corresponding backend RequestBody
 * @param {object} params ?customerName=Vallarta&username=admin&password=admin
 * @param {object} config
 * @returns
 */
const post = (url, data = {}, params = {}, config = {}) => {
  return request({
    url,
    data,
    params,
    method: 'post',
    openPreventRequest: true,
    ...config,
  })
}

/**
 *
 * @param {string} url
 * @param {string|number} params /api/item/info/{itemId}
 * @param {object} config
 * @returns
 */
const postId = (url, params = '', config = {}) => {
  url += '/' + params
  params = {}
  return request({
    url,
    params,
    method: 'post',
    openPreventRequest: true,
    ...config,
  })
}

/**
 *
 * @param {string} url
 * @param {object} params ?customerName=Vallarta&username=admin&password=admin
 * @param {object} config
 * @returns
 */
const postForm = (url, params = {}, config = {}) => {
  return request({
    url,
    params,
    method: 'post',
    form: true,
    openPreventRequest: true,
    ...config,
  })
}

/**
 *
 * @param {string} url
 * @param {object} data corresponding backend RequestBody
 * @param {object} params ?customerName=Vallarta&username=admin&password=admin
 * @param {object} config
 * @returns
 */
const download = (url, data = {}, params = {}, config = {}) => {
  return request({
    url,
    data,
    params,
    method: 'post',
    responseType: 'blob',
    openPreventRequest: true,
    ...config,
  })
}

export { get, post, postId, postForm, download }

使用

js
import { form } from '@/utils/request'
import { message } from 'antd'

export async function getUserById(params) {
  return form(`/user/getById`, params).then(res => {
    if (res.success) {
      return res.data
    } else {
      message.warning(res.msg)
      return {}
    }
  })
}

Released under the MIT License.