¿Cómo obtener el cuerpo del contenido de una llamada httpclient?

108

He estado tratando de averiguar cómo leer el contenido de una llamada httpclient, y parece que no puedo entenderlo. El estado de respuesta que obtengo es 200, pero no puedo averiguar cómo llegar al Json real que se devuelve, ¡que es todo lo que necesito!

El siguiente es mi código:

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

Y lo obtengo simplemente llamándolo desde un método:

Task<string> result =  GetResponseString(text);

Y esto es lo que obtengo

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>

Actualización: este es mi código actual según la respuesta de Nathan a continuación

    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;
    }

Y lo llamo de este método ...

 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);
        }
    }

Y sigue funcionando para siempre, llega a la línea una var model = JsonConvert.DeserializeObject<SentimentJsonModel>(result.Result); vez y continúa sin detenerse en otro punto de interrupción.

Cuando pause la ejecución, dice

Id = No se puede evaluar la expresión porque el código del método actual está optimizado., Estado = No se puede evaluar la expresión porque el código del método actual está optimizado., Método = No se puede evaluar la expresión porque el código del método actual está optimizado., Resultado = No se puede evaluar la expresión porque el código del método actual está optimizado.

.. Continúo con la ejecución, pero se ejecuta para siempre. No estoy seguro de cual es el problema

Sherman Szeto
fuente
¿Dónde y cómo se define _numRetries?
Nathan A
Está en el alcance de la clase y se inicializa con un 0 en el constructor. AnalyzeSingle () es el único lugar donde lo uso.
Sherman Szeto
¿Está ejecutando en modo de depuración? El problema optimizado puede deberse a que está ejecutando en modo de lanzamiento.
Nathan A
Actualmente estoy en Debug / iisExpress
Sherman Szeto

Respuestas:

176

La forma en que está utilizando await / async es pobre en el mejor de los casos y dificulta su seguimiento. Usted está mezclando awaitcon Task'1.Result, que recién está confuso. Sin embargo, parece que está viendo el resultado final de una tarea, en lugar de los contenidos.

He reescrito su función y llamada a función, lo que debería solucionar su problema:

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;
}

Y su llamada de función final:

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

O mejor:

var finalResult = await GetResponseString(text);
Nathan A
fuente
¡¡Gracias!! He estado tratando de entender cómo funciona async / await durante las últimas dos horas (MSDN + stackoverflow), pero obviamente aún no lo he comprendido completamente. ¿Hay algún recurso que sugiera?
Sherman Szeto
1
Sigue jugando con él y eventualmente lo dominarás. Es un concepto amplio para comprender de una vez.
Nathan A
Todavía tengo problemas. Actualicé mi problema en la publicación original. El problema puede ser que estoy codificando la ejecución sincrónica, pero no estoy seguro de cómo resolver ese problema
Sherman Szeto
1
HttpClient implementa IDisposable, por lo que es mejor envolverlo en una declaración de uso.
Payam
2
@Payam si bien es cierto que implementa, IDisposableno debe envolverlo en una usingdeclaración. Es una rara excepción a la regla. Consulte esta publicación para obtener más información: aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
maxshuty
62

Si no desea usar async, puede agregar .Resultpara forzar que el código se ejecute sincrónicamente:

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
fuente
2
@nbushnell agregar .Result a su PostAsync hace que no sea asincrónico
Mike
6
@ Mike, ¿no es eso lo que dice nbushnell? :-)
PoeHaH
¿Para qué es el tipo response? Tengo un código similar pero necesito hacerlo responseglobal, así que necesito el tipo. Gracias.
Azurespot
1
@AzureSpot: El tipo de respuesta es HttpResponseMessage.
RWC