本节摘要:Ajax 只是运输工具,HTTP 才是浏览器与服务器之间约定好的语言。请求方法的语义、状态码的准确解读、Content-Type 的匹配、缓存头部的工作方式——这些协议层知识直接决定你的请求是 200 还是 400、是毫秒级返回还是每次都等服务器重算。本节从三个真实故障切入,把与 Ajax 最相关的 HTTP 知识一次讲透。
现场一:前端把一个创建订单的请求从 GET 改成 POST,参数从查询串挪进了请求体,服务器却一直返回 400。网络面板里看,请求体明明有数据。原因:Content-Type 声明的是 application/json,但发送的却是拼接出来的表单字符串;服务器按 JSON 解析,第一个字符就崩了。知识点:头与体必须匹配。
现场二:列表页的筛选接口,开发环境好好的,上线后"改了筛选条件,结果永远是旧数据"。抓包发现接口返回 200,但响应头里赫然是 from cache 相关标记,且 URL 完全相同。原因:浏览器把 GET 响应按启发式缓存存了下来,同样的 URL 直接吃缓存,根本没到服务器。知识点:缓存控制是 HTTP 协议的一部分。
现场三:调试时给请求加了一个自定义头 X-Auth-Token,请求突然失败了,控制台提示跨域预检没通过——明明页面和接口"看起来"是同源的。知识点:方法、头部与协议端口共同定义了 HTTP 语义与同源边界。
三个现场指向同一个结论:Ajax 开发者必须把 HTTP 当作一门要读写的语言,而不只是透明的管道。

HTTP 方法定义"这次请求想干什么"。Ajax 里最常用的五个:
| 方法 | 语义 | 请求体 | 幂等 | 典型 Ajax 场景 |
|---|---|---|---|---|
| GET | 获取资源 | 无(参数在查询串) | 是 | 列表、详情、搜索 |
| POST | 创建资源 / 提交动作 | 有 | 否 | 发评论、下单、登录 |
| PUT | 整体替换资源 | 有 | 是 | 编辑保存整份配置 |
| PATCH | 部分更新资源 | 有 | 通常否 | 改一个字段 |
| DELETE | 删除资源 | 通常无 | 是 | 删除收藏、注销 |
"幂等"这一列值得专门解释:同样的请求发一次和发八次,服务器状态相同,就是幂等。GET 天然幂等(只是读);POST 不幂等——重复提交会创建多个订单,这就是表单要防重复点击(3.2 节)的协议层根源;PUT 幂等,因为"整体替换成 X"执行多少次结果都一样。理解幂等,你才知道哪些请求必须做防重、哪些失败后可以放心自动重试(4.4 节)。
工程实践上最常见的错误是"全程 GET 加参数"或"全程 POST":用 GET 做写操作(参数可能被缓存、被浏览器历史记录、被代理存档,还会被爬虫触发);用 POST 做一切读操作(失去缓存机会、语义混乱、接口难以演进)。方法选对,网关、缓存、监控才能各司其职。
状态码是响应的三位数结论。Ajax 代码要按它分流处理逻辑,读不准就会把 401 当成"网络错误"提示用户重启路由器。
2xx 成功 family。200 一切正常;201 创建成功(配合响应头 Location 指向新资源地址);204 成功但无返回体(典型如 DELETE 之后);206 部分内容(断点续传、视频拖动进度条就靠它)。注意判断成功用范围 status >= 200 && status < 300,只写 === 200 会漏掉 201/204——这是 2.1 节排障案例的教训之一。
3xx 重定向 family。301 永久迁移、302 临时迁移、304 缓存未变。对 XHR/Fetch 来说,浏览器默认自动跟随 301/302(Fetch 可通过 redirect 选项控制),脚本一般感知不到;304 例外——它不是"跳转"而是"缓存协商命中",浏览器直接用本地缓存,脚本看到的是 200 的效果但网络耗时接近零。
4xx 客户端 family。400 请求格式错误(头体不匹配、JSON 解析失败);401 未认证(没登录或凭证过期);403 已认证但无权限(登录了但不是管理员);404 资源不存在;405 方法不被允许(用 GET 打了一个只收 POST 的接口);429 请求太频繁(触发了限流)。4xx 的共同含义是**"你的请求有问题,重发同样的请求不会变好"**——所以对 4xx 做自动重试基本无意义,该提示用户、该改代码。
5xx 服务器 family。500 服务器内部错误、502 网关收到坏响应、503 服务不可用(过载或维护)、504 网关等待超时。5xx 的含义是**"不全是你的错,稍后重试有可能恢复"**——这是自动重试(配合退避)合理作用的区间。
一张分流速查:
2xx → 正常处理数据 304 → 缓存命中 无感知 401 → 引导重新登录 403 → 提示无权限 404 → 提示不存在或引导返回 4xx 其余 → 请求本身有问题 检查参数与格式 429 → 退避重试或提示稍后 5xx → 可重试 提示服务暂时不可用
头部是键值对形式的元信息,Ajax 开发者最常打交道的是这几组。
Content-Type(双向):请求方向声明"我的体是什么格式",响应方向声明"我的体是什么格式"。常见的取值与对应体格式——application/json(JSON 文本)、application/x-www-form-urlencoded(a=1&b=2 形式的表单字符串)、multipart/form-data(可含文件的分块表单,边界符自动生成)、text/plain、text/html。**现场一的 400 就是请求头与体不匹配**:声明 JSON 却发表单串,服务器解析第一步就失败。规则很简单:发什么声明什么,用 FormData 就让浏览器自己声明。
Authorization:携带访问令牌,最常见的形式是 Bearer 加令牌串。它几乎总是伴随 401 的世界——令牌过期、格式错、少前缀,服务器一律回 401。
Origin 与 Referer:浏览器自动附加,标明请求来自哪个页面。跨域场景(4.2 节)里 Origin 是 CORS 协商的核心字段。
Accept:声明客户端"想收"什么格式。多数接口默认返回 JSON,可不设;做内容协商的接口会按它分流。
读取响应头用 xhr.getResponseHeader('名字') 或 Fetch 的 response.headers.get('名字')。典型用途:从 Content-Disposition 拿文件名(4.3 节下载场景)、从分页相关自定义头拿总数、从 ETag 拿缓存标识。
⚠️ 注意一个易踩点:自定义头的命名有安全边界,且跨域响应里默认只暴露少数几个"简单头"(Cache-Control、Content-Type 等),想读其他头需要服务端配合 Access-Control-Expose-Headers 声明——"代码里读不到响应头但网络面板看得到"的怪象,十有八九是这个原因。
Ajax 应用的性能大头在网络往返,而 HTTP 缓存机制能让"重复请求同一家资源"这件事几乎免费。它分两层。
强缓存:响应头 Cache-Control: max-age=3600 告诉浏览器"这份响应一小时内在本地有效"。有效期内再请求同一 URL,浏览器直接用本地副本,连 HTTP 请求都不发(网络面板里会显示来自缓存,耗时为零)。配套的 no-cache(可缓存但每次要协商验证)与 no-store(完全不缓存,敏感数据必用)同样常用。
协商缓存:强缓存过期后,浏览器带着上次响应留下的标识去问服务器"变了吗"。标识有两种:ETag(内容的指纹)配合请求头 If-None-Match,或 Last-Modified(最后修改时间)配合 If-Modified-Since。内容没变,服务器只回一个 304,不含响应体——省掉的是传输,没省掉往返。
对 Ajax 开发,这套机制带来两条实操规则:
**规则一:接口数据慎用强缓存,善用协商缓存。**数据接口的典型配置是 no-cache 或短 max-age 加 ETag——保证新鲜度,同时未变化时用 304 省流量。把业务数据设成长强缓存,就是现场二的"筛选永远旧数据"事故。
**规则二:请求侧用 URL 变化来控制缓存命不命中。**GET 请求的完整 URL(含查询串)是缓存键。需要强制不走缓存时,加一个时间戳参数即可:
const url = '/api/time?t=' + Date.now(); // 每次都是新键 必然回源
反过来,同一份数据在有效期内请复用同一个 URL,让缓存层替你挡掉重复请求。Fetch 还提供了 cache: 'no-store' 等请求级选项,粒度比 URL 技巧更干净。
静态资源(JS、CSS、图片)则应放心用强缓存加长过期时间,配合文件名带内容哈希的发布策略——内容一变文件名就变,缓存键自然更新。这套"接口轻缓存、资源重缓存"的组合拳,是 4.1 节性能优化的协议层地基。
实际上不会,这也是敏感操作走 POST 的附加好处之一。但别把它当安全机制——防重复提交仍要靠按钮态与服务端幂等设计。
算成功。浏览器会把本地缓存补成完整响应交给脚本,你看到的是正常数据。性能优化时可以留意 304 的比例,它反映协商缓存的效率。
不少团队约定 HTTP 一律 200,业务成败放在响应体的 code 字段里。两种风格都能工作,关键是全链路统一——最糟的是混用:一半接口用 401 表未登录,另一半用 code=401 包在 200 里,客户端要写两套判断。2.4 节接口设计会继续讨论这个取舍。
把本节知识用起来的最好方式,是找自己项目里的一个真实请求"朗读"一遍——打开网络面板,选中它,从头到尾说一遍每一段在说什么。示范:请求行是 POST 加接口路径,说明这是一次创建类操作,非幂等,防重复要靠按钮锁或幂等键;请求头里 Content-Type 是 JSON、Authorization 携带 Bearer 令牌、Origin 标明来源页;响应行 201,创建成功,Location 头里有新资源的地址;响应头 Content-Type 确认了体的格式、ETag 给了缓存指纹、缓存声明是可协商的轻缓存;响应体是约定的包裹结构,业务码为零,data 里是新建的资源。能不卡壳地读完这一段,HTTP 对你就不再是黑盒。
朗读练习的价值在于建立条件反射式的分流直觉:看到 401 想到引导登录、看到 429 想到退避、看到 304 想到协商缓存命中、看到 Content-Type 与解析方式不符想到格式事故。这些反射在事故时刻比翻文档快得多,而它们只能从一次次真实朗读里长出来。建议每接手一个新项目,就把它的核心接口各朗读一遍——顺带把接口约定里的坑(业务码是包在 200 里还是用状态码、错误结构长什么样)提前摸清,比在联调时撞上要从容得多。朗读时还可以拉上后端同学一起,两个人对着同一段报文各讲一遍自己的理解,出入处往往就是将来出 bug 的地方——这种十分钟的对照,省下的是事后几小时的互相甩锅,也让两端对"同一份契约"的记忆保持一致。