¿Por qué usar HttpClient para conexión síncrona?

189

Estoy construyendo una biblioteca de clases para interactuar con una API. Necesito llamar a la API y procesar la respuesta XML. Puedo ver los beneficios del uso HttpClientpara la conectividad asincrónica, pero lo que estoy haciendo es puramente sincrónico, por lo que no puedo ver ningún beneficio significativo sobre el uso HttpWebRequest.

Si alguien puede arrojar alguna luz, lo agradecería enormemente. No soy de los que usan las nuevas tecnologías por el bien de ellas.

Salsa de tomate
fuente
3
Odio decírtelo, pero una llamada a través de HTTP nunca es puramente sincrónica debido a cómo funciona internamente la red de Windows (también conocida como puertos de finalización).
TomTom

Respuestas:

374

pero lo que estoy haciendo es puramente sincrónico

Puede usar HttpClientpara solicitudes síncronas muy bien:

using (var client = new HttpClient())
{
    var response = client.GetAsync("http://google.com").Result;

    if (response.IsSuccessStatusCode)
    {
        var responseContent = response.Content; 

        // by calling .Result you are synchronously reading the result
        string responseString = responseContent.ReadAsStringAsync().Result;

        Console.WriteLine(responseString);
    }
}

En cuanto a por qué debería usar HttpClientover, WebRequestse trata, bueno, HttpClientes el nuevo chico en el bloque y podría contener mejoras sobre el antiguo cliente.

Darin Dimitrov
fuente
27
¿Su uso sincrónico de los métodos asíncronos no bloquearía potencialmente su hilo de interfaz de usuario? Es posible que desee considerar algo así string responseString = Task.Run(() => responseContent.ReadAsStringAsync()).Result;si debe hacer esto sincrónico.
terrícola
13
@earthling, sí, Task.Runinvoca la tarea desde un ThreadPool, pero está solicitando .Resultque elimine todos los beneficios de esto y bloquee el hilo en el que lo llamó .Result(que generalmente es el hilo principal de la interfaz de usuario).
Darin Dimitrov
35
De acuerdo con esta publicación ( blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx ) llamar .Resultasí puede agotar el conjunto de hilos y causar un punto muerto.
Pete Garafano
16
Este código siempre se bloqueará si se ejecuta dentro de una tarea creada en el hilo principal de la interfaz de usuario por unnew TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()
Wim Coenen
24
Entonces, ¿cómo uso HttpClient sincrónicamente desde el hilo de la interfaz de usuario? Digamos que a propósito quiero bloquear el hilo de la interfaz de usuario (o estoy escribiendo una aplicación de consola) hasta que obtenga la respuesta HTTP ... Entonces, si Wait (), Result (), etc. puede causar puntos muertos, ¿cuál es la solución definitiva para esto sin El riesgo de punto muerto y sin utilizar otras clases como WebClient?
Dexter
26

Reiteraría la respuesta de Donny V. y la de Josh

"La única razón por la que no usaría la versión asincrónica es si intentara admitir una versión anterior de .NET que aún no tiene una compatibilidad asincrónica".

(Y votar si tuviera la reputación).

No recuerdo la última vez si alguna vez, agradecí el hecho de que HttpWebRequest arrojó excepciones para los códigos de estado> = 400. Para solucionar estos problemas, debe detectar las excepciones de inmediato y asignarlas a algunos mecanismos de respuesta que no sean excepciones. en su código ... aburrido, tedioso y propenso a errores en sí mismo. Ya sea que se comunique con una base de datos o que implemente un proxy web a medida, es 'casi' siempre deseable que el controlador Http solo le diga a su código de aplicación lo que se devolvió y le deje a usted decidir cómo comportarse.

Por lo tanto, HttpClient es preferible.

trev
fuente
1
Me sorprendió que en HttpClientsí mismo sea una envoltura HttpWebRequest(que de hecho, atrapa internamente esos WebExceptionobjetos y hace la conversión a a HttpResponseMessagepor ti). Pensé que sería más fácil crear un nuevo cliente desde cero.
Dai
44
Hay muchas buenas razones como no querer reescribir toda su base de código solo para una llamada http de muy bajo nivel que ni siquiera es crítica para el rendimiento (pero introduciría asíncrono a un millón de lugares).
FrankyBoy
En .net core 2 si desea evaluar dinámicamente una expresión con DynamicExpressionParser, es posible que no sea posible usar async; los indexadores de propiedades no pueden usar asíncrono; en mi situación, necesito evaluar dinámicamente una cadena como "GetDefaultWelcomeMessage [\" InitialMessage \ "]" donde este método crea una HttpCall y la sintaxis del índice es preferible a la sintaxis del método "Util.GetDefaultWelcomeMessage (\" InitialMessage \ ")"
eugen
7

Si está creando una biblioteca de clase, entonces quizás los usuarios de su biblioteca deseen usar su biblioteca de forma asincrónica. Creo que esa es la razón más importante allí mismo.

Tampoco sabes cómo se usará tu biblioteca. Quizás los usuarios procesarán muchas y muchas solicitudes, y hacerlo de forma asincrónica lo ayudará a funcionar de manera más rápida y eficiente.

Si puede hacerlo simplemente, trate de no cargar con la carga a los usuarios de su biblioteca que intentan hacer que el flujo sea asíncrono cuando pueda encargarse de ellos.

La única razón por la que no usaría la versión asincrónica es si intentara admitir una versión anterior de .NET que aún no tiene soporte asincrónico incorporado.

Josh Smeaton
fuente
Ya veo, así que haga que la biblioteca de clases sea asíncrona y permita que los usuarios del sistema decidan si la usarán de forma asíncrona o si usarán esperar y usar sincrónicamente.
Ketchup
erm, wait ayuda a hacer ciertas llamadas asincrónicas devolviendo el control a la persona que llama.
Josh Smeaton
6

En mi caso, la respuesta aceptada no funcionó. Estaba llamando a la API desde una aplicación MVC que no tenía acciones asíncronas.

Así es como logré hacerlo funcionar:

private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static T RunSync<T>(Func<Task<T>> func)
    {           
        CultureInfo cultureUi = CultureInfo.CurrentUICulture;
        CultureInfo culture = CultureInfo.CurrentCulture;
        return _myTaskFactory.StartNew<Task<T>>(delegate
        {
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = cultureUi;
            return func();
        }).Unwrap<T>().GetAwaiter().GetResult();
    }

Entonces lo llamé así:

Helper.RunSync(new Func<Task<ReturnTypeGoesHere>>(async () => await AsyncCallGoesHere(myparameter)));
Jonathan Alfaro
fuente
1
Thx @Darkonekt ... Esto funciona perfectamente para mí. Solo el HttpClient.SendAsync (...). El resultado nunca funciona dentro del AspNet Handler (.ASHX).
Rafael Kazuo Sato Simiao
3
public static class AsyncHelper  
{
    private static readonly TaskFactory _taskFactory = new
        TaskFactory(CancellationToken.None,
                    TaskCreationOptions.None,
                    TaskContinuationOptions.None,
                    TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
        => _taskFactory
            .StartNew(func)
            .Unwrap()
            .GetAwaiter()
            .GetResult();

    public static void RunSync(Func<Task> func)
        => _taskFactory
            .StartNew(func)
            .Unwrap()
            .GetAwaiter()
            .GetResult();
}

Luego

AsyncHelper.RunSync(() => DoAsyncStuff());

si usa esa clase, pase su método asíncrono como parámetro, puede llamar a los métodos asíncronos desde los métodos de sincronización de manera segura.

se explica aquí: https://cpratt.co/async-tips-tricks/

Lean Bonaventura
fuente
-1

Todas las respuestas parecen centrarse en el uso HttpClientsincrónico en lugar de dar una respuesta real a la pregunta.

HttpClientes más que un simple controlador de solicitud / respuesta, por lo que puede tratar algunas peculiaridades de diferentes redes. Es decir, en mi caso, trabajar con proxy NTLM que requiere negociación, enviando múltiples solicitudes / respuestas con tokens y credenciales entre el cliente y el servidor proxy para autenticar. HttpClient(usando HttpClientHandler) parece tener un mecanismo incorporado que maneja que devuelve recursos más allá del proxy con una llamada al método.

Bizniztime
fuente
Su respuesta tampoco explica cómo usar HttpClient de forma asincrónica.
user275801
@ user275801 Ese es un comentario tonto. Nadie preguntó eso. Es asíncrono por defecto.
Bizniztime