前端限流实战:从 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 的过度干预,提升了代码的可维护性和扩展性。修改完成后,请务必清除浏览器缓存,再次测试,你将只会看到一个优雅、准确的限流提示。
相关推荐
陈林梓4 小时前
Qiankun 微前端配置详解
前端
英俊潇洒美少年4 小时前
Vue3 的 JSX 函数组件,每次更新都会重新运行吗?
前端·javascript·vue.js
木斯佳4 小时前
前端八股文面经大全:腾讯前端暑期AI面(2026-03-26)·面经深度解析
前端·人工智能·ai·智能体·暑期实习
invicinble4 小时前
对于一个基本的前端后台管理框架的分析和认识
前端
恋猫de小郭4 小时前
Android 17 新适配要求,各大权限进一步收紧,适配难度提升
android·前端·flutter
高桥凉介发量惊人4 小时前
UI 与交互篇 (3/6):动画体系:隐式动画到自定义动画
前端
cyforkk4 小时前
前端架构实战:当服务器关闭时,如何优雅提示 502 错误?
服务器·前端·架构
高桥凉介发量惊人4 小时前
UI 与交互篇(1/6):组件化思路:从页面复制到可复用组件
前端
kyriewen4 小时前
Generator 函数:那个能“暂停”的函数,到底有什么用?
前端·javascript·面试