Чтение ответа от клиента Twitter возвращает ошибку - PullRequest
0 голосов
/ 05 августа 2020

Я пытаюсь создать функцию, которая вызывает API-интерфейс поиска в Twitter (используя 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)
}

1 Ответ

1 голос
/ 05 августа 2020

Номера строк и файлы, упомянутые в этом ответе, были записаны на дату ответа и не гарантируют вечной правильности.

Из 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, к которому он относится, можно найти здесь .

В частности, обратите внимание на строп. go. В строке 366 комментарий гласит: «Receive - это сокращение для вызова Request и Do».

Функция Request(), найденная в строке 280, просто создает *http.Request из объекта Sling свойства. Он возвращает указанный запрос, а также ошибку (подпись func (s *Sling) Request() (*http.Request, error))

Функция Do() является критической частью. Он выполняет HTTP-запрос, переданный в качестве первого аргумента, пытается проанализировать ответ (JSON) во втором предоставленном аргументе (интерфейс {}, который должен указывать на объект, на который должен быть проанализирован ответ), и одновременно пытается для анализа ошибок в третьем предоставленном аргументе. После всего этого он закрывает тело ответа. В частности, обратите внимание на строки 385 и 386:

// when err is nil, resp contains a non-nil resp.Body which must be closed
defer resp.Body.Close()

Другими словами, тело ответа закрывается еще до того, как оно дойдет до go -twitter Tweets() scope, так что вы точно не сможете прочитать тело на своей стороне. В конце концов, одна из задач этих библиотек - избавить вас от хлопот, связанных с , имеющим для чтения и анализа тел ответов и т.п.

В общем, вы должны иметь возможность получать информацию вам нужно использовать предоставленные пакеты. В частности, функция 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, определение которого можно найти здесь . Он включает в себя набор статусов (Tweet структур), а также некоторые метаданные. Вы также можете найти полезным определение структуры Tweet, найденное здесь .

Наконец, если по какой-то причине вам действительно нужно иметь возможность самостоятельно обрабатывать тело запроса, вы приходится выполнять запрос с нуля, что в любом случае в большинстве случаев сводит на нет цели библиотек go-twitter и sling. Вы можете в некоторой степени использовать sling для создания запроса, но это некрасиво (и на самом деле, вероятно, было бы проще создать запрос с нуля). Тем не менее, вот (непроверенный) пример кода, написанного со ссылкой на ссылки, ранее упомянутые в этом сообщении, а также этот :

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()

...