人人都会AI编程

请求流程、状态码、响应处理

更新时间:2026-07-11

XMLHttpRequest 虽然被称为“古老”的 API,但它奠定了浏览器端网络请求的基本模型。理解它的请求流程、状态码含义和响应处理方式,不仅有助于维护旧代码,也能更清晰地认识 Fetch 等现代方案底层的设计脉络。

请求流程:从创建到完成

一次完整的 XHR 请求会经历几个明确的阶段,每个阶段都对应 readyState 属性的一个值,并触发 onreadystatechange 事件:

| readyState | 状态常量 | 含义说明 |
|------------|------------------------|------------------------------------------|
| 0 | UNSENT | 已创建实例,但 open() 尚未调用 |
| 1 | OPENED | open() 已调用,可以设置请求头 |
| 2 | HEADERS_RECEIVED | 已收到服务器响应的状态行和头部信息 |
| 3 | LOADING | 正在接收响应体,部分数据可能已可用 |
| 4 | DONE | 请求完成(无论成功或失败),可获取完整响应 |

一个典型的请求流程如下:

  1. 创建实例
   const xhr = new XMLHttpRequest();
   // readyState === 0
   
  1. 调用 open 配置请求
   xhr.open('GET', '/api/users', true); // 第三个参数 true 表示异步
   // readyState 变为 1
   

open() 仅配置请求,还未真正发送。可以在这里设置请求头。

  1. 设置状态变化监听
   xhr.onreadystatechange = function() {
     if (xhr.readyState === 4) {
       // 请求完成,处理响应
     }
   };
   
  1. 发送请求
   xhr.send(null); // GET 请求通常传 null 或不传
   

调用 send() 后,请求真正发出。后续 readyState 会依次经历 2(收到头部)、3(接收数据中)、4(完成)。

实际开发中,推荐使用 onloadonerror 等现代事件代替 onreadystatechange,代码更清晰:

xhr.onload = function() {
  // 请求成功完成(包括4xx、5xx也视为成功加载)
  if (xhr.status >= 200 && xhr.status < 300) {
    // 处理成功响应
  }
};
xhr.onerror = function() {
  // 网络错误或请求完全无法发出
};
xhr.ontimeout = function() {
  // 请求超时
};

状态码:判断请求结果

HTTP 状态码是服务器对请求处理结果的数字标识。XHR 的 status 属性返回的就是这个状态码。虽然不是所有状态码都需要熟记,但以下几类必须能够识别并正确处理:

  • 2xx 成功

最常见的是 200 OK(成功返回数据)。POST 创建资源可能返回 201 Created,有时会附带 Location 头指向新资源地址。

  • 3xx 重定向

浏览器会自动跟随重定向(除非手动阻止),所以 XHR 的 status 通常不会遇到 301/302。如果服务端返回 304(Not Modified),表示本地缓存可用,通常来自协商缓存的响应。

  • 4xx 客户端错误

400 Bad Request 表示请求参数有误;
401 Unauthorized 表示需登录认证,前端应跳转登录页或刷新 token;
403 Forbidden 表示无权限访问,即使登录了也无权;
404 Not Found 表示资源不存在。
这些状态码都意味着请求已到达服务器,但服务器拒绝了请求或找不到资源,需要根据具体码进行不同的上游处理。

  • 5xx 服务器错误

500 Internal Server Error(服务器内部错误)、502 Bad Gateway(网关错误)、503 Service Unavailable(服务暂不可用,多用于服务器过载)。收到 5xx 时,通常可以实行重试策略,或者前台提示用户稍后再试。

一个健壮的响应处理必须区分这些状态,而不是仅拿 xhr.status === 200 一判了之。同时,onerror 捕获的是网络层面的异常(请求根本没到达服务器),此时 status 甚至不存在,与 4xx/5xx 需要严格区分处理。

响应处理:解析不同类型的数据

XHR 提供了多种方式读取服务器返回的响应体,借助 responseType 属性可以指定期望的数据格式,然后用相应属性获取:

| responseType | 对应获取属性 | 说明 |
|--------------|----------------|-----------------------------|
| "" (默认) | responseText | 字符串 |
| "json" | response | 自动解析为 JavaScript 对象 |
| "text" | responseText | 等同于默认 |
| "blob" | response | 二进制大对象,适合文件下载 |
| "arraybuffer" | response | 原始二进制数据,适合音视频、WebSocket |
| "document" | responseXML | XML / HTML 文档对象(较少用) |

示例:请求 JSON 数据

const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/user/1');
xhr.responseType = 'json'; // 自动解析成 JS 对象
xhr.onload = function() {
  if (xhr.status === 200) {
    console.log(xhr.response.name); // response 已经是对象
  }
};
xhr.send();

设置 responseType = 'json' 后,就不需要手动 JSON.parse()。但需注意:如果响应体格式非法,response 会是 null,应当在 onload 中判断。

错误处理的完整防御
一个生产级别的 XHR 封装,至少应覆盖:

  • 网络错误(onerror
  • 超时(timeout + ontimeout
  • HTTP 状态码异常(在 onload 中判断 status
  • 响应体解析异常(比如 JSON 解析失败,responsenull
  • 请求中断(abort() 调用,可通过一个 flag 或 onabort 捕获)

在后续小节介绍的 Axios 会将这些细节统一封装,但底层原理依旧是 XHR 的状态流转与事件模型。知其所以然,才能在遇到封装库边界问题时自行诊断,而不是束手无策。