Por que usar o HttpClient para conexão síncrona

188

Estou construindo uma biblioteca de classes para interagir com uma API. Preciso chamar a API e processar a resposta XML. Posso ver os benefícios de usar HttpClientpara conectividade assíncrona, mas o que estou fazendo é puramente síncrono, portanto, não vejo nenhum benefício significativo sobre o uso HttpWebRequest.

Se alguém puder lançar alguma luz, eu apreciaria muito. Eu não sou do tipo que usa novas tecnologias por causa disso.

Ketchup
fonte
3
Detesto dizer, mas uma chamada por HTTP nunca é puramente síncrona devido à maneira como a rede do Windows funciona internamente (também conhecida como portas de conclusão).
TomTom
Além disso, é bom saber - Efetivamente usar async / await com ASP.NET Web API
RBT

Respostas:

374

mas o que estou fazendo é puramente síncrono

Você pode usar HttpClientpara solicitações síncronas muito bem:

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

No que diz respeito ao motivo pelo qual você deve usar o HttpClientexcesso WebRequest, bem, HttpClienté o novo garoto do setor e pode conter melhorias em relação ao cliente antigo.

Darin Dimitrov
fonte
27
Seu uso síncrono dos métodos assíncronos não bloquearia potencialmente o thread da interface do usuário? Você pode considerar algo como isso, string responseString = Task.Run(() => responseContent.ReadAsStringAsync()).Result;se precisar tornar isso síncrono.
earthling
13
@earthling, sim, Task.Runinvoca a tarefa a partir de um ThreadPool, mas você está solicitando que .Resultela elimine todos os benefícios disso e bloqueie o segmento no qual você chamou isso .Result(que geralmente é o principal thread da interface do usuário).
Darin Dimitrov
35
De acordo com esta postagem ( blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx ), chamar .Resultassim pode esgotar o conjunto de threads e causar um conflito.
Pete Garafano
16
Este código será sempre impasse se executado dentro de uma tarefa criada no principal segmento interface do usuário por umnew TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()
Wim Coenen
24
Então, como eu uso o HttpClient de forma síncrona do thread da interface do usuário? Digamos que eu queira bloquear intencionalmente o thread da interface do usuário (ou estou escrevendo um aplicativo de console) até obter a resposta HTTP ... Portanto, se Wait (), Result (), etc. puder causar conflitos, qual é a solução definitiva para isso sem o risco de conflito e sem usar outras classes como o WebClient?
Dexter
26

Eu reiterava a resposta de Donny V. e a de Josh

"O único motivo pelo qual eu não usaria a versão assíncrona é se eu estava tentando oferecer suporte a uma versão mais antiga do .NET que ainda não possui suporte assíncrono."

(e voto positivo, se eu tivesse reputação.)

Não me lembro da última vez, fiquei grato pelo fato de o HttpWebRequest ter lançado exceções para códigos de status> = 400. Para contornar esses problemas, você precisa capturar as exceções imediatamente e mapeá-las para alguns mecanismos de resposta que não sejam de exceção no seu código ... chato, tedioso e propenso a erros em si. Seja comunicando-se com um banco de dados ou implementando um proxy da Web sob medida, é quase sempre desejável que o driver Http diga ao código do aplicativo o que foi retornado e deixe que você decida como se comportar.

Portanto, o HttpClient é preferível.

trev
fonte
1
Fiquei surpreso que HttpClientele próprio seja um invólucro HttpWebRequest(que de fato captura internamente esses WebExceptionobjetos e faz a conversão para um HttpResponseMessagepara você). Eu teria pensado que seria mais fácil construir um novo cliente totalmente do zero.
Dai
4
Há muitas boas razões para não querer reescrever toda a sua base de código apenas para uma chamada http de nível muito baixo que nem sequer é crítica para o desempenho (mas introduziria a assíncrona em um milhão de lugares).
FrankyBoy
No .net core 2, se você deseja avaliar dinamicamente uma expressão com DynamicExpressionParser, talvez não seja possível usar async; indexadores de propriedades não podem usar assíncrono; na minha situação, preciso avaliar dinamicamente uma string como "GetDefaultWelcomeMessage [\" InitialMessage \ "]" ", em que esse método cria um HttpCall e a sintaxe do índice é preferível à sintaxe do método" Util.GetDefaultWelcomeMessage (\ "InitialMessage \") "
eugen
7

Se você estiver criando uma biblioteca de classes, talvez os usuários da sua biblioteca desejem usá-la de forma assíncrona. Eu acho que essa é a maior razão aqui.

Você também não sabe como sua biblioteca será usada. Talvez os usuários processem muitas e muitas solicitações, e fazê-lo de forma assíncrona ajudará a executar com mais rapidez e eficiência.

Se você puder fazer isso de forma simples, tente não sobrecarregar os usuários da sua biblioteca, tentando tornar o fluxo assíncrono quando puder cuidar dele.

O único motivo pelo qual eu não usaria a versão assíncrona é se eu estava tentando oferecer suporte a uma versão mais antiga do .NET que ainda não possui suporte assíncrono.

Josh Smeaton
fonte
Entendo, então torne a biblioteca de classes assíncrona e permita que os usuários do sistema decidam se devem usá-la de forma assíncrona ou usar aguardar e usá-la de forma síncrona.
Ketchup
aguardar ajuda a tornar determinadas chamadas assíncronas, retornando o controle ao chamador.
quer
6

No meu caso, a resposta aceita não funcionou. Eu estava chamando a API de um aplicativo MVC que não tinha ações assíncronas.

Foi assim que consegui fazê-lo 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();
    }

Então eu chamei assim:

Helper.RunSync(new Func<Task<ReturnTypeGoesHere>>(async () => await AsyncCallGoesHere(myparameter)));
Jonathan Alfaro
fonte
1
Thx @Darkonekt ... Isso funciona perfeitamente para mim. Somente o resultado HttpClient.SendAsync (...) nunca funciona no AspNet Handler (.ASHX).
Rafael Kazuo Sato Simiao 22/10
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();
}

Então

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

se você usar essa classe, passe seu método assíncrono como parâmetro, poderá chamar os métodos assíncronos de métodos sincronizados de maneira segura.

é explicado aqui: https://cpratt.co/async-tips-tricks/

Lean Bonaventura
fonte
-1

Todas as respostas parecem focar no uso HttpClientsíncrono, em vez de dar uma resposta real à pergunta.

HttpClienté mais do que um simples manipulador de solicitação / resposta, para que ele possa lidar com algumas peculiaridades de diferentes redes. Ou seja, no meu caso, trabalhando com o proxy NTLM, que requer negociação, enviando várias solicitações / respostas com tokens e credenciais entre o cliente e o servidor proxy para autenticação. HttpClient(using HttpClientHandler) parece ter um mecanismo interno que lida com o retorno de recursos além do proxy com uma chamada de método.

Bizniztime
fonte
Sua resposta também não explica como usar o HttpClient de forma assíncrona.
User275801 21/06
@ user275801 Esse é um comentário idiota. Ninguém perguntou isso. É assíncrono por padrão.
Bizniztime 23/06