
在 Go 的 net/http 中,判断请求体是否为空不能仅依赖 ContentLength == 0,而应通过读取并解析 Body 并检查 io.EOF 等错误来准确判定,同时需兼顾安全性与健壮性。
在 go 的 `net/http` 中,判断请求体是否为空不能仅依赖 `contentlength == 0`,而应通过读取并解析 body 并检查 `io.eof` 等错误来准确判定,同时需兼顾安全性与健壮性。
在构建 RESTful API 或处理表单提交时,准确识别空请求体(empty request body)是常见且关键的需求。然而,许多开发者误以为检查 r.ContentLength == 0 就足够安全——这在实践中存在严重缺陷。
❌ 为什么 ContentLength == 0 不可靠?
Go 官方文档明确指出:对于服务端接收的请求,Request.Body 永远非 nil,但若无请求体,则首次读取即返回 io.EOF;而 ContentLength 字段在服务端虽可为 0,但它仅表示客户端声明的长度,并不保证实际传输行为:
- 客户端可能使用 Transfer-Encoding: chunked(此时 ContentLength 为 -1,且无长度声明);
- 客户端可能伪造或遗漏 Content-Length 头;
- 网络中断、代理截断、中间件重写等场景下,声明与实际严重不符。
因此,仅靠 r.ContentLength == 0 判断空体,既违反 HTTP 协议语义,也极易导致逻辑漏洞(如跳过校验却意外收到数据)。
✅ 推荐方案:以读取为核心,以错误分类为依据
最可靠的方式是 真正尝试读取 Body,并根据 json.Decoder.Decode() 或 io.Read 的返回错误进行语义化判断:
func handleUserCreate(w http.ResponseWriter, r *http.Request) {type Input struct { Name *string `json:"name"`} input := new(Input) decoder := json.NewDecoder(r.Body) err := decoder.Decode(input) switch {case err == io.EOF: // ✅ 明确表示:请求体完全为空(无字节)http.Error(w, "Empty request body", http.StatusBadRequest) return case err != nil: // ⚠️ 其他错误:JSON 格式错误、I/O 超时、流被关闭等 http.Error(w, "Invalid JSON: "+err.Error(), http.StatusBadRequest) return } // ✅ 成功解码:body 非空且格式合法,继续业务逻辑 log.Printf("Received name: %v", input.Name) }
? 注意:json.Decoder.Decode() 在输入为空时返回 io.EOF;若 Body 含有空白(如 “n”、” ” 或 {}),则会返回 nil(成功)或具体解析错误(如 json.SyntaxError),因此该方式天然区分「空」与「无效 / 空白有效载荷」。
? 进阶考量:内存安全与防御式设计
若需提前获取原始字节(例如日志记录、签名验证、多路复用解析),应避免无限制读取:
const maxBodySize = 1 << 20 // 1MB body, err := io.ReadAll(io.LimitReader(r.Body, maxBodySize)) if err != nil {http.Error(w, "Failed to read body", http.StatusInternalServerError) return } if len(body) == 0 {// 空请求体} // 后续可重复使用 body 字节切片做 JSON 解析、校验等 if len(body) > 0 {if err := json.Unmarshal(body, input); err != nil {// ……} }
⚠️ 重要提醒:
- r.Body 是单次读取流,一旦读取后即耗尽;若需多次使用,务必用 bytes.NewReader(body) 重建;
- 始终设置读取上限(如 io.LimitReader),防止恶意超大请求引发 OOM;
- 不要依赖 r.Header.Get(“Content-Length”) 字符串解析——它不可信,且 r.ContentLength 已是 int64 类型字段。
✅ 总结:三原则保障健壮性
| 原则 | 说明 |
|---|---|
| 读取即验证 | 真实读取 Body 是唯一权威方式,ContentLength 仅作辅助参考 |
| 错误语义化处理 | 区分 io.EOF(空)、json.SyntaxError(格式错)、io.ErrUnexpectedEOF(截断)等,拒绝一概而论 |
| 资源受控 | 始终限制读取长度,及时关闭或重置 Body 流 |
遵循以上实践,你将构建出既符合 HTTP 规范、又具备生产级鲁棒性的请求体空值检测逻辑。