我正在尝试制作一个调用twitter搜索API的函数(使用dghubble/go-twitter)
但是,当我尝试从该API读取响应时,我得到了http2: response body closed
这是我的代码:
func SearchTweets(query string) string {
config := oauth1.NewConfig("consumer_key", "consumer_secret")
token := oauth1.NewToken("access_token", "token_secret")
httpClient := config.Client(oauth1.NoContext, token)
// Twitter client
client := twitter.NewClient(httpClient)
// Search Tweets
_ , resp, err := client.Search.Tweets(&twitter.SearchTweetParams{
Query: "elon",
Count: 50,
})
if err != nil {
return err.Error()
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err.Error()
}
return string(body)
}
最佳答案
此答复中引用的行号和文件是在回答之日记录的,因此不能保证它们永远正确无误。
在dghubble's go-twitter search.go的第62行中,您正在调用的client.Search.Tweets()
函数:
resp, err := s.sling.New().Get("tweets.json").QueryStruct(params).Receive(search, apiError)
请注意,它使用sling
包构造和发送http请求,以及接收和解析http(JSON)响应。它所引用的sling
包可以在here中找到。具体来说,请密切注意sling.go。在第366行上,有一条注释指出:“接收是调用请求和执行的简写。”
在
Request()
行上找到的280
函数仅根据*http.Request
对象的属性构造一个Sling
。它返回所述请求以及错误(签名为func (s *Sling) Request() (*http.Request, error)
)Do()
函数是关键部分。它执行作为第一个参数传递的http请求,尝试将(JSON)响应解析为提供的第二个参数(一个interface {},它应指向您希望将响应解析为的对象),并同时尝试解析提供给第三个参数的任何错误。毕竟,它关闭了响应主体。 具体来说,注意第385和386行:// when err is nil, resp contains a non-nil resp.Body which must be closed
defer resp.Body.Close()
换句话说,响应主体在它进入go-twitter Tweets()
范围之前就已关闭,因此您肯定无法最终读取该主体。毕竟,这些库的要点是为您省去了读取和解析响应正文等内容的麻烦。通常,您应该能够使用提供的软件包获得所需的信息。具体来说,
Tweets()
函数具有以下签名:func (s *SearchService) Tweets(params *SearchTweetParams) (*Search, *http.Response, error)
。请注意,第一个返回值的类型为*Search
。这是从Receive()
以及Do()
函数读取的对象。它应该在关闭之前保存*http.Response
主体中的所有信息:search , resp, err := client.Search.Tweets(&twitter.SearchTweetParams{
Query: "elon",
Count: 50,
})
search
对象的类型为*Search
,其定义可以在here中找到。它包括状态的一部分(Tweet
结构)以及一些元数据。您还可能会发现here,这对Tweet
结构的定义很有帮助。最后,如果出于某种原因您确实需要自己处理请求主体,则必须从头开始执行请求,这无论如何都会破坏
go-twitter
和sling
库的目的。您可以在sling
的基础上构建一些请求,但它不是很漂亮(实际上,从头开始构建请求也可能更干净)。不过,这是一个(未经测试的)示例,它是通过引用本文前面提到的链接以及this one编写的代码:func MyTweetsFunc(httpClient *http.Client, requestBuilder *sling.Sling, params *twitter.SearchTweetParams)
{
// Use sling's Request() to construct the request, just as Receive() does
req, err := requestBuilder.New().Get("tweets.json").QueryStruct(params).Request()
if err != nil {
// handle the error
}
// Get the response through the httpClient, since a sling client would parse and close the body automatically.
resp, err := httpClient.Do(req)
if err != nil {
// handle the error
}
// Now you can do whatever you want with resp, which is simply an *http.Response. Its body is open for reading.
}
requestBuilder
可以这样构造:const twitterAPI = "https://api.twitter.com/1.1/"
requestBuilder := sling.New().Client(httpClient).Base(twitterAPI)
您通常将httpClient
传递给twitter.NewClient()
的客户端
关于api - 从Twitter客户端读取响应返回错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63267744/