uniapp 前端定时刷新token,接口排队等待,promise 接口封装

这篇具有很好参考价值的文章主要介绍了uniapp 前端定时刷新token,接口排队等待,promise 接口封装。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、需求

       此项目为小程序。小程序完成第一版token刷新设计思路是:根据接口调用返回的errorCode来判断当前用户的token和refreshToken是否过期。根据不同的errorCode,前端去调用接口完成token的刷新或者跳转到登录页面重新登录。

       由于小程序的用户功能权限可以在后台管理系统中配置,但是小程序的用户权限变化后,没有办法通知到小程序去更新token或者提示用户重新登录。所以跟后端配合,需要前端增加一个定时调用刷新token接口的需求。比如:后端设置的token是10h过期,那前端就可以每隔5h去调用刷新接口去更新token。新的token接口会返回当前用户最新的权限。从而实现用户功能权限的更新。

二、实现思路

 1、第一版代码(未增加定时刷新功能)接口封装如下

// 封装请求
const request = function(url, params) {
  if (!url) {
    console.log('请传入url......')
    return
  }
  return new Promise((resolve, reject) => {
    // to do
    // 接口调用前 参数params封装

    // 调用uniapp 请求接口
    uni.request({
      url: basePath + url, // 仅为示例,并非真实接口地址。
      data: params.data,
      header: {
        'token': store.getters.getToken,
        'content-type': params.contentType || 'application/x-www-form-urlencoded',
        'refreshToken': store.getters.getRefreshToken,
      },
      method: params.method || 'POST',
    }).then((uniData) => {
      // uniData为一个数组
      // 数组第一项为错误信息 即为 fail 回调
      // 第二项为返回数据
      const [err, uniRes] = uniData;
      if (err) {
        console.log('uni err-------------------:', err)
        uni.showToast({
          icon: 'none',
          title: '服务异常,请稍后重试!'
        })
        return
      }
      const res = uniRes.data || {}
      
      if (res.success) {
        let resultData = res.data || {}
        resolve(resultData)
      } else {
        const code = res.code
        // to do
        // 根据code处理异常

        reject()
      }
    })
  })
}

2. 实现定时刷新思路

  2.1、记录用户信息刷新的时间(使用的时间戳),存vuex:timeOfUserInfo

在一切重新获取了用户权限信息(一定是token变更了)的接口处,记录当前的时间(记录的是用户本地的时间,也是用用户本地的时间去计算的时间差,当本地时间有误的时候,可能会出现接口没有调用的问题)。

  2.2、判断是否调用refresh接口

接口A调用时,根据当前时间与timeOfUserInfo时间差,【1】如果超过3h,则先去调用refresh接口,同时挂起接口A.【2】如果没有超过3h,则直接调用

这里面存在2个问题

(1)、如果页面刷新调用接口时,有多个接口同时调用,那么就会refresh接口就会调用多次

             这时,我们可以用一个变量isRefreshing(默认为false)来记录当前是否在调用刷新接口,调用refresh接口时,isRefreshing设置为true。refresh接口调用成功后,isRefreshing设置为false。

(2)、如何挂起promise

              由于uni.request本来就是一个promise,我们可以直接设置他为pendding状态,放入队列中。等待refresh接口调用成功后,再充队列中将promise取出来,执行

代码如下:

1.模仿axios做了个,请求拦截器,返回拦截器

(1)、请求拦截器

beforeRequest(options = {}) {
      const $this = this
      return new Promise((resolve, reject) => {
        options.url =
          options.url.indexOf('http') === 0
            ? options.url
            : this.baseURL + options.url
        options.method = options.method || 'POST'
        options.data = options.data || {}

        // 封装自己的请求头
        options.header = {
          Authorization: store.getters.getToken,
          refreshToken: store.getters.getRefreshToken
        }
        if (!options.isFile) {
          // 非文件上传
          options.header['content-type'] =
            options.contentType || 'application/x-www-form-urlencoded'
        }
        $this.options = options
        // 排除不需要走refresh token的接口
        if (refreshTokenByTime.excludeUrls.indexOf(options.url) !== -1) {
          resolve(options)
          return
        }

        // 刷新时间到了后
        if (refreshTokenByTime.needRefresh()) {
          // 如果需要刷新token 并且当前token没有在刷新中,则去调用刷新接口
          if (!refreshTokenByTime.isRefresh) {
            // 触发refresh接口的接口,需要暂存起来
            pendingRequests.push({
              resolved: resolve,
              options: options
            })
            refreshTokenByTime.isRefresh = true
            refreshTokenByTime.refreshToken().then(() => {
              // 接口调用成功后,将request中的接口都调用一遍
              // 重点: 用于存储数据
              pendingRequests.forEach(item => {
                // 由于已经更新了token,则需要传新的token了
                item.options.header.Authorization = store.getters.getToken
                item.options.header.refreshToken = store.getters.getRefreshToken
                item.resolved(item.options)
              })
              // 执行完成后,清空队列
              pendingRequests = []
            })
          } else {
            // 这里存储了resolve,变相的实现请求的挂起
            //(只要没有resolved或rejected,请求就会一直处于pedding状态)
            // 并将Promise状态的改变放到了外部一个对象来控制 ,
            // 待定池缓存这个对象即可,待需要执行后续被拦截请求,
            // 只需要利用这个对象引用的 resolved 来改变Promise状态即可实现请求挂起的放行
            pendingRequests.push({
              resolved: resolve,
              options: options
            })
            return pendingRequests
          }
        } else {
          if (options.isShowLoading) {
            uni.showLoading({
              title: options.loadingText || '加载中...',
              mask: true
            })
          }
          resolve(options)
        }
      })
    },

(2)、响应拦截器

响应拦截器

// 响应拦截器
    handleResponse(data) {
      const $this = this
      uni.hideLoading()
      return new Promise((resolve, reject) => {
        const [err, uniRes] = data
        if (err) {
          console.log('uni err-------------------:', err)
          uni.showToast({
            icon: 'none',
            title: '服务异常,请稍后重试!'
          })
          reject()
          return
        }
        let res = uniRes.data || {}
        if (typeof res === 'string') {
          res = JSON.parse(res)
        }
        const options = {
          url: $this.options.url,
          resolve,
          reject
        }
        // 由于传了options,里面包含了resolve和reject,requestCallback方法就是处理接口返回的一 
        // 些异常信息。同样是返回的promise
        return requestCallback(res, options)
      })
    }

主要工作量在:请求拦截器中,如何做多接口的挂起操作。文章来源地址https://www.toymoban.com/news/detail-493690.html

三、代码实现

/**
 * description: 普通请求接口封装
 */
import config from '@/common/base-config.js'
import store from '@/store/index'
import { requestCallback, refreshTokenByTime } from './requestCallback.js'

const basePath = config.url + config.gateway

let pendingRequests = [] // 接口排队队列

function requestObj(options) {
  this.config = {
    baseURL: basePath,
    options: options,
    // 请求拦截器
    beforeRequest(options = {}) {
      const $this = this
      return new Promise((resolve, reject) => {
        options.url =
          options.url.indexOf('http') === 0
            ? options.url
            : this.baseURL + options.url
        options.method = options.method || 'POST'
        options.data = options.data || {}

        // 封装自己的请求头
        options.header = {
          Authorization: store.getters.getToken,
          refreshToken: store.getters.getRefreshToken
        }
        if (!options.isFile) {
          // 非文件上传
          options.header['content-type'] =
            options.contentType || 'application/x-www-form-urlencoded'
        }
        $this.options = options
        // 排除不需要走refresh token的接口
        if (refreshTokenByTime.excludeUrls.indexOf(options.url) !== -1) {
          resolve(options)
          return
        }

        // 刷新时间到了后
        if (refreshTokenByTime.needRefresh()) {
          // 如果需要刷新token 并且当前token没有在刷新中,则去调用刷新接口
          if (!refreshTokenByTime.isRefresh) {
            // 触发refresh接口的接口,需要暂存起来
            pendingRequests.push({
              resolved: resolve,
              options: options
            })
            refreshTokenByTime.isRefresh = true
            refreshTokenByTime.refreshToken().then(() => {
              // 接口调用成功后,将request中的接口都调用一遍
              // 重点: 用于存储数据
              pendingRequests.forEach(item => {
                // 由于已经更新了token,则需要传新的token了
                item.options.header.Authorization = store.getters.getToken
                item.options.header.refreshToken = store.getters.getRefreshToken
                item.resolved(item.options)
              })
              // 执行完成后,清空队列
              pendingRequests = []
            })
          } else {
            // 这里存储了resolve,变相的实现请求的挂起
            //(只要没有resolved或rejected,请求就会一直处于pedding状态)
            // 并将Promise状态的改变放到了外部一个对象来控制 ,
            // 待定池缓存这个对象即可,待需要执行后续被拦截请求,
            // 只需要利用这个对象引用的 resolved 来改变Promise状态即可实现请求挂起的放行
            pendingRequests.push({
              resolved: resolve,
              options: options
            })
            return pendingRequests
          }
        } else {
          if (options.isShowLoading) {
            uni.showLoading({
              title: options.loadingText || '加载中...',
              mask: true
            })
          }
          resolve(options)
        }
      })
    },
    // 响应拦截器
    handleResponse(data) {
      const $this = this
      uni.hideLoading()
      return new Promise((resolve, reject) => {
        const [err, uniRes] = data
        if (err) {
          console.log('uni err-------------------:', err)
          uni.showToast({
            icon: 'none',
            title: '服务异常,请稍后重试!'
          })
          reject()
          return
        }
        let res = uniRes.data || {}
        if (typeof res === 'string') {
          res = JSON.parse(res)
        }
        const options = {
          url: $this.options.url,
          resolve,
          reject
        }
        return requestCallback(res, options)
      })
    }
  }

  // request 请求
  // 下面最主要的一段代码,利用了promise的特性,当调用request方法时,先经过
  // 请求拦截可以拿到请求参数,在请求之前做处理,请求函数会把处理之后的参数作为结果抛出
  // 给了uni.request进行请求,uni.request执行完再次return了promise,接着执行响应
  // 拦截.then中的 response函数,response函数接收到uni请求响应的结果作为res传递给
  // 响应拦截(这里为什么能拿到uni的响应结果因为uni返回的也是promise),在response
  // 就可以对响应的数据处理后再进行promise的返回
  this.request = function request(options = {}) {
    return this.config
      .beforeRequest(options)
      .then(opt => {
        return uni.request(opt)
      })
      .then(res => this.config.handleResponse(res))
  }

  this.requestFile = function requestFile(options = {}) {
    return this.config
      .beforeRequest(options)
      .then(opt => {
        opt.formData = opt.data
        return uni.uploadFile(opt)
      })
      .then(res => this.config.handleResponse(res))
  }
}

module.exports = requestObj

到了这里,关于uniapp 前端定时刷新token,接口排队等待,promise 接口封装的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

阅读剩余 77%

原文地址:https://blog.csdn.net/u011169725/article/details/130599331

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 前端实现token的无感刷新--VUE

    前端实现token的无感刷新--VUE

    token刷新的方案    方案一:后端返回过期时间,前端判断token过期时间,去调用刷新token的接口    缺点:需要后端提供一个token过期时间的字段;使用本地时间判断,若本地时间被修改,本地时间比服务器时间慢,拦截会失败。    方案二:写个定时器,定时刷新token接口

    2024年02月19日
    浏览(9)
  • VUE前端实现token的无感刷新

    VUE前端实现token的无感刷新

    说实话,这个其实没啥好讲的,要说有复杂度的话,也主要是在后端。 实现token无感刷新对于前端来说是一项十分常用的技术,其本质都是为了优化用户体验,当token过期时不需要用户调回登录页重新登录,而是当token失效时,进行拦截,发送刷新token的请求,获取最新的tok

    2024年02月05日
    浏览(9)
  • 记录--前端无感知刷新token & 超时自动退出

    记录--前端无感知刷新token & 超时自动退出

    因为http请求是无状态的,是一次性的,请求之间没有任何关系,服务端无法知道请求者的身份,所以需要鉴权,来验证当前用户是否有访问系统的权限。 以oauth2.0授权码模式为例: 每次请求资源服务器时都会在请求头中添加 Authorization: Bearer access_token 资源服务器会先判断t

    2024年02月03日
    浏览(17)
  • VUE前端实现token的无感刷新,即refresh_token

    通常,对于一些需要记录用户行为的系统,在进行网络请求的时候都会要求传递一下登录的token。不过,为了接口数据的安全,服务器的token一般不会设置太长,根据需要一般是1-7天的样子,token过期后就需要重新登录。不过,频繁的登录会造成体验不好的问题,因此,需要体

    2024年02月09日
    浏览(13)
  • vue/uniapp - 返回上一页并onLoad/onShow刷新数据列表接口

    在uni中,返回页面是不会触发 onLoad 方法的; 如果我们只想在特定情况下返回上一页才需要刷新数据,那么用 onShow 的话,那刷新就太频繁了; 这时候,可以用 $emit 和 $on 去解决。 比如说,从详情页(detail.vue) 回到 列表页(list.vue): 进入的页面 给大佬暴赞👍👍👍👍👍👍

    2024年02月04日
    浏览(15)
  • 【Uniapp】小程序携带Token请求接口+无感知登录方案2.0

    【Uniapp】小程序携带Token请求接口+无感知登录方案2.0

    本次改进原文《【Uniapp】小程序携带Token请求接口+无感知登录方案》,在实际使用过程中我发现以下 bug : 若token恰好在用户访问接口时到期,就会直接查询为空,不反映token过期问题(例如:弹窗显示订单查询记录为空),并不是因为没有数据而是因为token过期了,接口返回

    2024年02月09日
    浏览(10)
  • 微信小程序自动刷新token,无感刷新token

            小程序登录开发通常是调用wx.login获取code,然后发送到后台,后台请求微信拿到用户openId,然后根据openId查询用户,有就走登录流程然后返回token,没有则创建用户之后走登录流程然后返回token,也就是都需要返回一个有时效性的token给小程序端,来保持登录状态,

    2024年02月12日
    浏览(12)
  • IdentityServer4 获取Token及刷新Token

    IdentityServer4 获取Token及刷新Token

    一、获取Token 使用PostMan,调用接口:http://192.168.31.132:7000/connect/token     二、刷新Token  使用PostMan,调用接口:http://192.168.31.132:7000/connect/token  

    2024年02月17日
    浏览(14)
  • .Net6 生成Token 和 刷新Token

    .Net6 生成Token 和 刷新Token

    关于Token的基础概念请自行百度,这里只是记录一下如何生成Token的流程及使用到的技术。 生成Token和验证Token主要使用下面这个包。 在API项目的配置文件中增加关于Token的配置信息。 代码如下: 需要注意的是SecrentKey 的值必须大于16位,不然在生成Token时会报错。 我们先创建

    2024年02月06日
    浏览(8)
  • Vue 无感刷新token

    Vue 无感刷新token

    关于无感刷新的理解:  实现token无感刷新对于前端来说是一项非常常用的技术,其本质是为了优化用户体验,当token过期时不需要用户跳回登录页重新登录,而是当token失效时,进行拦截,发送刷新token的请求,获取最新的token进行覆盖,让用户感受不到token已经过期 刷新token的一些方案

    2024年02月10日
    浏览(12)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包