从一个Bug谈前端响应拦截器的应用
一、问题场景
今天在开发商品管理系统时,遇到了一个有趣的问题:当添加重复的商品编号时,页面同时弹出了两条 "商品编号已存在" 错误提示:

这个问题暴露了前端错误处理机制的混乱,让我们从这个问题出发,深入了解响应拦截器的应用。
二、问题分析
原始代码
// 响应拦截器
instance.interceptors.response.use(result => {if (result.data.code === 1) {return result.data;}ElMessage.error(result.data.msg || '服务异常');return Promise.reject(result.data);},err => {if(err.response.status === 401){ElMessage.error('请先登录!')router.push('/login')}else{ElMessage.error('服务异常');}return Promise.reject(err);}
)// 业务代码
const saveProduct = async () => {try {// ... 表单验证等代码 ...const res = await productAddService(submitData)if (res.code === 1) {ElMessage.success('添加成功')// ... 其他成功处理 ...} else {ElMessage.error(res.msg || '添加失败')}} catch (error) {ElMessage.error(error.msg || '商品编号已存在')}
}
错误提示重复的原因
- 后端返回数据:{code: 0, msg: “商品编号已存在”, data: null}
- 响应拦截器发现 code !== 1,显示错误消息并 reject
- 业务代码的 catch 块又显示了一次错误消息
三、解决方案
统一的响应拦截器处理
// src/utils/request.js
instance.interceptors.response.use(result => {// 业务成功if (result.data.code === 1) {return result.data;}// 业务失败,统一处理错误提示ElMessage.error(result.data.msg || '操作失败');return Promise.reject(result.data);},err => {// 处理HTTP错误if(err.response.status === 401){ElMessage.error('请先登录!')router.push('/login')}else{ElMessage.error('服务异常');}return Promise.reject(err);}
)
简化业务代码
const saveProduct = async () => {try {await productForm.value.validate()const submitData = {...productModel.value,price: Number(productModel.value.price)}const service = isEditMode.value ? productEditService : productAddServiceconst res = await service(submitData)// 只处理成功情况ElMessage.success(`${isEditMode.value ? '编辑' : '添加'}成功`)dialogVisible.value = falseproductList()resetProductModel()} catch (error) {// 错误已在拦截器中处理,这里不需要重复处理return}
}
四、响应拦截器
什么是响应拦截器
这里我进行一个类比,想象你在一个公司工作:
- 你就是业务部门(前端业务代码)
- 前台小姐姐就是响应拦截器
- 各种快递就是服务器返回的数据
场景一:正常快递
快递 → 前台验收 → 签字 → 转交给你
对应代码:
// 响应拦截器(前台小姐姐)
axios.interceptors.response.use(response => {if (response.data.code === 1) { // 检查快递是否完好return response.data // 转交给业务部门}}
)// 业务代码(你)
const res = await getProductList() // 直接收到处理好的快递
console.log(res.data) // 使用快递内容
场景二:问题快递
坏快递 → 前台拒收 → 你完全不用处理
对应代码:
// 响应拦截器(前台小姐姐)
axios.interceptors.response.use(response => {if (response.data.code !== 1) { // 发现快递有问题ElMessage.error('快递有问题') // 通知你快递有问题return Promise.reject() // 直接拒收,不给你添麻烦}}
)// 业务代码(你)
try {const res = await getProductList()// 只需要处理正常情况
} catch {// 问题已经被前台处理了,你不用管
}
响应拦截器就像这个"前台接待",它在服务器响应返回到我们的业务代码之前,对所有响应进行统一的处理。
为什么需要响应拦截器?
- 没有响应拦截器时:
// 每个业务请求都需要重复处理这些情况
const getProductList = async () => {try {const res = await axios.get('/api/products')if (res.data.code === 1) { // 成功return res.data.data} else if (res.data.code === 401) { // 未登录ElMessage.error('请先登录')router.push('/login')} else { // 其他错误ElMessage.error(res.data.msg)}} catch (error) {ElMessage.error('网络错误')}
}const addProduct = async () => {try {const res = await axios.post('/api/product/add')// 又要重复上面的代码...} catch (error) {// 又要重复上面的代码...}
}
- 使用响应拦截器后:
// 统一的响应处理
axios.interceptors.response.use(response => {// 统一处理成功和失败if (response.data.code === 1) {return response.data}// 统一处理未登录if (response.data.code === 401) {ElMessage.error('请先登录')router.push('/login')return Promise.reject(response.data)}// 统一处理错误提示ElMessage.error(response.data.msg)return Promise.reject(response.data)},error => {// 统一处理网络错误ElMessage.error('网络错误')return Promise.reject(error)}
)// 业务代码变得简洁
const getProductList = async () => {try {const res = await axios.get('/api/products')return res.data // 只需处理成功的情况} catch (error) {// 错误已经在拦截器中处理过了}
}
四、请求流程图
请求发起 → 请求拦截器 → 服务器 → 响应拦截器 → 业务代码
五、最佳实践总结
- 响应拦截器职责
- 统一处理响应数据格式
- 统一处理错误提示
- 处理特殊状态码(如401未登录)
- 转换服务端数据结构(如果需要)
- 业务代码职责
- 关注业务逻辑
- 处理成功场景
- 可以选择性地处理特定错误
- 不重复错误提示
- 错误处理原则
- 统一入口处理错误
- 避免重复提示
- 提供清晰的错误信息
- 合理使用 Promise.reject()
六、扩展应用
处理登录失效
if (err.response.status === 401) {ElMessage.error('登录已过期,请重新登录')router.push('/login')
}
处理网络错误
if (!error.response) {ElMessage.error('网络连接失败,请检查网络设置')
}
处理特定业务错误
if (result.data.code === 100001) {// 处理特定业务错误码handleSpecialError(result.data)return Promise.reject(result.data)
}
七、总结
响应拦截器就像一个尽职尽责的前台,帮你处理了所有烦琐的检查工作,让我们可以专注于核心业务。
相关文章:
从一个Bug谈前端响应拦截器的应用
一、问题场景 今天在开发商品管理系统时,遇到了一个有趣的问题:当添加重复的商品编号时,页面同时弹出了两条 "商品编号已存在" 错误提示: 这个问题暴露了前端错误处理机制的混乱,让我们从这个问题出发&…...
JS进阶DAY4|节点操作
嘿👋 今天我们要一起深入探索JavaScript中的DOM操作,这是前端开发中不可或缺的技能。🌟 准备好了吗?让我们一起跳进DOM的海洋,看看怎么用代码操控网页的结构吧! 目录 1. 增加节点 1.1 使用 appendChild 方…...
【Web】2023安洵杯第六届网络安全挑战赛 WP
目录 Whats my name easy_unserialize signal Swagger docs 赛题链接:GitHub - D0g3-Lab/i-SOON_CTF_2023: 2023 第六届安洵杯 题目环境/源码 Whats my name 第一段正则用于匹配以 include 结尾的字符串,并且在 include 之前,可以有任…...
go 语言中协程和GMP模型
为什么需要协程? 协程用来更加精细地利用线程,支撑超高的并发的。协程,从 runtime 的角度看,协程就是一个被调度的 g 结构体。 G 就是协程,M 是线程,P 是为了优化多线程并发时,会抢夺协程队列的…...
coco数据集转换SAM2格式
coco是一个大json汇总了所有train的标签 SAM2训练一张图对应一个json标签 import json import os from pycocotools import mask as mask_utils import numpy as np import cv2def poly2mask(points, width, height):points_array np.array(points, dtypenp.int32).reshape(-…...
【CMD、PowerShell和Bash设置代理】
【CMD、PowerShell和Bash设置代理】 1. CMD(命令提示符)临时设置代理(只对当前会话有效):查看当前代理设置:清除临时代理设置:永久设置代理(对所有新的 CMD 会话有效)&am…...
22智能 代码作业集合
3-2 #include <stdio.h>int main() {int a 21;int b 10;int c ;c a b;printf("Line 1 - c 的值是 %d\n", c );c a - b;printf("Line 2 - c 的值是 %d\n", c );c a * b;printf("Line 3 - c 的值是 %d\n", c );c a / b;printf("…...
实现一个简单的后台架子(侧边栏菜单渲染,折叠,黑白主题,组件主题色,全屏,路由快捷栏)
目录 侧边栏菜单渲染 侧边栏折叠 黑白主题 全屏切换 切换组件主题色 tab快捷栏 代码 侧边栏菜单渲染 结合ElementPlus组件库进行实现 新建的Vue3项目,引入了格式化样式normalize.css和ElementPlus,并进行了全局引入 并进行了全局引入 设置高度为100% 粘贴ElementPlus的…...
vue3-canvas实现在图片上框选标记(放大,缩小,移动,删除)
双图版本(模板对比) 业务描述:模板与图片对比,只操作模板框选的位置进行色差对比,传框选坐标位置给后端,返回对比结果显示 draw.js文件: 新增了 createUuid,和求取两个数组差集的方…...
unity3d—demo(2d人物左右移动发射子弹)
目录 人物代码示例: 子弹代码示例: 总结上面代码: 注意点: 人物代码示例: using System.Collections; using System.Collections.Generic; using UnityEngine;public class PlayerTiao : MonoBehaviour {public f…...
【ETCD】【源码阅读】 深入解析 raftNode.start`函数:Raft 核心启动逻辑剖析
raftNode.start方法 是 etcd 中 Raft 模块的核心启动点,其职责是管理 Raft 状态机的状态变迁、日志处理及集群通信等逻辑。通过对源码的逐行分析,我们将全面揭示其运行机制,探讨其设计背后的分布式系统理念。 函数核心结构 raftNode.start 方…...
Robust Depth Enhancement via Polarization Prompt Fusion Tuning
paper:论文地址 code:github项目地址 今天给大家分享一篇2024CVPR上的文章,文章是用偏振做提示学习,做深度估计的。模型架构图如下 这篇博客不是讲这篇论文的内容,感兴趣的自己去看paper,主要是分享环境&…...
NEFTune,SFT训练阶段给Embedding加噪音
仿照CV里,数据增强的思路(给图像做旋转、反转、改变亮度等);NLP里,SFT训练数据较少时,也可往embedding上加噪音,来增加训练数据的丰富程度。进而提升最终训练效果。 前提假设:Embed…...
uniapp -- 实现页面滚动触底加载数据
效果 首选,是在pages.json配置开启下拉刷新 {"path": "pages/my/document/officialDocument","style": {"navigationStyle":</...
L22.【LeetCode笔记】相交链表(新版)
目录 1.题目 代码模板 2.分析 编辑 算法误区 正确方法1 但不能通过所有的测试用例 修改后 提交结果 正确方法2 节省代码的技巧 1.题目 https://leetcode.cn/problems/3u1WK4/description/ 给定两个单链表的头节点 headA 和 headB ,请找出并返回两个单…...
智能时代网络空间认知安全新观察
文章目录 前言一、历史上的四次认知革命二、人工智能革命掀起认知安全新浪潮三、人工智能技术塑造认知安全新范式四、人工智能治理应对认知安全新思考 前言 12月5日,在2024第三届北外滩网络安全论坛上以“智能时代网络空间认知安全新观察”为主题作主旨演讲&#x…...
游戏如何应对模拟器作弊
模拟器是指能在PC端模拟出安卓手机系统的软件,市面上比较常见的安卓模拟器有:雷电模拟器、MuMu模拟器、夜神模拟器等。 市面上常见的模拟器 模拟器既可以节省手机内存空间,避免长时间玩游戏手机发烫发热的尴尬,也可以用键盘鼠标对…...
c++ 判断一个 IP 地址(可能是 IPv6 或 IPv4)是否属于特定范围
在 C 中,判断一个 IP 地址(可能是 IPv6 或 IPv4)是否属于特定范围时,需要考虑两种不同的地址格式和它们的范围比较。IPv6 和 IPv4 地址结构完全不同,因此需要分别处理这两种地址类型。 实现思路: 识别 IP…...
计算机视觉——相机标定(Camera Calibration)
文章目录 1. 简介2. 原理3. 相机模型3.1 四大坐标系3.2 坐标系间的转换关系3.2.1 世界坐标系到相机坐标系3.2.2 相机坐标系到图像坐标系3.2.3 像素坐标系转换为图像坐标系3.2.4 世界坐标转换为像素坐标 3.3 畸变3.3.1 畸变类型3.3.1.1 径向畸变(Radial Distortion&a…...
【qt环境配置】windows下的qt与vs工具集安装\版本对应关系
vs工具集安装通过vs的在线安装器勾选工具集即可 工具包下载路径:https://www.microsoft.com/zh-cn/download/details.aspx?id40784 配置工具集在qt中可以自动扫描到 《正确在 Windows 上配置 MSVC(2019) 作为 Qt 编译器》https://b3logfile.com/pdf/article/15922…...
【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
