跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

如何在Golang中发送POST请求_Golang net/http POST请求方法

最直接方式是http.Post,但仅适用于固定类型且无自定义需求;更通用的是http.NewRequest+Do,可灵活控制header、body、超时等;务必关闭resp.Body并配置超时以防连接泄露。 用
http.Post
发送简单表单数据 最直接的方式是调用
http.Post
,但它只适合发送
application/x-www-form-urlencoded
或纯文本这类固定类型的请求。它会自动设置
Content-Type
,但无法自定义其他 header,也不方便传 JSON。 常见错误:传入
nil
的
body
导致 panic;或误把 JSON 字符串当
strings.NewReader
的参数却没设
Content-Type
为
application/json
。 只适用于无认证、无自定义 header、数据格式固定的场景 第三个参数必须是
io.Reader
,比如
strings.NewReader("key=value")
返回的
*http.Response
必须手动
Close()
,否则可能泄露连接
resp, err := http.Post("https://httpbin.org/post", "application/x-www-form-urlencoded", strings.NewReader("name=alice&age=30")) if err != nil { log.Fatal(err) } defer resp.Body.Close() // 关键:不能漏 body, _ := io.ReadAll(resp.Body)
用
http.NewRequest
+
http.DefaultClient.Do
控制细节 这是更通用、更可控的做法。你可以自由设置任意 header、使用任意 body 类型(JSON、XML、文件流等),也便于加超时、重试或自定义 transport。 典型踩坑点:
Content-Length
被错误设置(Go 通常自动计算);忘记设
Content-Type
导致后端解析失败;
Do
调用后不读取响应体,导致连接复用失效。 立即学习 “ go语言免费学习笔记(深入) ”; 构造
*http.Request
后,必须用
req.Header.Set()
显式设置
Content-Type
JSON 请求建议用
json.Marshal
序列化后传给
bytes.NewReader
务必在
Do
后调用
resp.Body.Close()
,哪怕你只关心 状态码
data := map[string]string{"name": "bob", "city": "shanghai"} jsonBytes, _ := json.Marshal(data) req, _ := http.NewRequest("POST", "https://httpbin.org/post", bytes.NewReader(jsonBytes)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer abc123")

client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close()

处理 JSON POST 并检查响应状态 发 JSON 很常见,但仅发出去不够——你还得确认对方是否成功接收并返回了预期结构。不要跳过
resp.StatusCode
判断,也不要直接
json.Unmarshal
未检查的响应体。 php版微信js-sdk支付接口类 php版微信js-sdk支付接口类 下载 容易被忽略的是:HTTP 状态码非 2xx 时,
resp.Body
仍可能含错误信息(如
{"error":"invalid token"}
),直接
Close()
就丢掉了调试线索。 先判断
resp.StatusCode < 200 || resp.StatusCode >= 300
,再决定如何处理 body 用
io.ReadAll
读完整响应体,避免残留数据影响连接复用 反序列化前确保
body
非空且是合法 JSON(可加
json.Valid
校验)
body, _ := io.ReadAll(resp.Body) if resp.StatusCode < 200 || resp.StatusCode >= 300 { log.Printf("HTTP error %d: %s", resp.StatusCode, string(body)) return } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { log.Printf("JSON parse failed: %v, raw: %s", err, string(body)) return }
超时与连接复用的实际影响
http.DefaultClient
默认没有超时,一旦后端卡住或网络中断,goroutine 会永久阻塞。同时,它默认启用连接池,但若 response body 没读完或没
Close
,连接就无法归还,池子很快耗尽。 线上服务出问题,八成跟这个有关:看着请求发出去了,实际连接数疯涨,新请求全 hang 在
Do
上。 永远不要用
http.DefaultClient
做生产请求;自己构建带
Timeout
的
*http.Client
transport 层可进一步限制最大空闲连接数:
&http.Transport{MaxIdleConnsPerHost: 32}
即使只读状态码,也要
io.Copy(io.Discard, resp.Body)
或
resp.Body.Close()
client := &http.Client{ Timeout: 5 * time.Second, Transport: &http.Transport{ MaxIdleConnsPerHost: 64, IdleConnTimeout: 30 * time.Second, }, }
真正难的不是写对那几行代码,而是每次
Do
之后,你有没有条件反射地看一眼
resp.Body.Close()
和超时配置。

相关文章