204 表示成功,但本来就没有内容给你。它不能有正文,客户端也不会去解析正文。
上一节是「有内容」的 200,这一节是「没内容」的 204。 两者的差别看着小,实际影响客户端行为和框架解析。
一、三个使用场景
删除:DELETE /item/1 成功,没什么可返回的。
提交:表单处理完,不需要返回到页面(不过更常见的是 303)。
心跳:探活接口只关心「活着吗」,不关心返回什么。
共同点:客户端不需要响应内容,只需要知道「成了」。
二、硬规矩:不能有正文
204 的响应里不能有正文,也不该带 Content-Length(带了必须是 0)或 Transfer-Encoding。
带了就是协议错误。
Demo 里 204 响应整段 81 字节,正文 0 字节;
对照的 200 空正文是 83 字节——只差 2 字节,但语义完全不同。
为什么要在意这 2 字节的差别?因为客户端处理不同:对 204 它不会去读正文、不会渲染、不会跳转;
对 200 空正文,它可能还会尝试解析 JSON,然后报「空响应」错误。
三、204 与 205、304 的区别
205 Reset Content 要求客户端重置视图(清空表单),很少用。
304 Not Modified 也是无正文,但它是缓存协商的结果,意思是「用你本地那份」,
不是「没有内容」。
这三个都无正文,只有 204 是「操作成功且无需返回」。
看码别只看有没有正文——这是读抓包时最容易误判的地方。
四、动手 Demo:204 与 200 空正文的对照
先发一个 DELETE 拿 204,再发一个 GET 拿 200 空正文,两份响应并列看字节数与首部。
def resp(raw, st):
return (u'HTTP/1.1 204 No Content', [(u'X-Why', u'deleted')], b'')
got = _one(resp, _req(u'DELETE', u'/item/1'))
print(len(got)) # 整段只有首部
实跑输出:
--------------------------------------------------------------------
204 No Content:成功,但没有东西给你
--------------------------------------------------------------------
状态行 HTTP/1.1 204 No Content
正文长度 0 字节
Content-Length 0
整段响应 81 字节
对照:200 空正文 HTTP/1.1 200 OK,83 字节
204 的用途 操作成功但无需返回内容(删除、提交、心跳)
204 的硬规矩 **不能有正文**,也不能带 Content-Length 以外的实体首部
204 vs 200 空正文 204 是「本来就没有」,200 空是「有但空」
用 204 的好处:客户端不会去解析正文,浏览器也不会跳转,
省掉一次无意义的渲染,也避免了「空正文被当成错误」的误判。
正文长度 0 字节、Content-Length 0、整段响应 81 字节。81 字节全是首部——这就是 204 的形态。
对照:200 空正文 83 字节。差的 2 字节就是状态行里 OK 与 No Content 的长度差。
204 vs 200 空正文 204 是「本来就没有」,200 空是「有但空」——这一句是本节的结论。
它不是文字游戏,客户端行为真的不一样。
30 秒实验
- 给 204 塞一个非空正文,看客户端会不会读(会读到,但那是协议错误)。
- 给 204 加
Content-Length: 5,看客户端会不会等 5 个字节(会,然后超时)。 - 想想:删除接口回 204 好,还是回 200 加
{"deleted": true}好?
常见误区
- 「204 就是空的 200」——语义不同,客户端处理路径也不同。
- 「204 可以带正文」——不能,带了就是协议错误。
- 「304 也是没有内容」——304 的意思是「用你缓存里那份」,不是「没有」。
自测三问(点开对答案)
1. 204 能不能带正文? —— 不能。带了就是协议错误,客户端可能报错或直接忽略。 2. 204 和 200 空正文有什么实际差别? —— 语义不同:204 表示本来就没有,客户端不会尝试解析;200 空正文可能被解析并报错。 3. 304 为什么也没有正文? —— 它的意思是「你本地那份还有效」,所以只需要首部,不需要重新传内容。204 没有内容。下一节看「只给一部分内容」的那个码。
- ☐ (本节完,下一节 4.2.3 206 Partial Content)