前端限流实战:从 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 的过度干预,提升了代码的可维护性和扩展性。修改完成后,请务必清除浏览器缓存,再次测试,你将只会看到一个优雅、准确的限流提示。
相关推荐
猜测719 小时前
新语法在旧设备上的问题
前端·javascript·node.js
前端若水19 小时前
实战:纯 CSS 实现“有图片的卡片不同样式”
前端·css
爱喝水的鱼丶20 小时前
SAP-ABAP:SAP 与 ABAP 关联逻辑与入门路径:业务×开发的协作指南
服务器·前端·数据库·学习·sap·abap
小陈的进阶之路20 小时前
Python系列课(2)——判断
java·前端·python
2301_8156453820 小时前
html.
前端·html
qq_3813385021 小时前
CSS @layer 级联层实战指南:从样式冲突到分层架构
前端·css
广州华水科技21 小时前
深度测评2026年好用的单北斗GNSS变形监测系统推荐,提升GNSS位移监测精度,引领智能监控新风尚
前端
ZC跨境爬虫21 小时前
跟着 MDN 学 HTML day_33:(Attr 接口与属性节点的深入理解)
前端·javascript·ui·html·音视频·html5
神所夸赞的夏天21 小时前
如何获取多层json数据,存成dictionary,并取最大最小值
java·前端·json