Vue3 + Axios + Ant Design Vue 请求封装详解教程(含 Token 鉴权、加密、下载)


一、完整源码(请先阅读)

import { message, Modal } from 'ant-design-vue';
import axios from 'axios';
import { localRead } from '/@/utils/local-util';
import { useUserStore } from '/@/store/modules/system/user';
import { decryptData, encryptData } from './encrypt';
import { DATA_TYPE_ENUM } from '../constants/common-const';
import _ from 'lodash';
import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';// token的消息头
const TOKEN_HEADER = 'Authorization';// 创建axios对象
const smartAxios = axios.create({baseURL: import.meta.env.VITE_APP_API_URL,
});// 退出系统
function logout() {useUserStore().logout();location.href = '/';
}// ================================= 请求拦截器 =================================smartAxios.interceptors.request.use((config) => {// 在发送请求之前消息头加入token tokenconst token = localRead(LocalStorageKeyConst.USER_TOKEN);if (token) {config.headers[TOKEN_HEADER] = 'Bearer ' + token;} else {delete config.headers[TOKEN_HEADER];}return config;},(error) => {// 对请求错误做些什么return Promise.reject(error);}
);// ================================= 响应拦截器 =================================// 添加响应拦截器
smartAxios.interceptors.response.use((response) => {// 根据content-type ,判断是否为 json 数据let contentType = response.headers['content-type'] ? response.headers['content-type'] : response.headers['Content-Type'];if (contentType.indexOf('application/json') === -1) {return Promise.resolve(response);}// 如果是json数据if (response.data && response.data instanceof Blob) {return Promise.reject(response.data);}// 如果是加密数据if (response.data.dataType === DATA_TYPE_ENUM.ENCRYPT.value) {response.data.encryptData = response.data.data;let decryptStr = decryptData(response.data.data);if (decryptStr) {response.data.data = JSON.parse(decryptStr);}}const res = response.data;if (res.code && res.code !== 1) {// `token` 过期或者账号已在别处登录if (res.code === 30007 || res.code === 30008) {message.destroy();message.error('您没有登录,请重新登录');setTimeout(logout, 300);return Promise.reject(response);}// 等保安全的登录提醒if (res.code === 30010 || res.code === 30011) {Modal.error({title: '重要提醒',content: res.msg,});return Promise.reject(response);}// 长时间未操作系统,需要重新登录if (res.code === 30012) {Modal.error({title: '重要提醒',content: res.msg,onOk: logout,});setTimeout(logout, 3000);return Promise.reject(response);}message.destroy();message.error(res.msg);return Promise.reject(response);} else {return Promise.resolve(res);}},(error) => {// 对响应错误做点什么if (error.message.indexOf('timeout') !== -1) {message.destroy();message.error('网络超时');} else if (error.message === 'Network Error') {message.destroy();message.error('网络连接错误');} else if (error.message.indexOf('Request') !== -1) {message.destroy();message.error('网络发生错误');}return Promise.reject(error);}
);// ================================= 对外提供请求方法:通用请求,get, post, 下载download等 =================================/*** get请求*/
export const getRequest = (url, params) => {return request({ url, method: 'get', params });
};/*** 通用请求封装* @param config*/
export const request = (config) => {return smartAxios.request(config);
};/*** post请求*/
export const postRequest = (url, data) => {return request({data,url,method: 'post',});
};// ================================= 加密 =================================/*** 加密请求参数的post请求*/
export const postEncryptRequest = (url, data) => {return request({data: { encryptData: encryptData(data) },url,method: 'post',});
};// ================================= 下载 =================================export const postDownload = function (url, data) {request({method: 'post',url,data,responseType: 'blob',}).then((data) => {handleDownloadData(data);}).catch((error) => {handleDownloadError(error);});
};/*** 文件下载*/
export const getDownload = function (url, params) {request({method: 'get',url,params,responseType: 'blob',}).then((data) => {handleDownloadData(data);}).catch((error) => {handleDownloadError(error);});
};function handleDownloadError(error) {if (error instanceof Blob) {const fileReader = new FileReader();fileReader.readAsText(error);fileReader.onload = () => {const msg = fileReader.result;const jsonMsg = JSON.parse(msg);message.destroy();message.error(jsonMsg.msg);};} else {message.destroy();message.error('网络发生错误', error);}
}function handleDownloadData(response) {if (!response) {return;}// 获取返回类型let contentType = _.isUndefined(response.headers['content-type']) ? response.headers['Content-Type'] : response.headers['content-type'];// 构建下载数据let url = window.URL.createObjectURL(new Blob([response.data], { type: contentType }));let link = document.createElement('a');link.style.display = 'none';link.href = url;// 从消息头获取文件名let str = _.isUndefined(response.headers['content-disposition'])? response.headers['Content-Disposition'].split(';')[1]: response.headers['content-disposition'].split(';')[1];let filename = _.isUndefined(str.split('fileName=')[1]) ? str.split('filename=')[1] : str.split('fileName=')[1];link.setAttribute('download', decodeURIComponent(filename));// 触发点击下载document.body.appendChild(link);link.click();// 下载完释放document.body.removeChild(link);window.URL.revokeObjectURL(url);
}

二、源码详解

1. 依赖与常量

import { message, Modal } from 'ant-design-vue'; // 用于弹出消息和模态框提示
import axios from 'axios'; // axios 请求库
import { localRead } from '/@/utils/local-util'; // 本地存储读取工具
import { useUserStore } from '/@/store/modules/system/user'; // 用户状态管理
import { decryptData, encryptData } from './encrypt'; // 加解密函数
import { DATA_TYPE_ENUM } from '../constants/common-const'; // 数据类型常量
import _ from 'lodash'; // 工具库
import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js'; // 本地存储key常量const TOKEN_HEADER = 'Authorization'; // 请求头携带token的键名

2. 创建 Axios 实例

const smartAxios = axios.create({baseURL: import.meta.env.VITE_APP_API_URL, // 基础请求地址从环境变量读取
});

3. 退出登录

function logout() {useUserStore().logout(); // 调用用户状态登出方法location.href = '/'; // 跳转到登录页或首页
}

4. 请求拦截器

smartAxios.interceptors.request.use((config) => {const token = localRead(LocalStorageKeyConst.USER_TOKEN); // 从本地获取tokenif (token) {config.headers[TOKEN_HEADER] = 'Bearer ' + token; // 自动添加到请求头} else {delete config.headers[TOKEN_HEADER]; // 无token则删除}return config;},(error) => {return Promise.reject(error); // 请求错误直接抛出}
);

作用:每个请求发送前自动带上 token,方便后端身份校验。

5. 响应拦截器

smartAxios.interceptors.response.use((response) => {// 判断响应内容是否是 JSONlet contentType = response.headers['content-type'] || response.headers['Content-Type'];if (contentType.indexOf('application/json') === -1) {return Promise.resolve(response); // 非 JSON,直接返回原始响应}// 响应是 Blob 类型异常处理if (response.data && response.data instanceof Blob) {return Promise.reject(response.data);}// 解密处理if (response.data.dataType === DATA_TYPE_ENUM.ENCRYPT.value) {response.data.encryptData = response.data.data;let decryptStr = decryptData(response.data.data);if (decryptStr) {response.data.data = JSON.parse(decryptStr);}}const res = response.data;// 业务异常处理,code !== 1 表示失败if (res.code && res.code !== 1) {// 令牌失效或账号异地登录if (res.code === 30007 || res.code === 30008) {message.destroy();message.error('您没有登录,请重新登录');setTimeout(logout, 300);return Promise.reject(response);}// 安全登录提醒if (res.code === 30010 || res.code === 30011) {Modal.error({ title: '重要提醒', content: res.msg });return Promise.reject(response);}// 长时间未操作需重新登录if (res.code === 30012) {Modal.error({ title: '重要提醒', content: res.msg, onOk: logout });setTimeout(logout, 3000);return Promise.reject(response);}message.destroy();message.error(res.msg);return Promise.reject(response);} else {return Promise.resolve(res); // 成功返回业务数据}},(error) => {// 网络错误统一提示if (error.message.includes('timeout')) {message.destroy();message.error('网络超时');} else if (error.message === 'Network Error') {message.destroy();message.error('网络连接错误');} else if (error.message.includes('Request')) {message.destroy();message.error('网络发生错误');}return Promise.reject(error);}
);

作用:统一处理服务器响应,自动解密、鉴权失效跳转、提示错误信息。

6. 通用请求封装(GET,POST)

export const getRequest = (url, params) => {return request({ url, method: 'get', params });
};export const postRequest = (url, data) => {return request({ url, method: 'post', data });
};export const request = (config) => {return smartAxios.request(config);
};

统一通过 request 发起请求,方便未来统一处理或扩展。

7. 加密请求

export const postEncryptRequest = (url, data) => {return request({url,method: 'post',data: { encryptData: encryptData(data) }, // 发送加密后的数据});
};

对敏感参数统一加密,保证传输安全。

8. 文件下载封装

export const postDownload = (url, data) => {request({url,method: 'post',data,responseType: 'blob',}).then(handleDownloadData).catch(handleDownloadError);
};export const getDownload = (url, params) => {request({url,method: 'get',params,responseType: 'blob',}).then(handleDownloadData).catch(handleDownloadError);
};

支持 GET/POST 下载,返回二进制流。

9. 下载结果与错误处理

function handleDownloadError(error) {if (error instanceof Blob) {const fileReader = new FileReader();fileReader.readAsText(error);fileReader.onload = () => {const msg = fileReader.result;const jsonMsg = JSON.parse(msg);message.destroy();message.error(jsonMsg.msg);};} else {message.destroy();message.error('网络发生错误', error);}
}function handleDownloadData(response) {if (!response) return;let contentType = _.isUndefined(response.headers['content-type']) ? response.headers['Content-Type'] : response.headers['content-type'];let url = window.URL.createObjectURL(new Blob([response.data], { type: contentType }));let link = document.createElement('a');link.style.display = 'none';link.href = url;let str = _.isUndefined(response.headers['content-disposition'])? response.headers['Content-Disposition'].split(';')[1]: response.headers['content-disposition'].split(';')[1];let filename = _.isUndefined(str.split('fileName=')[1]) ? str.split('filename=')[1] : str.split('fileName=')[1];link.setAttribute('download', decodeURIComponent(filename));document.body.appendChild(link);link.click();document.body.removeChild(link);window.URL.revokeObjectURL(url);
}

处理下载文件流,自动从响应头解析文件名并触发浏览器下载。错误时尝试读取 Blob 里的错误信息并提示。


三、总结

  • 封装 axios 实例方便集中管理基础地址、拦截器、错误处理。
  • 请求拦截器自动附带 token,保证鉴权。
  • 响应拦截器统一处理异常、token失效自动登出、数据加密解密。
  • 请求方法封装标准的 getRequestpostRequest 和加密 postEncryptRequest
  • 文件下载封装支持流下载及错误提示,提升用户体验。
  • 使用此封装,业务代码只需专注调用接口,提升开发效率和代码规范。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL注入安全研究

​据OWASP 2023报告显示,SQL注入连续15年位居Web安全威胁榜首,在应用漏洞中占比34.1%​​ ​NIST统计显示:2022-2023年高危SQL注入漏洞同比增长27%,企业平均修复成本达$320,000​ 一、漏洞本质与技术原理解析 1. SQL注入核心机理…

Ubuntu最新版本(Ubuntu22.04LTS)安装nfs服务器

NFS(Network File System)是一种允许不同计算机之间共享文件的网络文件系统。 在Ubuntu 22.04 LTS中,您可以使用以下步骤安装并配置NFS服务器。 一、安装NFS服务器 在Ubuntu 22.04 LTS中,您可以使用以下命令安装NFS服务器&…

学习笔记丨数字信号处理(DSP)的应用——图像处理篇

📌 DSP在图像处理中的应用:核心技术解析 数字信号处理(DSP)是图像处理的核心技术之一,广泛应用于增强、压缩、分析和识别等领域。以下是DSP在图像处理中的关键应用及技术细节: 目录 🔍 图像增…

Kafka Broker处理消费者请求源码深度解析:从请求接收到数据返回

在Kafka生态体系中,消费者从Broker拉取消息是实现数据消费的关键环节。Broker如何高效处理消费者请求,精准定位并返回对应分区数据,直接决定了整个消息系统的性能与稳定性。接下来,我们将聚焦Kafka Broker端,深入剖析其…

Objective-C与Swift混合编程

Objective-C与Swift混合编程的基本概念 Objective-C与Swift混合编程是指在同一项目中同时使用两种语言进行开发。这种混合编程方式在迁移旧项目或利用Swift新特性时非常有用。两种语言可以相互调用,但需要遵循特定的规则和桥接机制。 设置混合编程环境 在Xcode项…

IDE深度集成+实时反馈:企业级软件测试方案Parasoft如何重塑汽车巨头的测试流程

在汽车行业数字化转型的浪潮中,全球第四大汽车集团Stellantis曾面临严峻的测试效率挑战:开发与测试流程脱节、团队对“测试左移”策略的抵触、TDD(测试驱动开发)推进困难……这些痛点直接导致质量保障滞后,拖慢产品交付…

【Linux】Linux异步I/O -libaio

一、libaio 原理概述 1.1 libaio 介绍 libaio(Linux Asynchronous I/O)是 Linux 内核提供的异步 I/O 库,其核心原理是: 异步提交:应用程序通过 io_submit 提交 I/O 请求后立即返回,不阻塞进程事件通知&a…

git submodule 和git repo介绍

这是一个 Git 子模块(submodule)管理问题。当一个 Git 仓库(主仓库)中包含多个其他 Git 仓库(子仓库)时,最推荐的做法是使用 Git 子模块 或 Git 子树(subtree) 进行管理。…

识别网络延迟与带宽瓶颈

识别网络延迟与带宽瓶颈 在分布式系统与微服务架构日益普及的背景下,网络性能成为影响系统响应速度与服务可用性的重要因素。网络延迟和带宽瓶颈是两类最常见的网络性能障碍。准确识别这两类瓶颈,有助于系统架构师从根源优化服务质量,保障系统在高并发、高流量场景下依然具…

Linux内网穿透(frp)

目标:让我的VMware虚拟机某个服务拥有自己的外网访问地址 FRP 服务端(公网服务器)配置 1. 下载 FRP 登录公网服务器,执行以下命令下载并解压 FRP: # 下载对应版本(以Linux 64位为例) wget h…

《Vuejs设计与实现》第 9 章(简单 diff 算法)

目录 9.1 减少 DOM 操作的性能开销 9.2 DOM 复用与 key 的作用 9.3 找到需要移动的元素 9.4 如何移动元素 9.5 添加新元素 9.6 移除不存在的元素 9.7 总结 当新旧 vnode 的子节点都是一组节点时,为了以最小的性能开销完成更新操作,需要比较两组子…

队列,环形缓冲区实现与应用:适用于GD32串口编程或嵌入式底层驱动开发

环形缓冲区实现与应用:从基础到实践 在嵌入式系统和实时数据处理场景中,环形缓冲区(Circular Buffer)是一种非常常用的的数据结构,它能有效地管理数据的读写操作,尤其适用于数据流的临时存储与转发。 今天…

WHAT - Expo Go 和 development build

文章目录 1. 什么是 Expo Go?简介作用限制2. 什么是 Development Build(开发构建)?简介功能创建方式3. 它们有什么区别?总结建议怎么从 Expo Go 迁移到开发构建一、什么是“迁移”?二、迁移步骤总览三、详细操作步骤1. 安装 expo-dev-client2. 配置 eas.json(Expo 应用服…

Keepalived 配置 VIP 的核心步骤

Keepalived 配置 VIP 的核心步骤主要涉及安装软件、主备节点配置及服务管理。以下是具体操作指南: 一、安装 Keepalived ‌Ubuntu/Debian 系统‌ sudo apt update sudo apt install keepalived ‌CentOS/RHEL 系统‌ sudo yum install keepalived 注:需确保已配置 EPE…

HarmonyOS 5折叠屏自适应广告位布局方案详解

以下是HarmonyOS 5折叠屏广告位自适应布局的完整技术方案,综合响应式设计、动态交互与元服务融合策略: 一、核心布局技术‌ ‌断点响应式设计‌ 基于屏幕宽度动态调整布局结构,避免简单拉伸: // 定义断点阈值(单位&am…

【数据分析十:Classification prediction】分类预测

一、分类的定义 已知:一组数据(训练集) (X, Y) 例如: x:数据特征/属性(如收入) y:类别标记(是否有借款) 任务: 学习一个模型,利用每一条记录…

设计模式-接口隔离原则(Interface Segregation Principle, ISP)

接口隔离原则(Interface Segregation Principle, ISP) 核心思想:客户端不应被迫依赖它们不使用的接口方法。 目标:通过拆分臃肿的接口为更小、更具体的接口,减少不必要的依赖,提高系统的灵活性和可维护性。…

超融合:系统工程还是软件工程? 从H3C UIS9.0看超融合的技术本质

在数字化转型的浪潮中,超融合基础架构(Hyper-Converged Infrastructure, HCI)凭借其简化部署、弹性扩展和高效运维的优势,成为企业IT基础设施升级的重要选择。 然而,关于超融合究竟属于系统工程还是软件工程的讨论一直…

青少年编程与数学 01-012 通用应用软件简介 01 Microsoft Office办公软件

青少年编程与数学 01-012 通用应用软件简介 01 Microsoft Office办公软件 **一、Microsoft Office办公软件概述****二、发展过程**(一)早期起源(二)技术演进 **三、主要用途或功能**(一)文字处理&#xff0…

vivado IP综合选项

在 Vivado 中,生成 IP 文件时的 Synthesis Options 提供了两种主要的综合模式:Global 和 Out of Context per IP。这两种模式的主要区别如下: 1. Global Synthesis(全局综合) 定义:在这种模式下&#xff…