必须显式调用 r.ParseForm() 才能读取表单数据,否则 r.FormValue 返回空;ParseForm 解析 URL 查询和表单体(不含文件),含文件需用 ParseMultipartForm 并设 MaxMemory;r.PostForm 仅含 POST 数据,优先使用以避免 GET 参数干扰。
http.Request.ParseForm 正确读取表单数据Go 标准库不自动解析表单,必须显式调用 ParseForm 才能访问 r.Form 和 r.PostForm。跳过这步直接读 r.FormValue("name") 可能返回空值,尤其在 POST 请求中。
常见错误现象:前端提交了 username=alice,但后端打印 r.FormValue("username") 为空;或 r.Method 是 POST 却无法获取任何字段。
r.ParseForm(),且仅需一次 —— 多次调用会 panicParseForm 同时解析 URL 查询参数(r.URL.Query())和请求体(application/x-www-form-urlencoded 或 multipart/form-data),但对后者只解析非文件字段r.ParseMultipartForm,否则 ParseForm 会忽略文件字段且不报错r.Form 与 r.PostForm 的实际用途r.Form 包含所有键值(GET 查询 + POST 表单),而 r.PostForm 仅含 POST 请求体中的键值(不含 URL 参数)。多数场景下应优先用 r.PostForm 避免意外覆盖。
例如:请求 URL 是 /login?debug=1,POST body 是 username=admin&password=123,则:
fmt.Println(r.Form) // map[debug:[1] username:[admin] password:[123]] fmt.Println(r.PostForm) // map[username:[admin] password:[123]]
r.PostForm.Get("username")
r.FormValue("key") 是快捷方式,等价于 r.PostFormValue("key")(注意:它不查 r.URL.Query,这点文档易误导)Get 取首个值,或用 ["key"][0](需判空)multipart/form-data(含文件上传)的正确流程当 HTML 表单设 enctype="multipart/form-data",ParseForm 不解析文件字段,必须用 ParseMultipartForm,且需预先设置内存限制。
典型错误:未调用 ParseMultipartForm 就尝试 r.MultipartForm.File["avatar"],结果为 nil;或未设 MaxMemory 导致大文件直接写磁盘而无提示。
r.ParseMultipartForm(32 (32MB 内存上限),否则 r.MultipartForm 为 nil
r.MultipartForm.File["fieldname"] 获取,返回 []*multipart.FileHeader
r.PostFormValue("title") 读取,无需从 MultipartForm.Value 手动取fileHeader.Open() 得到 io.ReadCloser,务必 defer 关闭r.Body 不能反复读取?如何安全复用Go 的 http.Request.Body 是单次读取流,调用 ParseForm 或 ParseMultipartForm 会消耗它。之后再读 r.Body 会得到空内容 —— 这是生产环境常见静默失败点。
ParseXXX 前用 io.ReadAll(r.Body) 保存,并用 bytes.NewReader 重置 r.Body
r.Body 在中间件和 handler 中多次读取;标准做法是中间件解析后把结构体塞进 r.Context()
json.NewDecoder(r.Body).Decode(&v),避免碰 ParseForm —— 二者互斥表单解析本身不复

ParseForm 返回的 error(比如请求体超限或编码错误),导致后续读取始终为空却无提示。