http.post 返回400因未设content-type,需用http.newrequest手动构造并设header;传文件必须用multipart/form-data;表单须显式调parseform,json需用json.decode。

Go 发送 POST 表单时 http.Post 为什么总返回 400?
因为 http.Post 默认不设置 Content-Type,后端收不到标准的表单格式。它把数据当纯文本发,不是 application/x-www-form-urlencoded。
正确做法是用 http.NewRequest 手动构造请求:
- 用
url.Values构建键值对,再调用.Encode()得到合法表单字符串 - 显式设置 header:
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - 把编码后的字节传给
http.DefaultClient.Do(req)
示例:
data := url.Values{"username": {"alice"}, "password": {"123"}}
req, _ := http.NewRequest("POST", "https://api.example.com/login", strings.NewReader(data.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, _ := http.DefaultClient.Do(req)
上传文件和表单字段混在一起,该用 multipart/form-data 还是 url.Values?
只要带文件,必须用 multipart/form-data;纯文本字段(如 name、email)不能和文件共用 url.Values 编码。
net/http 自带 multipart.Writer,但注意三点:
- 别手动写 boundary —— 用
wtr := multipart.NewWriter(resp.Body),它会自动生成并设好 header - 文本字段用
wtr.WriteField("key", "value"),文件用wtr.CreateFormFile("file", "name.txt")再写入内容 - 写完必须调用
wtr.Close(),否则服务端收不到结束标记,可能卡住或报 400
表单提交后服务端收不到字段,检查 ParseForm 调用时机
Go 的 http.Request 不会自动解析表单体,必须显式调用 r.ParseForm() 或 r.ParseMultipartForm(),否则 r.Form 是空的。
常见错误:
- 在调用
ParseForm前就读r.FormValue("xxx")→ 返回空字符串 - POST 传了 JSON 却调
ParseForm→ 没用,JSON 要用json.Decode(r.Body) - 用了
ParseMultipartForm但没设MaxMemory→ 大文件直接 500,建议设成32 (32MB)
要不要用第三方库比如 go-resty?
小项目纯 net/http 足够,可控性强;但遇到重试、超时链、Bearer Token 自动注入、JSON/表单自动切换时,go-resty 真省事。
关键区别:
-
resty.R().SetFormData(map[string]string{"a": "b"}).Post(url)自动设 content-type 和编码 - 它默认带 30 秒超时,而原生
http.Client默认无限等待 - 如果已在用
resty,别为了表单又切回原生 —— 容易漏掉 header 继承或 cookie jar 同步
真正容易被忽略的是:表单字段名大小写敏感,后端要什么就填什么,UserID 和 userid 是两个字段,错一个就收不到。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











