Web 项目中 Axios 与 HTTP 状态码的正确打开方式

76 阅读7分钟

Web 项目中 Axios 与 HTTP 状态码的正确打开方式

在日常 Web 开发中,我们几乎每天都在和 axios 打交道。但你真的理解它与 HTTP 状态码之间的关系吗?很多开发者对状态码存在误解,导致错误处理逻辑混乱、用户体验差,甚至埋下线上隐患。

本文将结合实战经验,系统梳理 HTTP 状态码分类、常见误区、Axios 默认行为及增强策略,帮助你构建更健壮的前端请求体系。


一、关于状态码的常见错误认知

❌ 误区1:“404 是服务器的问题”

真相:404 属于 4xx 客户端错误。它表示“你请求的资源不存在”,责任在客户端(比如拼错了 URL、访问了已删除的页面)。服务器只是如实告知结果,这恰恰说明服务正常!

判断标准:如果修正请求(改路径、加参数)就能成功 → 就是客户端问题。


❌ 误区2:“只要返回了 JSON 错误信息,就是业务错误,不算异常”

真相:Axios 的成功/失败判断只看状态码是否在 2xx 范围内,与响应体内容无关。

// 即便服务器返回 { code: 400, msg: "用户名不能为空" }
// 只要状态码是 400,Axios 就会进入 .catch()
axios.post('/register', { username: '' })
  .then(res => { /* 不会执行 */ })
  .catch(err => { /* 会执行!*/ });

⚠️ 很多团队把“业务错误”和“HTTP 错误”混为一谈,导致前端不得不在 .catch() 里写大量业务逻辑,代码难以维护。


❌ 误区3:“304 Not Modified 是错误”

真相:304 表示缓存命中,无需重新下载资源,是一种成功的优化行为。但它属于 3xx 重定向类,Axios 默认将其视为失败(因为不是 2xx)!

这意味着如果你不做特殊处理,所有触发 304 的请求都会进 .catch() —— 这显然不合理。


二、HTTP 状态码权威分类(开发必备)

所有 4xx 状态码都表示「客户端错误」(Client Error)
所有 5xx 状态码才表示「服务端错误」(Server Error)

这是 HTTP 协议的官方定义(RFC 7231 等标准)。

✅ 正确分类

状态码范围类型责任方含义说明
4xx客户端错误客户端请求有问题:格式错、权限不足、资源不存在、方法不对等。服务器理解请求,但拒绝处理或无法处理(因为客户端没按规矩来)。
5xx服务端错误服务端服务器内部出错:代码崩溃、数据库连不上、网关超时等。即使客户端请求完全正确,服务器也无法正常响应。

❌ 常见误解澄清

有些人会误以为:

  • “404 是服务器没找到资源,所以是服务端的问题?”
  • “403 是服务器禁止我访问,是不是服务器配置错了?”

但实际上:

  • 404:客户端请求了一个不存在的路径(比如拼错 URL),服务器如实告知“没有这个东西”——这是对客户端错误请求的合理响应。
  • 403:客户端已认证,但试图访问无权访问的资源(比如普通用户访问 /admin),服务器拒绝——这是客户端越权,不是服务器故障。

👉 关键判断标准

如果修正客户端的请求(改 URL、加 Token、换参数、换方法等)就能成功,那就是 4xx(客户端错误)
如果客户端请求完全正确,但服务器依然挂了或返回异常,那就是 5xx(服务端错误)

📌 举例对比

状态码场景错误类型谁该负责修复?
400 Bad RequestPOST 提交的 JSON 缺少必填字段客户端前端/调用方检查参数
401 Unauthorized没带 Token 或 Token 过期客户端前端应重新登录或刷新 Token
403 Forbidden普通用户访问管理员接口客户端前端不应发起此请求,或提示无权限
404 Not Found请求了 /api/user/999(ID 不存在)客户端前端应确保 ID 有效,或处理“未找到”情况
429 Too Many Requests短时间内发太多请求被限流客户端前端应加节流或提示用户稍后再试
500 Internal Server Error服务器代码抛异常(如空指针)服务端后端需查日志修复 bug
502 Bad GatewayNginx 无法连接后端服务服务端运维/后端检查服务是否宕机
503 Service Unavailable服务器维护或过载服务端后端扩容或等待恢复

💡 在 Axios 中如何区分?

axios.get('/some-api')
  .catch(error => {
    if (error.response) {
      const status = error.response.status;

      if (status >= 400 && status < 500) {
        console.log('⚠️ 客户端错误(4xx):', status);
        // 通常提示用户:检查输入、重新登录、无权限等
      } else if (status >= 500) {
        console.log('🔥 服务端错误(5xx):', status);
        // 通常提示:“系统繁忙,请稍后再试” 或 上报监控
      }
    } else {
      // 网络错误(如断网、DNS 失败)——也属于客户端环境问题
      console.log('🌐 网络连接失败');
    }
  });

✅ 总结

  • 4xx = 客户端错了 → 前端/调用方需要修正请求。
  • 5xx = 服务端错了 → 后端需要排查和修复。
  • 不要混淆“服务器返回了错误”和“服务器自身出错”:4xx 是服务器正确地拒绝了错误请求,这恰恰说明服务器工作正常!

三、Axios 中关于状态码的补充逻辑

1. 自定义“成功”范围:validateStatus

默认只有 2xx 成功。但我们可以扩展:

const instance = axios.create({
  baseURL: '/api',
  // 允许 2xx 和 304 都算成功
  validateStatus: (status) => {
    return (status >= 200 && status < 300) || status === 304;
  }
});

这样 304 就能进入 .then(),避免在 .catch() 里处理缓存逻辑。


2. 响应拦截器:统一错误分类与提示

instance.interceptors.response.use(
  // 成功回调
  (response) => {
    // 可在此处解包业务数据(如 return response.data.data)
    return response;
  },
  // 失败回调
  (error) => {
    const { response } = error;

    if (response) {
      const { status, data } = response;

      // 客户端错误(4xx)
      if (status >= 400 && status < 500) {
        switch (status) {
          case 401:
            // Token 失效,跳转登录
            window.location.href = '/login';
            break;
          case 403:
            message.error('无权限访问');
            break;
          case 404:
            message.error('请求资源不存在');
            break;
          case 429:
            message.warning('操作太频繁,请稍后再试');
            break;
          default:
            message.error(data?.message || '请求参数错误');
        }
      }
      // 服务端错误(5xx)
      else if (status >= 500) {
        message.error('系统繁忙,请稍后再试');
        // 可上报 Sentry / 自研监控
        reportErrorToMonitor(error);
      }
    } 
    // 网络错误(无 response)
    else {
      message.error('网络连接失败,请检查网络');
    }

    // 务必 reject,让上层 .catch 能捕获
    return Promise.reject(error);
  }
);

3. 区分“HTTP 错误”与“业务错误”

更优雅的做法:所有请求都返回 200,在响应体中用 code 字段表示业务状态。

// 成功
{ "code": 0, "data": { ... }, "msg": "ok" }

// 业务失败
{ "code": 1001, "data": null, "msg": "用户名已存在" }

此时 Axios 永远走 .then(),前端只需判断 res.data.code

instance.interceptors.response.use(
  (response) => {
    const { code, data, msg } = response.data;
    if (code !== 0) {
      message.error(msg);
      return Promise.reject(new Error(msg));
    }
    return data; // 只返回业务数据
  }
);

✅ 优点:逻辑清晰,.catch() 仅处理网络/服务器崩溃等真正异常。
⚠️ 缺点:违背 RESTful 原则,调试时状态码信息缺失。

建议:内部系统可用此方案;对外 API 或追求规范的项目,仍推荐使用标准 HTTP 状态码。


四、总结:最佳实践清单

  • 明确责任边界:4xx 是前端问题,5xx 是后端问题。
  • 不要在 .catch() 里写业务逻辑,除非你使用“全 200”方案。
  • 304 应视为成功,通过 validateStatus 修正 Axios 默认行为。
  • 统一错误提示:在响应拦截器中按状态码分类处理。
  • 关键 5xx 错误上报监控,便于快速发现线上问题。
  • 合理选择方案:标准状态码(规范) vs 全 200(简化前端)。

最后提醒:HTTP 状态码是 Web 的通用语言。用好它,你的 API 更专业,你的前端更健壮,你的协作更高效。

觉得有用?点赞 + 收藏,让更多人少踩坑! 💪


参考资料