vue3+axios+useAxios多接口域名检测和加解密(AES)网络请求封装

需要实现的功能

  • vue3+axios+useAxios网络请求封装
  • 主域名返回多个接口域名,循环检测接口域名,状态码为200,即该域名可以用
  • 解密后端返回的加密的数据

实现代码

  • 简单的vue3+axios+useAxios网络请求封装
//useAxiosApi.ts 
import { useAxios } from '@vueuse/integrations/useAxios' // 导入VueUse库中的useAxios函数
import axios from 'axios' // 导入Axios库
import { showToast } from 'vant'
import { useTokenStore } from '@/stores/token'
import { useIsLoggedStore } from '@/stores/isLogged'

//创建一个Axios实例
const instance = axios.create({
  baseURL'https://uuiiiiiiew/'// 替换成你想要的baseURL
  withCredentialsfalse// 是否携带凭证,默认为false
  timeout10000 // 请求超时时间,默认为5秒
})
instance.defaults.headers.common['Access-Control-Allow-Origin'] = '*'
//添加请求拦截器
instance.interceptors.request.use(
  (config) => {
    const tokenStore = useTokenStore()
    const x_token = tokenStore.x_token
    const isLoggedStore = useIsLoggedStore()
    if (!x_token) {
      isLoggedStore.logoutState()
    }
    if (x_token) {
      config.headers = {
        ...config.headers,
        Authorization`Bearer ${x_token}`
      }
    }
    return config
  },
  (error) => {
    // 请求错误的处理逻辑
    return Promise.reject(error)
  }
)

instance.interceptors.response.use(
  (response) => {
    if (response.status !== 200 && response.status !== 201) {
      // 401: Token过期;
      if (response.status === 401) {
        //返回登录页面
        showToast('未登录,请先登录')
      }
      return Promise.reject(response.data.data || 'Error')
    } else {
      return response
    }
  },
  (error) => {
    console.log('err' + error)
    // showToast(error.message)
    return Promise.reject(error.response.data)
  }
)

//导出
export default function useAxiosApi(url, config{
  //console.log(url, config)
  return useAxios(url, config, instance)
}

  • 加上多接口域名返回和接口检查以及解密
//useAxiosApi.ts
import { useAxios } from '@vueuse/integrations/useAxios' // 导入VueUse库中的useAxios函数
import { Decrypt, isBase64 } from './secret' //解密
import axios from 'axios' // 导入Axios库
import { showToast } from 'vant'
import { useTokenStore } from '@/stores/token'
import { useIsLoggedStore } from '@/stores/isLogged'

//创建一个新的Axios实例
const mainDomainInstance = axios.create({
  baseURL'http://19.142.34.1:3000/'// 把这个替换成你的主域名
  timeout10000 // 请求超时时间,默认为10秒
})
// 存储多个域名
let allUrls = []
//选中的接口域名
let selectedUrl = ''
// 使用新的Axios实例请求主域名,获得多个域名
async function getServerUrls({
  const { data: urls } = await mainDomainInstance.get('/api/v1/domain/list')
  //需要单独解密
  allUrls = JSON.parse(Decrypt(urls)).data
}
//域名检测
async function checkServers({
  if (allUrls.length > 0) {
    for (const url of allUrls) {
      try {
        const response = await mainDomainInstance.get(url.domain + '/api/v1/banners')
        if (response.status === 200) {
          selectedUrl = url.domain // 如果服务器可用,设置为选中
          break // 停止循环,不再检查剩余服务器
        }
      } catch (err) {
        // 如果服务器不可用,打印错误并继续循环
        console.error(`Error checking server `, err)
      }
    }
  }
}
//放到一个方法里面,统一调用
async function setup({
  await getServerUrls()
  await checkServers()
}

async function createInstance({
  await setup() // 确保setup函数执行完成
  //创建一个Axios实例
  const instance = axios.create({
    baseURL: selectedUrl, // 使用到的接口域名
    withCredentialsfalse// 是否携带凭证,默认为false
    timeout10000 // 请求超时时间,默认为5秒
  })
  instance.defaults.headers.common['Access-Control-Allow-Origin'] = '*'
  //添加请求拦截器
  instance.interceptors.request.use(
    (config) => {
      const tokenStore = useTokenStore()
      const x_token = tokenStore.x_token
      const isLoggedStore = useIsLoggedStore()
      if (!x_token) {
        isLoggedStore.logoutState()
      }
      if (x_token) {
        config.headers = {
          ...config.headers,
          Authorization`Bearer ${x_token}`
        }
      }
      return config
    },
    (error) => {
      // 请求错误的处理逻辑
      return Promise.reject(error)
    }
  )

  instance.interceptors.response.use(
    (response) => {
    //检测接口是否是被加密的
      // console.log('123456', isBase64(response.data))
      if (isBase64(response.data)) {
        if (response.status !== 200 && response.status !== 201) {
          // 401: Token过期;
          if (response.status === 401) {
            //返回登录页面
            showToast('未登录,请先登录')
          }
          return Promise.reject(JSON.parse(Decrypt(response.data)) || 'Error')
        } else {
          response.data = JSON.parse(Decrypt(response.data))
          return response
        }
      } else {
      //未被加密的
        if (response.status !== 200 && response.status !== 201) {
          // 401: Token过期;
          if (response.status === 401) {
            //返回登录页面
            showToast('未登录,请先登录')
          }
          return Promise.reject(response.data.data || 'Error')
        } else {
        
          return response
        }
      }
    },
    (error) => {
      console.log('err' + error)
      if (isBase64(error.response.data)) {
        error.response.data = JSON.parse(Decrypt(error.response.data))
        return Promise.reject(error.response.data)
      } else {
        return Promise.reject(error.response.data)
      }
    }
  )

  return instance
}

//导出
export default async function useAxiosApi(url, config{
  //console.log(url, config)
  const instance = await createInstance()
  return useAxios(url, config, instance)
}
  • secret.ts 文件
import CryptoJS from 'crypto-js/crypto-js'
const key = CryptoJS.enc.Utf8.parse('4643647'// 密钥
const iv = CryptoJS.enc.Utf8.parse('437457'// 偏移量
/**
 * AES 解密 :字符串 key iv  返回base64
 *  */

export function Decrypt(word{
  const base64 = CryptoJS.enc.Base64.parse(word)
  const src = CryptoJS.enc.Base64.stringify(base64)

  const decrypt = CryptoJS.AES.decrypt(src, key, {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
  })
  return CryptoJS.enc.Utf8.stringify(decrypt)
}
/**
 * AES加密 :字符串 key iv  返回base64
 */

export function Encrypt(word{
  const srcs = CryptoJS.enc.Utf8.parse(word)
  const encrypted = CryptoJS.AES.encrypt(srcs, key, {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
  })
  return CryptoJS.enc.Base64.stringify(encrypted.ciphertext)
}

//判断是否已经加密
export function isBase64(str{
  const base64Regex =
    /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$/
  return base64Regex.test(str)
}

  • 发现的问题

我们发现每次请求的时候,都会去检查域名,我们的要求是:只需要进入页面的时候请求一次接口检测,不需要每次请求都触发接口检测。

  • 解决方法

可以在模块级别执行setup函数。这样,当模块首次加载时,setup会执行一次,而之后的请求就不会再触发它。由于模块只会加载一次,所以setup也只会执行一次。为此,需要使用.then链将Axios实例的创建延迟到setup执行完成之后。createInstance函数不再需要等待setup,因为当instance被创建时,setup已经完成了。

  • 最终代码
//useAxiosApi.ts
import { useAxios } from '@vueuse/integrations/useAxios'
import { Decrypt, isBase64 } from './secret'
import axios from 'axios'
import { showToast } from 'vant'
import { useTokenStore } from '@/stores/token'
import { useIsLoggedStore } from '@/stores/isLogged'

let allUrls = []
let selectedUrl = ''
let serverCheckedInstance = null

const mainDomainInstance = axios.create({
  baseURL'http://19xx.143.24.1:3000/',
  timeout10000
})

async function getServerUrls({
  const { data: urls } = await mainDomainInstance.get('/api/v1/domain/list')
  allUrls = JSON.parse(Decrypt(urls)).data
}

async function checkServers({
  if (allUrls.length > 0) {
    for (const url of allUrls) {
      try {
        const response = await mainDomainInstance.get(url.domain + '/api/v1/banners')
        if (response.status === 200) {
          selectedUrl = url.domain
          break
        }
      } catch (err) {
        console.error(`Error checking server `, err)
      }
    }
  }
}

const setup = async () => {
  await getServerUrls()
  await checkServers()
  serverCheckedInstance = axios.create({
    baseURL: selectedUrl,
    withCredentialsfalse,
    timeout10000
  })

  serverCheckedInstance.defaults.headers.common['Access-Control-Allow-Origin'] = '*'

  serverCheckedInstance.interceptors.request.use(
    (config) => {
      const tokenStore = useTokenStore()
      const x_token = tokenStore.x_token
      const isLoggedStore = useIsLoggedStore()
      if (!x_token) {
        isLoggedStore.logoutState()
      }
      if (x_token) {
        config.headers = {
          ...config.headers,
          Authorization`Bearer ${x_token}`
        }
      }
      return config
    },
    (error) => {
      return Promise.reject(error)
    }
  )

  serverCheckedInstance.interceptors.response.use(
    (response) => {
      if (isBase64(response.data)) {
        if (response.status !== 200 && response.status !== 201) {
          if (response.status === 401) {
            showToast('未登录,请先登录')
          }
          return Promise.reject(JSON.parse(Decrypt(response.data)) || 'Error')
        } else {
          response.data = JSON.parse(Decrypt(response.data))
          return response
        }
      } else {
        if (response.status !== 200 && response.status !== 201) {
          if (response.status === 401) {
            showToast('未登录,请先登录')
          }
          return Promise.reject(response.data.data || 'Error')
        } else {
          return response
        }
      }
    },
    (error) => {
      if (isBase64(error.response.data)) {
        error.response.data = JSON.parse(Decrypt(error.response.data))
        return Promise.reject(error.response.data)
      } else {
        return Promise.reject(error.response.data)
      }
    }
  )
}

// Start the setup process immediately
setup()

export default async function useAxiosApi(url, config{
  const getInstance = () => {
    if (!serverCheckedInstance) {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(getInstance())
        }, 500)
      })
    }
    return Promise.resolve(serverCheckedInstance)
  }

  const instance = await getInstance()
  return useAxios(url, config, instance)
}

现在,当你的JavaScript模块(即useAxiosApi.ts)加载时,会立即开始执行setup函数,该函数将获取接口域名,然后检查其可用性,然后创建一个配置好的Axios实例。稍后,当你需要发起HTTP请求时,useAxiosApi函数将等待Axios实例创建完成,然后进行请求。这样能确保setup只执行一次,且所有请求都在正确的接口域名上执行。


原文始发于微信公众号(大前端编程教学):vue3+axios+useAxios多接口域名检测和加解密(AES)网络请求封装

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/288877.html

(0)
土豆大侠的头像土豆大侠

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!