Golang中HTTP错误代码400的原因和解决方法

Golang中HTTP错误代码400的原因和解决方法 我过去曾成功发出过许多请求,但通常是发给我自己的网络服务器。这次我被卡住了。这个请求在 Postman 中工作正常,但在我的 Go 代码中,服务器返回了 400 错误码。我希望响应能给我一些线索,告诉我需要修改什么。

在 Postman 中,我将主机设置为: https://finnhub.io/api/v1

然后在 URL 行添加了: /stock/candle,并将其设置为 ‘get’。我设置了参数,URL 随后显示为: {{host}}/stock/candle?symbol=msft&resolution=D&from=1649887189&to=1658527189

授权信息在集合中设置为: 键:X-Finnhub-Token 值:c362xxxxxxxxxuojg (已为安全考虑修改) 我点击发送,它按请求执行。我得到了一系列股票蜡烛图数据。很好。

然而,当我在 Go 中尝试这样做时,我得到了 400 错误。

urlString := fmt.Sprintf("https://finnhub.io/api/v1" + "/stock/candle")

req, err := http.NewRequest("get", urlString, nil)
values := req.URL.Query()
values.Add("symbol", specification.Stock.Symbol)
if specification.TriggerCriteria.FrequencyUnit == data.UnitDay {
	values.Add("resolution", "D")
}
now := time.Now()
nowString := fmt.Sprintf("%v", now.Unix())
oneHundredDaysAgo := time.Hour * 24 * 100 * -1
past := now.Add(oneHundredDaysAgo)
pastString := fmt.Sprintf("%v", past.Unix())
values.Add("from", pastString)
values.Add("to", nowString)
req.URL.RawQuery = values.Encode()

req.Header.Add("X-Finnhub-Token", "c362jxxxxxxx0uojg")

resp, err := http.DefaultClient.Do(req)`

响应状态码为 400。

请求截图: image


更多关于Golang中HTTP错误代码400的原因和解决方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

12 回复

响应体为空。

更多关于Golang中HTTP错误代码400的原因和解决方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我正在使用 Goland 作为我的 IDE。我可以查看任何变量,但我找不到任何问题。

你检查过响应体了吗?它显示什么?

我希望能从响应中得到一些线索,告诉我需要修改什么。

是否有可以查看的“控制台”(网络浏览器)?它是否抱怨CORS或类似的问题?

我怀疑你是对的。我可能提供了一个无效的 to 参数,因为我当时正在学习那个 API。

仅凭400响应很难判断问题所在。 能否打印出resp.Body的内容,这可能会给你更多线索。 例如:

io.Copy(os.Stdout, res.Body)

也许可以贴出一些完整的可运行代码,这样我们就能看到发生了什么……

Sibert:

httputil.DumpResponse

我刚刚再次运行了有问题的代码来检查转储的响应。这次它成功了,状态码是200。而且我并没有修改代码。这相当奇怪,但我需要继续推进。我会使用SDK,因为它每次都能正常工作。 感谢您的回复。

我尚未查明为何会收到错误代码 400。不过,我已经下载了 Finnhub 的 Go SDK,它可以正常获取数据。这是一个非常复杂的项目,访问层级很高,我还没找到他们具体在哪里执行请求。我想我会使用这个 SDK。但我仍然想知道,为什么我能在 Postman 中使用这个请求,却无法在 Go 中作为一个简单的请求使用。

我还没弄明白为什么会出现错误代码400。

服务器的响应是什么?类似 dump, err := httputil.DumpResponse 这样的吗?

你说响应是空的,但如果你能从服务器获得响应,可能会找到线索。服务器必须以某种方式响应。我曾经用AJAX玩过一些API,遇到过不少CORS错误。

有趣。 除了以下几行代码外,程序每次都会发送相同的请求:

fmt.Sprintf("%v", now.Unix())

这几行代码每次运行都会变化。(from 参数也是如此。) 会不会是服务器对某些值有异议? 例如,服务器会拒绝未来的 to 值吗? 这会不会是客户端和服务器之间的时钟偏差问题? 如果在请求中省略 to= 参数会怎么样?

我逐步查看了他们的代码,他们的请求看起来和我的完全一样。

SDK:
GET /api/v1/stock/candle?from=1649970549&resolution=D&symbol=AAPL&to=1658610549 HTTP/1.1
Host: finnhub.io
User-Agent: OpenAPI-Generator/2.0.13/go
Accept: application/json
X-Finnhub-Token: cbbb00iad3ibhoa1vbcg
Accept-Encoding: gzip


Mine:
GET /api/v1/stock/candle?from=1649970549&resolution=D&symbol=AAPL&to=1658610549 HTTP/1.1
Host: finnhub.io
User-Agent: OpenAPI-Generator/2.0.13/go
Accept: application/json
X-Finnhub-Token: cbbb00iad3ibhoa1vbcg
Accept-Encoding: gzip

我是使用以下方法创建这些请求的:

httputil.DumpRequestOut

问题出在查询参数的编码上。你在创建请求后修改了req.URL.Query(),但http.NewRequest已经解析了URL,而后续对req.URL.Query()的修改没有正确应用到请求上。正确的做法是在创建请求前构建完整的URL,或者使用url.Values直接设置RawQuery

以下是修正后的代码:

package main

import (
    "fmt"
    "net/http"
    "net/url"
    "time"
)

func main() {
    baseURL := "https://finnhub.io/api/v1/stock/candle"
    
    // 创建查询参数
    values := url.Values{}
    values.Add("symbol", "msft")
    values.Add("resolution", "D")
    
    now := time.Now()
    oneHundredDaysAgo := now.Add(-100 * 24 * time.Hour)
    values.Add("from", fmt.Sprintf("%d", oneHundredDaysAgo.Unix()))
    values.Add("to", fmt.Sprintf("%d", now.Unix()))
    
    // 构建完整URL
    fullURL := fmt.Sprintf("%s?%s", baseURL, values.Encode())
    
    req, err := http.NewRequest("GET", fullURL, nil)
    if err != nil {
        panic(err)
    }
    
    req.Header.Add("X-Finnhub-Token", "c362xxxxxxxxxuojg")
    
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    
    fmt.Printf("Status Code: %d\n", resp.StatusCode)
}

或者使用req.URL.RawQuery的替代方案:

package main

import (
    "fmt"
    "net/http"
    "net/url"
    "time"
)

func main() {
    baseURL := "https://finnhub.io/api/v1/stock/candle"
    
    req, err := http.NewRequest("GET", baseURL, nil)
    if err != nil {
        panic(err)
    }
    
    // 直接设置RawQuery
    values := url.Values{}
    values.Add("symbol", "msft")
    values.Add("resolution", "D")
    
    now := time.Now()
    oneHundredDaysAgo := now.Add(-100 * 24 * time.Hour)
    values.Add("from", fmt.Sprintf("%d", oneHundredDaysAgo.Unix()))
    values.Add("to", fmt.Sprintf("%d", now.Unix()))
    
    req.URL.RawQuery = values.Encode()
    req.Header.Add("X-Finnhub-Token", "c362xxxxxxxxxuojg")
    
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    
    fmt.Printf("Status Code: %d\n", resp.StatusCode)
}

关键点:

  1. 使用url.Values{}而不是req.URL.Query()来构建查询参数
  2. 确保在创建请求前或通过RawQuery设置完整的查询字符串
  3. HTTP方法应该使用大写的"GET"

这两种方法都能确保查询参数正确编码并包含在请求中,从而避免400错误。

回到顶部