读取来自 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,
})

searchobject的类型是*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()