如何更加完善的封装axios请求:防抖拦截、无感刷新token、各环境baseUrl配置等

前言

先看下常见的封装请求及使用方法:

1.新建request.js,内容为:

import axios from 'axios'

const service = axios.create({
  baseURL: '',
  timeout: 3000,
  headers: {}
})
service.interceptors.request.use(
  config => {
  // 这里是添加token、判断登录权限等等一些处理逻辑
  return config
},
  error => {
    Promise.reject(error)
  }
)

service.interceptors.response.use(
  res => {
  // 这里是一些处理逻辑
  return res.data
},
  error => {
    return Promise.reject(error)
  }
)
export default service

2.新建一些api文件如 /api/user.js,内容为:

import request from '@/utils/request'

// 用户登录
export function userLogin(data) {
  return request({
    url: '/api/user/login',
    method: 'POST', // post请求
    data
  })
}

// 获取列表信息
export function userList(id) {
  return request({
    url: '/api/user/list?id='+id,
    method: 'GET' // get请求
  })
}

// 文件下载类
export function fileMyDownload(data) {
  return request({
    url: '/api/file/my/file/download/' + data,
    method: 'GET',
    responseType: 'blob'
  })
}

3.页面引入使用

import {userLogin,userList,fileMyDownload} from '/api/user.js'

//页面中的请求方法
userLogin(data).then()
userList(data).then()
fileMyDownload(data).then()

小结

上面这写法api下全是export function,接口多了然后就是几百成千行类似的代码或者是被拆分成几十个js文件。页面使用中也需根据实际import或移除相应的接口方法名称。对于这问题可继续优化完善。

优化

优化api

首先将整合所有的export function方法,将多个export function封装为getRequest。优化为:

import request from '@/utils/request'

const api={
  userLogin:'/user/login',
  userList:'/user/list',
  getUserList:'get|/user/list', // 不同请求类型可在url前添加,使用|分隔
  getUserListById:'/user/list/$id' // 动态参数id时,可添加$标识符
  download:'/user/my/file/download'
}

export const getRequest = (apiKey,data, options= {}) => {
  let url = api[apiKey] || apiKey
  if (Object.keys(options.apiKey || {}).length) {
    for (const key in options.apiKey) {
      url = url.replace(key, options.apiKey[key])
    }
  }
  let method = 'POST' // 默认请求方式
  // 如果url带有|分隔符,提取|前面的作为请求method并过滤掉|前面的
  if (url.indexOf('|') !== -1) {
    method = url.split('|')[0]
    url = url.replace(/.*\|/, '')
  }
  return Object.assign(
    {
      url: '/api'+ url, // 添加前缀
      method: method,
      data
    },
    options
  )
}

页面使用

import { getRequest } from '/api/getRequest'

  export default {
    mounted() {
      const data = {}
      // 1.正常请求,以post方式请求/api/user/login
      getRequest('userLogin', data).then()
      // 2.以get方式请求/api/user/list。同时可在request.js中设置将data转为params,即追加到url后面
      // 2.1 url接口前添加get|
      getRequest('getUserList', data).then()
      // 2.2使用options参数
      getRequest('userList', data, { method: 'get' }).then()
      // 2.3不使用api定义的,直接使用接口url
      getRequest('get|/user/list', data).then()
      // 或
      getRequest('/user/list', data, { method: 'get' }).then()
      //3.url带参数的,使用options参数的apikey,即可正常请求/api/user/list/1
      getRequest('getUserListById', data, { apiKey: { $id: '1' } }).then()
      //4.数据流下载类,添加responseType,同时还可以定义不同的headers
      getRequest('download', data, { responseType: 'blob', headers: {} }).then()
    }
  }

经过上面优化封装,可解决所有不同的请求,可大量减少类似的重复代码,更方便阅读。同步的request.js可优化为:

除了常见的添加token请求头外,还增加了:

1. 在get请求时将data参数转为params方式,直接拼接到url后面;

3. 请求防抖拦截,同一个接口和参数在一定时间内不重复发请求

4. 无感刷新token

5.不同环境baseUrl配置

import * as md5 from 'md5'

//5 通过运行的命令获取各不同环境的请求api等,无需在根目录添加如.env.development等文件
const mode = import.meta.env.MODE
const baseUrl: any = {
  development: '', // 默认命令dev
  production: '', // build
  preRelease: '' // 自定义其他环境如 "build:pre": "vite build --mode preRelease"
}

const service = axios.create({
  baseURL: baseUrl[mode],
  timeout: 3000, // request timeout
  headers: {}
})

const axiosList = {}

/***************************无感刷新换token相关****************/
let refreshTokenAjax: boolean = false
// 存储请求的数组
const subscribesArr = []

// 请求push到数组中
function subscribesArrRefresh(cb) {
  subscribesArr.push(cb)
}

// 用新token发起请求
function reloadSubscribesArr(newToken) {
  subscribesArr.map(cb => cb(newToken))
}

// 使用refreshToken请求获取新的token
function getNewToken(refreshToken) {
 //根据接口需求,处理刷新所需参数
  const params: any = { refreshToken: refreshToken }
  axios
    .post('/mock/system/user/token.json', params)
    .then((result: any) => {
      // 刷新成功,这里需要将新的token保存
      // .... 一些保存逻辑
      reloadSubscribesArr(data.token)
      refreshTokenAjax = false
    })
    .catch(() => {
      //... 换取token失败的处理逻辑,一般为退出到登录页
      refreshTokenAjax = false
    })
}
/***************************无感刷新换token相关结束****************/

service.interceptors.request.use(
  config => {
    //1. get请求时,将参数放到url后面
    if (config.method.toUpperCase() === 'GET') {
      config.params = config.data
    }
    //2. 让每个请求携带自定义token 请根据实际情况自行修改
    const token = '' // 一般从store或storage里获取
    if (token) {
      config.headers['Authorization'] = token
    }
    //3. 全局防抖拦截,请根据实际情况自行修改
    const urlKey = md5(config.url + JSON.stringify(config.data || {}))
    const lastTime = axiosList[urlKey]
    if (lastTime) {
      const diffTime = new Date().getTime() - lastTime
      if (diffTime < 1500) {
        //间隔时间,小于此时间视为重复提交
        return Promise.reject({ code: 1, msg: '数据正在处理,请勿重复提交' })
      }
    }
    axiosList[urlKey] = new Date().getTime() // 保持或更新请求记录
    // 添加记录,然后自动清除
    setTimeout(() => {
      delete axiosList[urlKey]
    }, 5000)
    // 全局防抖拦截结束
   // 4.无感刷新token开始
  const refreshToken = '' // 一般从store或storage里获取刷新token所需的refreshToken 
    if (!token && refreshToken) { //token过期逻辑判断
      if (!refreshTokenAjax) {
        getNewToken(refreshToken)
      }
      refreshTokenAjax = true
      return new Promise(resolve => {
        subscribesArrRefresh(newToken => {
          config.headers['Authorization'] = newToken
          resolve(config)
        })
      })
    }
  // 无感刷新token结束
    return config
  },
  error => {
    Promise.reject(error)
  }
)

总结

至此封装一个getRequest方法即满足所有接口请求,新增接口url时直接修改api={}即可,在页面引入使用时也无需单独引入,统一引入同一方式即可。减少了大量的代码

GitHub - 337547038/vue-form-design: ak-design可视化低代码快速开发平台,使用基于vue3.x桌面端组件库 Elemnet-Plus,通过可视化的操作,可轻松快速完成拖拽表单编辑设计器、数据列表页设计、流程管理设计器、数据大屏可视化拖拽设计编辑器、数据统计拖拽设计;提供功能强大的各类组件,可适用于各种复杂场景,代码简洁、易于二次开发;用于学习研究,欢迎交流,微信:337547038

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
无感刷新token是指在前端发起请求时,如果当前的token已过期,则自动刷新token,并重新发起请求,达到用户无感知的效果。在axios中,可以通过拦截器实现无感刷新token的功能。 在axios中,我们可以通过使用请求拦截器和响应拦截器来实现无感刷新token的逻辑。首先,我们可以在请求拦截器中判断当前的token是否已过期。如果token未过期,则直接返回请求对象,如果token已过期,则在请求拦截器中发起刷新token请求。 在刷新token请求中,我们可以使用当前过期的token请求后端接口,然后根据后端返回的结果来判断是否成功刷新token。如果刷新token成功,则将新的token更新到请求信息中,并重新发起之前的请求。 在响应拦截器中,我们可以判断后端返回的响应状态码。如果响应状态码为401,说明当前的token已过期,则调用刷新token的逻辑重新获取token,并再次发起请求。如果刷新token请求失败,则说明用户的登录状态无效,可以跳转到登录页面重新登录。 通过以上的逻辑,我们可以实现无感刷新token的效果,用户不需要手动处理token过期的问题,从而提升用户体验。同时在后端也需要配合实现刷新token的逻辑,并提供相应的接口供前端调用。 无感刷新token的实现涉及到了前后端的配合,前端通过axios拦截器实现token的自动刷新,后端提供相关的接口。通过这种方式,可以有效解决token过期后需要重新登录的问题,提升应用的用户体验。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值