
本文详解 go 中使用 `net/http` 发送标准 `application/x-www-form-urlencoded` 类型 post 请求的正确方式,指出常见错误(如缺失 content-type、误用缓冲区),并提供可直接运行的健壮示例代码。
在 Go 中模拟 curl -X POST -d "key=val" ... 的行为,关键在于两点:正确设置请求头 Content-Type 和 以 URL 编码格式提交请求体。原代码中虽调用了 data.Encode() 生成了 api_token=MY_KEY&action=list_projects 字符串,但遗漏了至关重要的 Content-Type: application/x-www-form-urlencoded 头,导致服务端无法识别表单数据,从而返回“无 POST 数据”的错误。
以下是推荐的两种实现方式:
✅ 方式一:使用 http.PostForm(最简洁,推荐初学者)
package main
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
func main() {
apiUrl := "https://example.com/api/"
data := url.Values{
"api_token": {"MY_KEY"},
"action": {"list_projects"},
}
resp, err := http.PostForm(apiUrl, data)
if err != nil {
fmt.Printf("请求失败: %v\n", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("读取响应失败: %v\n", err)
return
}
fmt.Println("HTTP 状态:", resp.Status)
fmt.Println("响应内容:", string(body))
}http.PostForm 内部自动设置 Content-Type 并编码表单,语义清晰、不易出错。
✅ 方式二:手动构造 http.Request(更灵活,适合进阶控制)
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
func main() {
apiUrl := "https://example.com/api/"
data := url.Values{}
data.Set("api_token", "MY_KEY")
data.Set("action", "list_projects")
// 注意:必须显式设置 Content-Type
req, err := http.NewRequest("POST", apiUrl, bytes.NewBufferString(data.Encode()))
if err != nil {
fmt.Printf("创建请求失败: %v\n", err)
return
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("发送请求失败: %v\n", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("读取响应失败: %v\n", err)
return
}
fmt.Println("HTTP 状态:", resp.Status)
fmt.Println("响应内容:", string(body))
}⚠️ 关键注意事项
- 永远检查 err:网络请求可能因超时、DNS 失败、连接拒绝等失败,忽略 err 会导致程序静默崩溃或逻辑错误。
- defer resp.Body.Close() 必须在 err == nil 后调用:否则当 resp 为 nil 时会 panic。
- 避免使用已弃用的 ioutil(Go 1.16+):生产环境建议升级至 io.ReadAll(需 import "io"),ioutil 已被标记为 deprecated。
- 不要用 strings.NewReader 替代 bytes.NewBufferString:二者功能等价,但 bytes.NewBufferString 更常用于此场景;重点在于 Content-Type 设置,而非 Reader 类型。
掌握这两种方式后,你就能可靠地与绝大多数基于表单的 REST API(如 PHP 后端、传统 Web 框架)完成交互。










