前端限流实战:从 429 状态码处理到消除“双重报错”

前端限流实战:从 429 状态码处理到消除"双重报错"

在开发限流功能时,我们经常会遇到两个阶段的体验问题:第一阶段是前端直接显示生硬的 HTTP 状态码错误,第二阶段是修复后出现"双重错误提示"。本文将带你从前端拦截器的角度,一步步优化限流交互体验。

一、 第一阶段:拒绝生硬的 HTTP 错误

当后端限流切面生效时,会抛出 429 状态码。如果前端没有针对性处理,用户看到的将是 Request failed with status code 429 这种毫无意义的报错。
现象:

后端日志显示触发了限流:

text 复制代码
接口触发限流:操作太快啦,请稍微休息一下!

但前端仅显示 Axios 的默认网络错误。
原因分析:

前端的 Axios 响应拦截器中没有对 429 状态码进行专门捕获,导致错误直接被抛到底层的默认处理逻辑。
解决方案:

common.js 的响应拦截器 error 处理函数中,增加对 429 状态码的判断,提取后端返回的业务错误信息。

javascript 复制代码
// ==================== B. 异常响应 (401, 429, 500 等) ====================
async function (error) {
  const originalRequest = error.config;
  const response = error.response;
  // -----------------------------------------------------
  // 关键修复:拦截 429 状态码并提取后端错误提示
  // -----------------------------------------------------
  if (response && response.status === 429) {
    // 从后端返回的 Result 对象中提取 errorMsg
    // 后端结构:{ "success": false, "errorMsg": "...", "data": null }
    const msg = (response.data && response.data.errorMsg) 
                ? response.data.errorMsg 
                : "请求过于频繁,请稍后再试";
    // 返回 reject 阻止业务逻辑继续
    return Promise.reject(msg);
  }
  
  // ... (后续 401 等逻辑保持不变) ...
}

二、 第二阶段:消除"双重报错"的尴尬

应用了上面的代码后,虽然能看到友好提示了,但往往会出现一个新的问题:页面上同时弹出了两个一模一样的错误提示框。
现象:

用户触发限流后,屏幕顶部弹出了两个红色的提示框,内容都是"操作太快啦"。
根源分析:

这是一个典型的"职责不清"问题,导致了冗余操作:

  1. 全局拦截器 :在捕获到 429 后,为了"保险"起见,手动调用了 ELEMENT.Message.error(msg) 进行弹窗。
  2. 业务代码 :在请求的 .catch() 中也写了弹窗逻辑(例如 this.$message.error(err))。
    当拦截器弹窗并 reject 后,业务代码再次捕获到错误并弹窗,导致了双重提示。

三、 架构级修复:职责单一原则

在成熟的前端架构中,全局拦截器应遵循**"只清洗数据,不干涉表现"**的原则。

  • 拦截器职责:将 HTTP 协议层的错误(如 429、500)转化为业务层可读的字符串,并向下传递。
  • 业务层职责 :接收错误信息,并根据当前页面风格决定如何展示(弹窗、Alert、写入日志等)。
    最佳实践代码:
    我们需要删除拦截器中手动弹窗的代码,只负责提取错误信息。
javascript 复制代码
// ==================== B. 异常响应 (401, 429, 500 等) ====================
async function (error) {
  const originalRequest = error.config;
  const response = error.response;
  // -----------------------------------------------------
  // 429 限流修复:仅负责提取后端错误消息 (不再手动弹窗)
  // -----------------------------------------------------
  if (response && response.status === 429) {
    // 提取后端 Result 对象中的 errorMsg
    const msg = (response.data && response.data.errorMsg) 
                ? response.data.errorMsg 
                : "请求过于频繁,请稍后再试";
    // 【架构级修复点】:此处不再调用 ELEMENT.Message.error(msg)
    // 理由:业务代码中的 .catch() 已经具备弹窗能力。
    // 此处重复调用会导致弹出两条信息。
    
    return Promise.reject(msg); 
  }
  // -----------------------------------------------------
  // 401 主动刷新逻辑 (保持原有逻辑)
  // -----------------------------------------------------
  if (
    response &&
    response.status === 401 &&
    !originalRequest.url.includes("/user/refresh") &&
    !originalRequest._retry
  ) {
    // ... (保持你原有的 Token 刷新逻辑不变) ...
  }
  // -----------------------------------------------------
  // 兜底处理:提取其他非 429/401 类型的错误
  // -----------------------------------------------------
  const finalMsg = (response && response.data && response.data.errorMsg) 
                   ? response.data.errorMsg 
                   : (error.message || "系统繁忙");
                   
  return Promise.reject(finalMsg);
}

四、 总结

通过这次重构,我们不仅解决了限流提示的问题,更重要的是理清了前端异常处理的边界:

  1. 数据清洗:拦截器把晦涩的 HTTP 429 错误,变成了友好的中文提示。
  2. 交互统一 :限流报错现在统一通过业务页面的 .catch() 弹出,其样式和位置与"密码错误"、"格式错误"等其他业务报错保持完全一致。
    这种设计避免了全局拦截器对 UI 的过度干预,提升了代码的可维护性和扩展性。修改完成后,请务必清除浏览器缓存,再次测试,你将只会看到一个优雅、准确的限流提示。
相关推荐
XGeFei13 小时前
【表单处理】——如何防止CSRF(跨站请求伪造)攻击的?
前端·网络·csrf
还不秃顶的计科生13 小时前
多模态模型下载
java·linux·前端
GISer_Jing13 小时前
笑不活了!蒸馏Skill竟能复刻前任、挽留同事?三大热门项目+完整地址汇总
前端·人工智能
Bigger14 小时前
🚀 mini-cc:打造你的专属轻量级 AI 编程智能体
前端·node.js·claude
小江的记录本14 小时前
【网络安全】《网络安全三大加密算法结构化知识体系》
java·前端·后端·python·安全·spring·web安全
广师大-Wzx14 小时前
JavaWeb:前端部分
java·前端·javascript·css·vue.js·前端框架·html
M ? A14 小时前
你的 Vue v-memo 与 v-once,VuReact 会编译成什么样的 React 代码?
前端·javascript·vue.js·经验分享·react.js·面试·vureact
是上好佳佳佳呀14 小时前
【前端(七)】CSS3 核心属性笔记:单位、背景、盒子模型与文本换行
前端·笔记·css3
踩着两条虫15 小时前
VTJ:技术架构概述
前端·架构·ai编程
超级无敌攻城狮15 小时前
Agent 到底是怎么跑起来的
前端·后端·架构