
Go 中使用 http.NewRequest("GET", url, body) 时,若将查询参数写入请求体(body),服务端将无法读取——因为 GET 请求语义上不携带请求体,参数必须拼接在 URL 查询字符串中。
go 中使用 http.newrequest("get", url, body) 时,若将查询参数写入请求体(body),服务端将无法读取——因为 get 请求语义上不携带请求体,参数必须拼接在 url 查询字符串中。
在 Go 的 HTTP 客户端中,http.NewRequest(method, url, body) 的第三个参数 body 仅用于传输请求载荷(payload),它会被写入 HTTP 报文的消息体(message body)部分,并自动设置 Content-Length 头。但根据 HTTP/1.1 规范及绝大多数 RESTful API(包括 Poloniex)的设计约定:GET 请求不应包含请求体,服务端通常会忽略 body 内容,仅从 URL 的查询字符串(query string)中解析参数。
你原始代码的问题正在于此:
values := url.Values{"currencyPair": []string{pair}, "depth": []string{fmt.Sprintf("%d", depth)}}
req, err := http.NewRequest("GET", reqURL, strings.NewReader(values.Encode()))
虽然 values.Encode() 正确生成了 "currencyPair=BTC_ETH&depth=20",但这段数据被作为请求体发送,而非附加到 URL 上。因此目标 API(https://poloniex.com/public?command=returnOrderBook)根本收不到 currencyPair 参数,自然返回 {"error":"Please specify a currency pair."}。
✅ 正确做法是:将所有查询参数统一构造成 url.Values,再通过 url.URL 结构安全拼接到 URL 的 RawQuery 字段中,最后用 nil 作为 body 参数调用 http.NewRequest:
import (
"net/http"
"net/url"
"fmt"
)
pair := "BTC_ETH"
depth := 20
// 构建基础 URL(不含查询参数)
baseURL := "https://poloniex.com/public"
u, err := url.Parse(baseURL)
if err != nil {
panic(err)
}
// 构建完整查询参数(含原有 command)
q := u.Query()
q.Set("command", "returnOrderBook")
q.Set("currencyPair", pair)
q.Set("depth", fmt.Sprintf("%d", depth))
u.RawQuery = q.Encode()
// ✅ 此时 u.String() 即为完整 URL:https://poloniex.com/public?command=returnOrderBook¤cyPair=BTC_ETH&depth=20
req, err := http.NewRequest("GET", u.String(), nil) // body 必须为 nil
if err != nil {
panic(err)
}
// 可选:设置自定义 Header(如 User-Agent)
req.Header.Set("User-Agent", "Poloniex GO API Agent")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Printf("Response: %s\n", string(body))
⚠️ 注意事项:
-
url.Values中的值必须是[]string类型;整数需显式转换为字符串(如fmt.Sprintf("%d", depth)),避免类型错误; - 不要手动字符串拼接(如
reqURL + "&" + values.Encode()),易出错且不处理 URL 编码(例如空格、特殊符号); -
url.Parse()+u.Query()+u.RawQuery是 Go 标准库推荐的安全方式,自动完成 URL 编码(如将BTC/ETH编码为BTC%2FETH); -
http.NewRequest("GET", ..., nil)中body为nil表示无请求体,此时ContentLength自动为 0,符合 HTTP 语义。
总结:GET 请求的参数永远属于 URL,而非请求体。理解 http.NewRequest 各参数的实际作用(尤其是 body 的网络层含义),是避免此类“看似逻辑正确却调用失败”问题的关键。










