读取来自 Twitter 客户端的响应 returns 出错
Reading response from Twitter client returns an error
我正在尝试创建一个调用 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) 响应。可以在 here.
中找到它引用的 sling
包
具体请关注sling.go。在第 366 行,评论指出,“Receive is shorthand for calling Request and Do。”
Request()
函数位于 280
行,它只是根据 Sling
object 的属性构造一个 *http.Request
。它returns表示请求,还有一个错误(签名是func (s *Sling) Request() (*http.Request, error)
)
Do()
函数是关键部分。它执行作为第一个参数传递的 http 请求,尝试将 (JSON) 响应解析为提供的第二个参数(一个接口{},它应该指向您希望解析响应的 object into),并同时尝试将任何错误解析为提供的第三个参数。所有这些之后,它 关闭响应 body。 具体来说,请注意第 385 和 386 行:
// when err is nil, resp contains a non-nil resp.Body which must be closed
defer resp.Body.Close()
换句话说,响应 body 在到达 go-twitter Tweets()
范围之前就已经关闭了,所以您肯定无法阅读body 在你这边。毕竟,这些库的一个目的是让您免于读取和解析响应主体等的麻烦。
一般来说,您应该能够使用提供的软件包获得所需的信息。具体来说,Tweets()
函数具有以下签名:func (s *SearchService) Tweets(params *SearchTweetParams) (*Search, *http.Response, error)
。请注意,第一个 return 值的类型为 *Search
。这是从 Receive()
读取的 object,然后是 Do()
函数。它应该包含 *http.Response
的 body 关闭前的所有信息:
search , resp, err := client.Search.Tweets(&twitter.SearchTweetParams{
Query: "elon",
Count: 50,
})
search
object的类型是*Search
,其定义见here. It includes a slice of statuses (Tweet
structures) as well as some metadata. You may also find the definition of a Tweet
structure helpful, found here.
最后,如果出于某种原因您确实需要能够自己处理请求 body,您将不得不从头开始执行请求,这在很大程度上违背了 go-twitter
的目的和 sling
图书馆无论如何。您可以在某种程度上构建 sling
来构建请求,但它并不漂亮(事实上,从头开始构建请求可能会更清晰)。尽管如此,这里有一个(未经测试的)示例,代码是通过引用此 post 中先前提到的链接以及 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()
我正在尝试创建一个调用 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) 响应。可以在 here.
sling
包
具体请关注sling.go。在第 366 行,评论指出,“Receive is shorthand for calling Request and Do。”
Request()
函数位于 280
行,它只是根据 Sling
object 的属性构造一个 *http.Request
。它returns表示请求,还有一个错误(签名是func (s *Sling) Request() (*http.Request, error)
)
Do()
函数是关键部分。它执行作为第一个参数传递的 http 请求,尝试将 (JSON) 响应解析为提供的第二个参数(一个接口{},它应该指向您希望解析响应的 object into),并同时尝试将任何错误解析为提供的第三个参数。所有这些之后,它 关闭响应 body。 具体来说,请注意第 385 和 386 行:
// when err is nil, resp contains a non-nil resp.Body which must be closed
defer resp.Body.Close()
换句话说,响应 body 在到达 go-twitter Tweets()
范围之前就已经关闭了,所以您肯定无法阅读body 在你这边。毕竟,这些库的一个目的是让您免于读取和解析响应主体等的麻烦。
一般来说,您应该能够使用提供的软件包获得所需的信息。具体来说,Tweets()
函数具有以下签名:func (s *SearchService) Tweets(params *SearchTweetParams) (*Search, *http.Response, error)
。请注意,第一个 return 值的类型为 *Search
。这是从 Receive()
读取的 object,然后是 Do()
函数。它应该包含 *http.Response
的 body 关闭前的所有信息:
search , resp, err := client.Search.Tweets(&twitter.SearchTweetParams{
Query: "elon",
Count: 50,
})
search
object的类型是*Search
,其定义见here. It includes a slice of statuses (Tweet
structures) as well as some metadata. You may also find the definition of a Tweet
structure helpful, found here.
最后,如果出于某种原因您确实需要能够自己处理请求 body,您将不得不从头开始执行请求,这在很大程度上违背了 go-twitter
的目的和 sling
图书馆无论如何。您可以在某种程度上构建 sling
来构建请求,但它并不漂亮(事实上,从头开始构建请求可能会更清晰)。尽管如此,这里有一个(未经测试的)示例,代码是通过引用此 post 中先前提到的链接以及 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()