如何在 Go 中正确发送带表单数据的 POST 请求

12次阅读

如何在 Go 中正确发送带表单数据的 POST 请求

本文详解 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("请求失败: %vn", err)         return     }     defer resp.Body.Close()      body, err := ioutil.ReadAll(resp.Body)     if err != nil {         fmt.Printf("读取响应失败: %vn", 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("创建请求失败: %vn", 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("发送请求失败: %vn", err)         return     }     defer resp.Body.Close()      body, err := ioutil.ReadAll(resp.Body)     if err != nil {         fmt.Printf("读取响应失败: %vn", 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 框架)完成交互。

text=ZqhQzanResources