从一个Bug谈前端响应拦截器的应用

news2024/12/26 11:40:26

一、问题场景

今天在开发商品管理系统时,遇到了一个有趣的问题:当添加重复的商品编号时,页面同时弹出了两条 "商品编号已存在" 错误提示:
在这里插入图片描述
这个问题暴露了前端错误处理机制的混乱,让我们从这个问题出发,深入了解响应拦截器的应用。

二、问题分析

原始代码

// 响应拦截器
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 || '商品编号已存在')
    }
}

错误提示重复的原因

  1. 后端返回数据:{code: 0, msg: “商品编号已存在”, data: null}
  2. 响应拦截器发现 code !== 1,显示错误消息并 reject
  3. 业务代码的 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 : productAddService
        const res = await service(submitData)
        
        // 只处理成功情况
        ElMessage.success(`${isEditMode.value ? '编辑' : '添加'}成功`)
        dialogVisible.value = false
        productList()
        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 {
    // 问题已经被前台处理了,你不用管
}

响应拦截器就像这个"前台接待",它在服务器响应返回到我们的业务代码之前对所有响应进行统一的处理

为什么需要响应拦截器?

  1. 没有响应拦截器时:
// 每个业务请求都需要重复处理这些情况
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) {
        // 又要重复上面的代码...
    }
}
  1. 使用响应拦截器后:
// 统一的响应处理
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) {
        // 错误已经在拦截器中处理过了
    }
}

四、请求流程图

请求发起 → 请求拦截器 → 服务器 → 响应拦截器 → 业务代码

五、最佳实践总结

  1. 响应拦截器职责
    • 统一处理响应数据格式
    • 统一处理错误提示
    • 处理特殊状态码(如401未登录)
    • 转换服务端数据结构(如果需要)
  2. 业务代码职责
    • 关注业务逻辑
    • 处理成功场景
    • 可以选择性地处理特定错误
    • 不重复错误提示
  3. 错误处理原则
    • 统一入口处理错误
    • 避免重复提示
    • 提供清晰的错误信息
    • 合理使用 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)
}

七、总结

响应拦截器就像一个尽职尽责的前台,帮你处理了所有烦琐的检查工作,让我们可以专注于核心业务。

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

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

相关文章

量子变分算法---损失函数

引子 关于损失函数,我们知道在强化学习中,会有一个函数,用来表示模型每一次行为的分数,通过最大化得分,建立一个正反馈机制,若模型为最优则加分最多,若决策不佳则加很少分或者扣分。而在神经网络…

车间的图纸在传输过程的安全怎么保障?

车间的图纸在传输过程的安全保障,要从很多方面出发分析,本文从以下几点为大家列出几个,看看有没有你想知道的呢~ 1、采用先进的图纸加密软件 采用先进的加密算法对图纸进行加密处理,确保图纸文件在存储、传输和使用过程中的安全性…

MQTT消息服务器mosquitto介绍及说明

Mosquitto是一个开源的消息代理软件,支持MQTT协议(消息队列遥测传输协议)。MQTT是一种轻量级的发布/订阅消息传输协议,专为低带宽、不可靠网络环境下的物联网设备通信而设计。以下是关于Mosquitto服务器的一些介绍和说明&#xff…

想在iPad上远程操作安卓手机的APP,怎样实现iPad远程控制安卓?

学生党或互联网行业的打工人,人手连三台电子设备也很常见,手机、平板还有笔记本电脑一大堆,如果出门要全带上,背包压力也变大。 有没有想过用远程控制功能,让iPad远程控制安卓手机?这样做,出门就…

VBA高级应用30例应用在Excel中的ListObject对象:向表中添加注释

《VBA高级应用30例》(版权10178985),是我推出的第十套教程,教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开,这套教程案例与理论结合,紧贴“实战”,并做“战术总结”,以…

Spring源码分析之Bean的实例化(createBeanInstance())

前言: 通过Spring源码分析之Bean的创建过程(createBean)-CSDN博客我们可以知道如果没有动态代理以及循环依赖的前提之下的话那么一个普通的单例Bean的创建后就是实例化,属性填充,初始化这三个步骤那么这篇文章的话我们就先说一下实例化也就是doCreateBean方法里面的createBeanI…

一次“okhttp访问间隔60秒,提示unexpected end of stream“的问题排查过程

一、现象 okhttp调用某个服务,如果第二次访问间隔上一次访问时间超过60s,返回错误:"unexpected end of stream"。 二、最终定位原因: 空闲连接如果超过60秒,服务端会主动关闭连接。此时客户端恰巧访问了这…

蓝桥杯准备训练(lesson5 ,c++)

单目操作符与第 2 章 C/C输⼊输出(上) 8. 单⽬操作符8.1 和--8.1.1 前置 和 后置8.1.2 前置-- 和 后置-- 8.2 和 - 第 2 章 C/C输⼊输出(上)1. getchar 和 putchar1.1 getchar()1.2 putchar() 2. scanf 和 printf2.1 printf2.1.1…

Linux系统挂载exfat格式U盘教程,触觉智能RK3562开发板演示

本文介绍Linux系统(Ubuntu/Debian通用)挂载exfat格式U盘的方法,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教…

LeetCode 0935.骑士拨号器:动态规划(DP)

【LetMeFly】935.骑士拨号器:动态规划(DP) 力扣题目链接:https://leetcode.cn/problems/knight-dialer/ 象棋骑士有一个独特的移动方式,它可以垂直移动两个方格,水平移动一个方格,或者水平移动两个方格,垂…

No.4 笔记 探索网络安全:揭开Web世界的隐秘防线

在这个数字时代,网络安全无处不在。了解Web安全的基本知识,不仅能保护我们自己,也能帮助我们在技术上更进一步。让我们一起深入探索Web安全的世界,掌握那些必备的安全知识! 1. 客户端与WEB应用安全 前端漏洞&#xff1…

PHP使用local-proxy的一种思路! | 架构师之路(19)

《架构师之路:架构设计中的100个知识点》 19.脚本语言使用长连接的一种思路 脚本类语言,例如PHP,不能像C/Java那样能搞服务常驻内存,不能搞长连接? 为什么脚本语言要搞长连接? 脚本类语言每次访问后端数据库…

【51单片机】程序实验1112.外部中断-定时器中断

主要参考学习资料:B站【普中官方】51单片机手把手教学视频 前置知识:C语言 单片机套装:普中STC51单片机开发板A4标准版套餐7 码字不易,求点赞收藏加关注(•ω•̥) 有问题欢迎评论区讨论~ 目录 程序实验11&12.外部中断-定时器…

驱动---1.DAC8552实现三角波输出

最近开始进行新项目的研发,考虑用DAC做一个前级输出,选择了DAC8552这个器件的一个模块,用了野火的指南者做主控,芯片是STM32F103VET6,主频是72MHz。 一、器件手册重要信息提取 1.DAC8552具有十六位的分辨率、双通道输…

虚幻引擎生存建造系统

先做一个建造预览模式,按下按键B后进入建造预览模式 首先创建自定义事件Preview Loop 用射线追踪摆放物体预览位置,并做一个预览材质 增强输入设置按键 每帧判断是否进入建造模式 预览模式制作成功! 接着做点击左键放置物品&#xff0…

IP研究 | 大数据洞察黄油小熊的爆火之路

一只来自泰国的小熊在国内红成了顶流。 今年,黄油小熊以烘焙店“打工人”的超萌形象迅速走红,2个月内火遍中国的社交媒体,泰国门店挤满飘洋过海求合影的中国粉丝,根据数说故事全网大数据洞察,黄油小熊2024年度的线上声…

深度学习案例:DenseNet + SE-Net

本文为为🔗365天深度学习训练营内部文章 原作者:K同学啊 一 回顾DenseNet算法 DenseNet(Densely Connected Convolutional Networks)是一种深度卷积神经网络架构,提出的核心思想是通过在每一层与前面所有层进行直接连接…

【java学习笔记】Set接口实现类-LinkedHashSet

一、LinkedHashSet的全面说明 (就是把数组不同位置的链表当成一个节点然后相连)

【大模型系列篇】LLaMA-Factory大模型微调实践 - 从零开始

前一次我们使用了NVIDIA TensorRT-LLM 大模型推理框架对智谱chatglm3-6b模型格式进行了转换和量化压缩,并成功部署了推理服务,有兴趣的同学可以翻阅《NVIDIA TensorRT-LLM 大模型推理框架实践》,今天我们来实践如何通过LLaMA-Factory对大模型…

【C++】LeetCode:LCR 078. 合并 K 个升序链表

题干: 给定一个链表数组,每个链表都已经按升序排列。 请将所有链表合并到一个升序链表中,返回合并后的链表。 解法:优先队列 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *ne…