Jak pobrać treść z wywołania httpclient?

110

Próbowałem wymyślić, jak odczytać zawartość wywołania httpclient, ale nie mogę tego uzyskać. Status odpowiedzi, który otrzymuję, to 200, ale nie mogę dowiedzieć się, jak dostać się do rzeczywistego zwracanego pliku Json, czyli wszystkiego, czego potrzebuję!

Oto mój kod:

async Task<string> GetResponseString(string text)
{
    var httpClient = new HttpClient();

    var parameters = new Dictionary<string, string>();
    parameters["text"] = text;
    Task<HttpResponseMessage> response = 
        httpClient.PostAsync(BaseUri, new FormUrlEncodedContent(parameters));

    return await response.Result.Content.ReadAsStringAsync();
}

Otrzymuję to po prostu wywołując to z metody:

Task<string> result =  GetResponseString(text);

I to właśnie dostaję

response Id = 89, Status = RanToCompletion, Method = "{null}", Result = "StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:\r\n{\r\n Connection: keep-alive\r\n Date: Mon, 27 Oct 2014 21:56:43 GMT\r\n ETag: \"5a266b16b9dccea99d3e76bf8c1253e0\"\r\n Server: nginx/0.7.65\r\n Content-Length: 125\r\n Content-Type: application/json\r\n}" System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>

Aktualizacja: To jest mój aktualny kod na odpowiedź Nathana poniżej

    async Task<string> GetResponseString(string text)
    {
        var httpClient = new HttpClient();

        var parameters = new Dictionary<string, string>();
        parameters["text"] = text;

        var response = await httpClient.PostAsync(BaseUri, new FormUrlEncodedContent(parameters));
        var contents = await response.Content.ReadAsStringAsync();

        return contents;
    }

I nazywam to tą metodą ....

 string AnalyzeSingle(string text)
    {
        try
        {
            Task<string> result = GetResponseString(text);
            var model = JsonConvert.DeserializeObject<SentimentJsonModel>(result.Result);

            if (Convert.ToInt16(model.pos) == 1)
            {
                _numRetries = 0;
                return "positive";
            }

            if (Convert.ToInt16(model.neg) == 1)
            {
                _numRetries = 0;
                return "negative";
            }

            if (Convert.ToInt16(model.mid) == 1)
            {
                _numRetries = 0;
                return "neutral";
            }

            return "";
        }
        catch (Exception e)
        {
            if (_numRetries > 3)
            {
                LogThis(string.Format("Exception caught [{0}] .... skipping", e.Message));
                _numRetries = 0;
                return "";
            }
            _numRetries++;
            return AnalyzeSingle(text);
        }
    }

I działa wiecznie, var model = JsonConvert.DeserializeObject<SentimentJsonModel>(result.Result); Raz trafia w linię i dalej jedzie bez zatrzymywania się na innym punkcie przerwania.

Kiedy wstrzymuję wykonanie, mówi

Id = Nie można ocenić wyrażenia, ponieważ kod bieżącej metody jest zoptymalizowany., Status = Nie można ocenić wyrażenia, ponieważ kod bieżącej metody jest zoptymalizowany., Metoda = Nie można ocenić wyrażenia, ponieważ kod bieżącej metody jest zoptymalizowany., Wynik = Nie można ocenić wyrażenia, ponieważ kod bieżącej metody jest zoptymalizowany.

.. Kontynuuj wykonywanie, ale to po prostu trwa wiecznie. Nie jestem pewna na czym polega problem

Sherman Szeto
źródło
Gdzie i jak definiuje się _numRetries?
Nathan A,
Znajduje się w zakresie klasy i jest inicjowany wartością 0 w konstruktorze. AnalyzeSingle () to jedyne miejsce, w którym go używam.
Sherman Szeto
Czy pracujesz w trybie debugowania? Zoptymalizowany problem może być spowodowany pracą w trybie wydania.
Nathan A
Jestem obecnie na Debug / iisExpress
Sherman Szeto

Odpowiedzi:

177

Sposób, w jaki używasz await / async, jest w najlepszym razie słaby i utrudnia śledzenie. Mieszasz się awaitz Task'1.Result, co jest po prostu mylące. Wygląda jednak na to, że patrzysz na końcowy wynik zadania, a nie na zawartość.

Przepisałem twoją funkcję i wywołanie funkcji, co powinno rozwiązać twój problem:

async Task<string> GetResponseString(string text)
{
    var httpClient = new HttpClient();

    var parameters = new Dictionary<string, string>();
    parameters["text"] = text;

    var response = await httpClient.PostAsync(BaseUri, new FormUrlEncodedContent(parameters));
    var contents = await response.Content.ReadAsStringAsync();

    return contents;
}

I ostatnie wywołanie funkcji:

Task<string> result = GetResponseString(text);
var finalResult = result.Result;

Albo jeszcze lepiej:

var finalResult = await GetResponseString(text);
Nathan A.
źródło
Dzięki!! Próbowałem uzyskać, jak działa async / await przez ostatnie kilka godzin (MSDN + stackoverflow), ale najwyraźniej jeszcze tego nie zrozumiałem. Czy są jakieś zasoby, które byś zasugerował?
Sherman Szeto
1
Po prostu baw się z tym, a w końcu to zrozumiesz. Jest to wielka koncepcja, którą można pojąć od razu.
Nathan A
Nadal mam problemy. Zaktualizowałem mój problem w oryginalnym poście. Problem może polegać na tym, że koduję do wykonania synchronicznego, ale nie jestem pewien, jak rozwiązać ten problem
Sherman Szeto
1
HttpClient implementuje IDisposable, więc lepiej jest opakować go w instrukcję using.
Payam
2
@Payam, chociaż prawdą jest, że implementuje IDisposable, nie powinieneś zawijać go w usingoświadczeniu. To rzadki wyjątek od reguły. Zobacz ten post, aby uzyskać więcej informacji: aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
maxshuty
63

Jeśli nie chcesz używać async, możesz dodać, .Resultaby wymusić synchroniczne wykonywanie kodu:

private string GetResponseString(string text)
{
    var httpClient = new HttpClient();

    var parameters = new Dictionary<string, string>();
    parameters["text"] = text;

    var response = httpClient.PostAsync(BaseUri, new FormUrlEncodedContent(parameters)).Result;
    var contents = response.Content.ReadAsStringAsync().Result;

    return contents;
 }  
nbushnell
źródło
2
@nbushnell dodając .Result do PostAsync sprawia, że ​​nie jest on asynchroniczny
Mike
6
@Mike, czy to nie jest to, co mówi nbushnell? :-)
PoeHaH
Do czego służy ten typ response? Mam podobny kod, ale muszę uczynić go responseglobalnym, więc potrzebuję typu. Dzięki.
Azurespot
1
@AzureSpot: Typ odpowiedzi De to HttpResponseMessage.
RWC