go 的 http.request.header.get("content-type") 返回空表示客户端未发送该头,需检查 error 并用 mime.parsemediatype() 解析;r.parseform() 不处理 json,须按 mediatype 分支解析;r.body 是单次读取流,需缓存避免重复读取。

Go 的 http.Request.Header.Get("Content-Type") 返回空或不准?
Go 标准库不会自动解析或“识别” Content-Type,它只是原样读取请求头。常见错误是直接调用 r.Header.Get("Content-Type") 却没注意到大小写不敏感但拼写必须准确(比如写成 "content-type" 也能工作,但写成 "Content_Type" 就失效),更关键的是:如果客户端根本没发这个头,就返回空字符串——不是 nil,也不是默认值。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 永远用
r.Header.Get("Content-Type"),不要自己拼接或猜键名 - 检查返回值是否为空:
if ct := r.Header.Get("Content-Type"); ct == "",此时应拒绝请求或按业务逻辑 fallback(比如 API 默认只接受application/json) - 注意 multipart 请求的 Content-Type 带参数,例如
multipart/form-data; boundary=----WebKitFormBoundary...,不能直接字符串比较,要用mime.ParseMediaType()
mime.ParseMediaType() 解析失败导致 panic?
这个函数不 panic,但它返回 (string, map[string]string, error)。常见坑是忽略第三个 error,直接用前两个返回值,结果拿到空 media type 和 nil params map,后续调用 strings.HasPrefix() 或取 params["boundary"] 就 panic。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 必须检查 error:
mediaType, params, err := mime.ParseMediaType(ct);if err != nil就该返回 400 Bad Request - 对
application/json这类无参数类型,params是空 map,没问题;但multipart/form-data必须有boundary,否则无效 - 不要自己用
strings.Split()手动切分 Content-Type,RFC 2045 规定参数值可带引号、转义,mime.ParseMediaType()才是唯一可靠方式
为什么 r.ParseForm() 后 r.FormValue() 拿不到 JSON 字段?
因为 r.ParseForm() 只处理 application/x-www-form-urlencoded 和 multipart/form-data,对 application/json 完全无视。它不会报错,也不会填充 r.Form,所以 r.FormValue("xxx") 永远返回空。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 先判断
mediaType,再决定用哪个解析路径:if strings.HasPrefix(mediaType, "application/json")→ 用json.NewDecoder(r.Body).Decode() - 别在 JSON 请求里还调用
r.ParseForm(),它会消费r.Body,导致后续json.Decode()读到空内容 - 如果想统一收口,可以封装一个
ParseRequestBody(r *http.Request, v interface{}) error,内部按 Content-Type 分支处理
自定义中间件里提前读取 r.Body 导致后续解析失败?
HTTP 请求体是单次读取流,一旦 io.ReadAll(r.Body) 或 json.Decode() 调用过,r.Body 就变空了。后续任何依赖 r.Body 的逻辑(包括标准库的 r.ParseMultipartForm())都会读不到数据。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 如果中间件需要 inspect body(比如日志、鉴权),必须用
io.TeeReader或重置r.Body:r.Body = io.NopCloser(bytes.NewReader(buf)) - 标准库没有内置 “rewind” 支持,别指望
r.Body.Seek(0, 0)—— 大部分http.Request.Body实现不支持 Seek - 最稳妥的做法:在 handler 开头一次性读完并缓存,然后根据 Content-Type 分发给不同解析器,避免多次读取
Content-Type 不是魔法开关,Go 不会替你做格式推断或自动反序列化。每个分支都要显式处理,而且顺序和 Body 读取时机稍错一点,就会静默失败。










