uniapp+uview(luch-request)无痛刷新token

2,075 阅读1分钟

森林.jpg

产生原因

获取的token有时效性且不会自动刷新,为了方便封装在请求文件中
需求是不能重新登录而且后端不处理

基本逻辑

 本文根据请求后返回状态判断是否过期,过期则刷新,规定401为token失效
 在刷新token过程中不进入失败回调
 刷新后重新请求并返回数据

代码实现

import { getToken } from '@/api'
import { request } from 'http'

// 是否正在刷新token的标记
let isRefreshing = false
// 重试请求队列
let requests = []

// 此vm参数为页面的实例,可以通过它引用vuex中的变量
module.exports = (vm) => {
  // 初始化请求配置
  uni.$u.http.setConfig((config) => {
    /* config 为默认全局配置*/
    config.custom = {
      auth: true, // token
      toast: true, // 提示
      catch: true, // reject
    }
    config.timeout = 8000
    return config
  })

  // 请求拦截
  uni.$u.http.interceptors.request.use(
    (config) => {
      // 可使用async await 做异步操作
      /* 根域名 */
      config.baseURL = 'https://...'
      // 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{}
      config.data = config.data || {}
      // 根据custom参数中配置的是否需要token,添加对应的请求头
      if (config.custom.auth) {
        // 可以在此通过vm引用vuex中的变量
        config.header.Authorization = vm.vuex_token
      }
      return config
    },
    (config) => {
      // 可使用async await 做异步操作
      return Promise.reject(config)
    }
  )

  // 响应拦截
  uni.$u.http.interceptors.response.use(
    (response) => {
      /* 对响应成功做点什么 可使用async await 做异步操作*/
      const data = response.data
      if (!data.data || !data.stauts){
        // 自定义参数
        const custom = response.config.custom
        // 如果没有显式定义custom的toast参数为false的话,默认对报错进行toast弹出提示
        if (custom.toast) uni.$u.toast(data.message || '请求超时或服务器异常')
        // 如果需要catch返回,则进行reject
        if (custom.catch) return Promise.reject(data)
        else return new Promise(() => {}) // 否则返回一个pending中的promise,请求不会进入catch中
      }
      return data.data ? data.data : data
    },
    async (response) => {
      switch (response.statusCode) {
        case 401:
          if (!isRefreshing) {
            // 正在刷新
            isRefreshing = true
            await getToken()
            requests.forEach(async ({ fn, resolve }) => {
              // 逐个按请求队列顺序重新发起请求
              const res = await fn()
              resolve(res)
            })
            requests = [] // 清空请求队列
            return uni.$u.http.request(response.config)
          } else {
            // 同时并发出现的请求 新的token没回来之前 先用promise 存入等待队列中
            return new Promise((resolve) => {
              const fn = () => Promise.resolve(uni.$u.http.request(response.config))
              requests.push({ fn, resolve })
            })
          }
        default:
          return uni.$u.toast(response.data.msg || '请求超时或服务器异常')
      }
    }
  )
}