sanlangguo / learn-notes

学习笔记
16 stars 1 forks source link

axios 错误重传 #15

Open sanlangguo opened 2 years ago

sanlangguo commented 2 years ago

需求背景动态域名切换--axios 请求失败时,切换当前域名,用新的域名请求

  1. 错误状态获取
全局拦截 axios 处理

import axios from 'axios'
import { config } from '@/api/config.js'
import axiosRetry from 'axios-retry'
import { getSpareDomains } from '@/api'

// axios 公共变量
const axiosConf = {
  local: 'test-domain',
  timeout: 50000,
  headers: {}
}

/ *
 * uat dev prod 域名不一致,可能调用多个服务, 分开写 DOMAIN_Conf_1,有逻辑可以共用
 */
const DOMAIN_Conf_1 = {
  ...axiosConf,
  uat: 'uat1-com',
  production: 'prod1-com'
}

const DOMAIN_Conf_2 = {
  ...axiosConf,
  uat: 'uat2-com',
  production: prod2-com'
}

export const api_1 = axios.create({
  baseURL: config.url_1,
  ...DOMAIN_Conf_1
})

export const api_2 = axios.create({
  baseURL: config.url_2,
    ...DOMAIN_Conf_2
})

const services = [
  api_1,
  api_2,
]

services.forEach(service => {
  axiosRetry(
    service,
    { retries: 0 },
    {
      retryCondition (error) {
        return error.code === 'ECONNABORTED' // Retry timed out requests
      }
    }
  )
  // response interceptors
  service.interceptors.response.use(
    response => {
      if (response.status == 200) {
        return response.data
      }
    },
    async (error) => {
    // 符合错误类型时,调用
      if (error && error.message && error.message.includes('Network')) {
        const res = await getSpareDomains(error)
        return res
      } else {
        return Promise.reject(error)
      }
    }
  )
})
  1. 错误重试逻辑分开放到单独文件处理,便于维护,并且如果备案域名也不能访问的情况下,会依次轮询接口
  1. js 不能本机去 ping 域名是否可以使用 2. 如果通过接口去 ping 不能模拟本地的网络是否能访问 (例如上海联通不通,其他地方的联通是可以通的,所以只能本机轮训备用域名)
api/index.js

// 测试数据
// const resObj = {
//   'test-domain': 'test-domain-1, test-domain-2',
//   'uat1-com': 'uat1-com-1, uat1-com-2',
//   'prod1-com: prod1-com-1, prod1-com-2',
// }

import axios from 'axios'
import { HTTPS } from '@/api/config.js'
const env = process.env.VUE_APP_WEB

// 公共业务域名接口
const API = axios.create({
  baseURL: HTTPS.url,
  timeout: 50000,
})

API.interceptors.response.use(
  response => {
    // eslint-disable-next-line
    if (response.status == 200) {
      return response.data
    }
  },
  error => {
    return Promise.reject(error)
  }
)

// update deposit account number
export const getSpareDomains = async (error) => {
// 防止多次请求备案域名
  const domains = JSON.parse(window.sessionStorage.getItem('domains'))
  let url = error.config[env]
  if (domains) {
    url = await checkDomain(error, domains)
  } else {
    const res = await API.get('api/1/app/param/config/domain')
    if (res.code === 1 && res.data) {
      for (const key in res.data) {
        if (res.data[key]) {
          res.data[key] = res.data[key].replace(/\s+/g, '')
          res.data[key] = res.data[key].split(',')
        }
      }
      window.sessionStorage.setItem('domains', JSON.stringify(res.data))
      url = await checkDomain(error, res.data)
    }
  }
  return url
}

const checkDomain = async function (error, data) {
  const checkName = error.config[env]
  let result = null // 返回响应值
  for (const key in data) {
    if (key === checkName && data[key].length) {
      for (let i = 0; i < data[key].length; i++) {
        if (!result) {
          result = await retryHttps(error, data[key][i])
        } else {
          return result
        }
      }
    }
  }
  return result
}

async function retryHttps (error, url) {
  error.config.baseURL = error.config.baseURL.replace(error.config.baseURL.split('/')[2], url)
  error.config.url = error.config.url.replace(error.config.url.split('/')[2], url)
  return axios.request(error.config).then(res => {
    return res.data
  }).catch(e => {
    return false
  })
}