前端接口请求支持内容缓存和过期时间

news2024/11/17 12:46:04

前端接口请求支持内容缓存和过期时间

支持用户自定义缓存时间,在规则时间内读取缓存内容,超出时间后重新请求接口

首先封装一下 axios,这一步可做可不做。但是在实际开发场景中都会对 axios 做二次封装,我们在二次封装的 axios 基础上继续封装,增加支持缓存功能

request.js

import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
import cache from '@/plugins/cache'
import qs from 'qs'

// 本地开发环境需要加请求头
if (process.env.NODE_ENV === 'development') {
  axios.defaults.headers['tenantId'] = 'yikon'
}
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
axios.defaults.headers['lang'] = 'CN'
// 创建axios实例
const service = axios.create({
  // axios中请求配置有baseURL选项,表示请求URL公共部分
  baseURL: process.env.VUE_APP_BASE_API,
  // 超时
  timeout: 100000,
})

// request拦截器
service.interceptors.request.use(
  (config) => {
    // 是否需要设置 token
    const isToken = (config.headers || {}).isToken === false
    // 是否需要防止数据重复提交
    const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
    if (getToken() && !isToken) {
      config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
    }
    // get请求映射params参数
    if (config.method === 'get' && config.params) {
      let url = config.url + '?' + qs.stringify(config.params)
      url = url.slice(0, -1)
      config.params = {}
      config.url = url
    }
    if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
      const requestObj = {
        url: config.url,
        data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
        time: new Date().getTime(),
      }
      const sessionObj = cache.session.getJSON('sessionObj')
      if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
        cache.session.setJSON('sessionObj', requestObj)
      } else {
        // 忽略重复请求的地址
        const exUrls = [
          '/bioTask/uploadFileByCondition',
          '/LabTq/updateTqTaskRecordById',
          '/cskSamle/uploadVcfFile',
          '/cskReport/updateReport',
        ]

        const s_url = sessionObj.url // 请求地址
        const s_data = sessionObj.data // 请求数据
        const s_time = sessionObj.time // 请求时间
        const interval = 3000 // 间隔时间(ms),小于此时间视为重复提交
        if (
          s_data === requestObj.data &&
          requestObj.time - s_time < interval &&
          s_url === requestObj.url &&
          !exUrls.includes(config.url)
        ) {
          const message = '数据正在处理,请勿重复提交'
          return Promise.reject(new Error(message))
        } else {
          cache.session.setJSON('sessionObj', requestObj)
        }
      }
    }
    return config
  },
  (error) => {
    Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  (res) => {
    // 未设置状态码则默认成功状态
    const code = res.data.code || '0'
    // 获取错误信息
    const msg = res.data.message
    // 二进制数据则直接返回
    if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
      return res.data
    }
    if (code === 401 || code === '10006') {
      MessageBox.confirm('登录状态已过期,请重新登录', '系统提示', {
        confirmButtonText: '重新登录',
        cancelButtonText: '取消',
        type: 'warning',
      }).then(() => {
        store.dispatch('LogOut').then(() => {
          location.href = '/login'
        })
      })
    } else if (code !== '0') {
      Message({
        message: msg || '接口请求异常',
        type: 'error',
      })
      return Promise.reject(new Error(msg))
    } else {
      return res.data
    }
  },
  (error) => {
    let { message } = error
    if (message === 'Network Error') {
      message = '后端接口连接异常'
    } else if (message.includes('timeout')) {
      message = '系统接口请求超时'
    } else if (message.includes('Request failed with status code')) {
      message = '系统接口' + message.substr(message.length - 3) + '异常'
    }
    Message({
      message: message,
      type: 'error',
      duration: 5 * 1000,
    })
    return Promise.reject(error)
  }
)

export default service

新建 catchAjax.js ,当我们想用接口缓存时,就用 catchAjax 方法,不想用时还用上面的 request 文件,互不影响

const cacheMap = new Map()
// 定义状态池
const statusMap = new Map()
// 回调callbackMap
const callbackMap = new Map()
// 引入axios
import myAxios from '@/utils/request'
// qs用于序列化对象,将对象序列化为用&拼接的参数
import qs from 'qs'

// 一般只缓存GET接口
function generateCacheKey(request) {
  return request.url + '?' + qs.stringify(request.params)
}

// 返回指定分钟后的时间戳 过期时间
function generateExpTime(minutes) {
  // 获取当前时间戳
  let now = new Date()
  // 添加分钟数
  now.setMinutes(now.getMinutes() + minutes)
  // 返回未来的时间戳
  return now.getTime()
}

// 导出请求方法
export function cacheRequest(request) {
  if (request.method && request.method.toUpperCase() !== 'GET') {
    throw new Error('cacheRequest 仅支持GET请求')
  }
  if (request.expTime && !/^\d+$/.test(request.expTime)) {
    throw new Error('expTime 必须是正整数')
  }
  // 用当前请求的 url + 参数 来当做缓存的key
  const cacheKey = generateCacheKey(request)
  // 判断状态池中是否有数据
  if (statusMap.has(cacheKey)) {
    // 获取当前的状态
    const currentStatus = statusMap.get(cacheKey)
    // 如果接口已经在缓存中,则进入这里
    if (currentStatus === 'complete') {
      // 判断是否过期
      let nowTime = new Date().getTime()
      // 已经过期的数据不能从缓存中取,设置这个状态是pending,重新走接口
      if (nowTime >= cacheMap.get(cacheKey).expTime) {
        statusMap.set(cacheKey, 'pending')
      } else {
        // 没有过期则从缓存中返回数据
        return Promise.resolve(cacheMap.get(cacheKey)?.data)
      }
    }

    if (currentStatus === 'pending') {
      // 判断回调池中是否有数据
      return new Promise((resolve, reject) => {
        if (callbackMap.has(cacheKey)) {
          callbackMap.get(cacheKey).push({
            onSuccess: resolve,
            onError: reject,
          })
        } else {
          callbackMap.set(cacheKey, [
            {
              onSuccess: resolve,
              onError: reject,
            },
          ])
        }
      })
    }
  }
  // 设置接口状态
  statusMap.set(cacheKey, 'pending')

  // 判断是否需要缓存,并且缓存池中有数据时,返回缓存池中的数据
  return myAxios(request)
    .then((res) => {
      // 接口响应成功后吧当前的请求状态设置为complete,下次请求时就会走缓存,不会走网络
      statusMap.set(cacheKey, 'complete')
      // 往缓存中赛数据,同时设置过期时间
      cacheMap.set(cacheKey, {
        data: res,
        // 默认缓存5分钟
        expTime: generateExpTime(request.expTime || 5),
      })
      // 判断在接口响应期间是否有请求,如果有请求,则遍历所有的回调并执行
      if (callbackMap.has(cacheKey)) {
        callbackMap.get(cacheKey).forEach((callback) => {
          callback.onSuccess(res)
        })
        // 响应完数据后吧回调删除
        callbackMap.delete(cacheKey)
      }
      // 返回真实的接口数据
      return res
    })
    .catch((error) => {
      statusMap.delete(cacheKey)
      if (callbackMap.has(cacheKey)) {
        callbackMap.get(cacheKey).forEach((callback) => {
          callback.onError(error)
        })
        callbackMap.delete(cacheKey)
      }
      return Promise.resolve(error)
    })
}

使用方法

<template>
  <div>
    <el-button type="primary" @click="cacheAxios">测试</el-button>
  </div>
</template>

<script>
import { cacheRequest } from '@/utils/catchAjax'

const getArticleList = (params) => {
  return cacheRequest({
    url: 'http://localhost:10086/order/list',
    method: 'get',
    params,
    expTime: 1, // 缓存一分钟
  })
}

export default {
  name: 'index',
  methods: {
    cacheAxios() {
      getArticleList({
        pageNum: 1,
        pageSize: 10,
      }).then((res) => {
        console.log(res)
      })
    },
  },
}
</script>

image-20231030181210907

我们在 1 分钟内连续点击按钮,发现只会走一次接口,但是控制台可以打印多次数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1150746.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis入门指南学习笔记(1):初识Redis

一.什么是Redis? Redis全称为Remote Dictionary Server&#xff0c;即远程字典服务器&#xff0c;它采用字典结构来存储数据&#xff0c;并允许其他应用通过TCP协议来访问数据。 字典在众多开发语言中都很常见&#xff0c;其形式为键值对&#xff0c;根据键可以获取相应的值…

文件名替换,关键字替换改名:不同路径中的多个文件如何批量重命名

在日常生活和工作中&#xff0c;我们经常需要处理大量的文件&#xff0c;包括重命名、分类、整理等操作。其中&#xff0c;批量重命名不同路径中的多个文件是一项非常常见的任务。本文将介绍云炫文件管理器常见的批量重命名方法&#xff1a;文件名关键字替换改名&#xff0c;帮…

python类模拟“对战游戏”

Game类含玩家昵称、生命值、攻击力(整数)&#xff0c;暴击率、闪避率(小数)&#xff0c;在魔术方法init定义&#xff1b;attack方法中实现两个Game实例对战模拟。 (本笔记适合初通Python类class的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.py…

请输入正确的小程序的链接,并确保小程序允许被搜索

公众号文章插入小程序时提示“请输入正确的小程序的链接&#xff0c;并确保小程序允许被搜索”&#xff1a; 这是因为你输入的是小程序路径&#xff0c;不是小程序链接。 如果是你想使用小程序路径&#xff0c;需要点击第一张图中的蓝色字“去搜索”&#xff0c;然后搜索选择你…

少儿编程 2023年9月中国电子学会图形化编程等级考试Scratch编程四级真题解析(判断题)

2023年9月scratch编程等级考试四级真题 判断题(共10题,每题2分,共20分) 11、运行程序后,变量"result"的值是6 答案:对 考点分析:考查积木综合使用,重点考查自定义积木的使用 图中自定义积木实现的功能是获取两个数中最大的那个数并存放在result变量中,左…

Find My水杯|苹果Find My技术与水杯结合,智能防丢,全球定位

2018年中国智能水杯行业的市场规模约为32亿元&#xff0c;而到2021年&#xff0c;这一市场规模将达到45亿元&#xff0c;增长率约为8.6%。随着智能科技的不断深入发展&#xff0c;智能水杯也越来越受到消费者的青睐&#xff0c;他们更加偏爱智能水杯带来的便捷。智能水杯可以监…

Webpack5中devServer配置contentBase报错的问题

结果报错&#xff0c;没有contentBase这个属性&#xff0c;已经被弃用了。 新的配置方式&#xff1a; const path require(path);module.exports {//...devServer: {static: {directory: path.join(__dirname, public),},compress: true,port: 9000,}, };

关于Spring和SpringBoot中对配置文件的读取

Spring读取xml文件 具体流程见网址Spring源码分析2 — spring XML配置文件的解析流程 - 知乎 (zhihu.com) 我这里只做一下总结和自己的理解&#xff1a; &#xff08;1&#xff09;通过getConfigLocations方法, 从web.xml文件中读取配置文件地址&#xff0c;如果web.xml中读取…

ES 8.x新特性一览(完整版)

一、看点 在 2022 年 2 月 11 日&#xff0c;Elasticsearch&#xff08;ES&#xff09;正式发布了 8.0 版本&#xff0c;而截止到 2023 年 10 月&#xff0c;历经一年半时间&#xff0c;ES官方已经连续发布了多个版本&#xff0c;最新版本为 8.10.4。这一系列的更新引入了众多引…

【多线程面试题二十】、 如何实现互斥锁(mutex)?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;如何实现互斥锁&#xf…

JavaSE20——IO流

IO流 1 Java IO流 I/O是Input/Output的缩写&#xff0c; I/O技术是非常实用的技术&#xff0c;用于处理设备之间的数据传输。如读/写文件&#xff0c;网络通讯等。Java程序中&#xff0c;对于数据的输入/输出操作以“流(stream)” 的方式进行 I(Input): 输入流指的是将数据以…

世界前沿技术发展报告2023《世界航空技术发展报告》(七)机载系统与武器技术

&#xff08;七&#xff09;机载系统与武器技术 1.机载系统技术1.1 美国推进商用5G技术在航空装备中的应用1.2 人工智能技术在航空中的应用日益增多1.3 美国空军研究实验室推出综合座舱感知技术1.4 美国空军为固定翼飞机驾驶员选定新一代头盔1.5 美国DARPA探索通过机载光能量中…

用LibreOffice在excel中画折线图

数据表格如下。假设想以x列为横坐标&#xff0c;y1和y2列分别为纵坐标画折线图。 选择插入-》图表&#xff1a; 选择折线图-》点和线&#xff0c;然后点击“下一步”&#xff1a; 选择&#xff1a;列中包含数据序列&#xff0c;然后点击完成&#xff08;因为图挡住了数据…

【多线程面试题十五】、synchronized可以修饰静态方法和静态代码块吗?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;synchronized可以修饰静…

生物信息学分析-blast序列比对及结果详细说明

1. 软件说明 Blast是一种基于序列比对的分析工具&#xff0c;可以用于寻找生物序列之间的同源性&#xff0c;它的全称是Basic Local Alignment Search Tool。 Blast有多种版本和用途&#xff0c;最常见的是基于Web的Blast和本地安装的Blast程序。Web版Blast可以直接在NCBI网站…

小白如何在一个月写一篇论文(中文核心,SCI)

小白如何半年发3篇sci的我教你如何快速“水”一篇sci论文_哔哩哔哩_bilibili 计算机视觉&#xff0c;cv领域 半年发3篇sci的我教你如何快速“水”一篇sci论文 计算机视觉(辅导 SCI EI 核心) 微信&#xff1a;whbwqq123或主页加up 小白如何快速写出一篇论文并成功发表&…

图像处理与计算机视觉--神经网络--手动计算

文章目录 1.简单感知器分类模型1.1.简单感知器分类模型介绍1.2.简单感知器分类模型实现 2线性神经元分类模型2.1.线性神经元分类模型介绍2.2.线性神经元分类模型实现 3.基于遍历学习的神经网络计算模型3.1.基于遍历学习的神经网络计算模型介绍3.2.基于遍历学习的神经网络计算模…

Windows原生蓝牙编程 第二章 选取设备输入配对码并配对【C++】

蓝牙系列文章目录 第一章 获取本地蓝牙并扫描周围蓝牙信息并输出 第二章 选取设备输入配对码并配对 文章目录 前言头文件一、选择想要配对的设备并设置配对码1.1 设置配对码1.2 选择设备并配对 二、全部代码三、测试结果总结 前言 接着第一章&#xff0c;我们已经把扫描到的蓝…

Composition API的引入

目录 全局API的移除和替代 插件的改进 TypeScript支持的增强 优势 劣势 总结 Vue.js 3.x版本引入了Composition API&#xff0c;这是一个全新的API风格&#xff0c;旨在提高代码的可读性和重用性。Composition API使我们可以根据逻辑相关性组织代码&#xff0c;而不是按照…

3.3每日一题(变量可分离方程)

1、判断类型选方法&#xff1a;等式中分别提一个x、y出来&#xff0c;形成了x与y相乘的等式&#xff1b;为变量可分离类型 2、不一定非得把y解出来&#xff0c;化成上述的等式即可&#xff08;为隐函数的方程解&#xff09; 注&#xff1a;等式不定积分后记得&#xff0b;一个…