Perché usare HttpClient per la connessione sincrona


189

Sto costruendo una libreria di classi per interagire con un'API. Devo chiamare l'API ed elaborare la risposta XML. Posso vedere i vantaggi dell'utilizzo HttpClientdella connettività asincrona, ma quello che sto facendo è puramente sincrono, quindi non vedo alcun vantaggio significativo sull'uso HttpWebRequest.

Se qualcuno potesse far luce, lo apprezzerei molto. Non sono uno che usa la nuova tecnologia per il gusto di farlo.


3
Odio dirtelo, ma una chiamata su HTTP non è mai puramente sincrona a causa del funzionamento interno delle reti Windows (ovvero porte di completamento).
TomTom


Risposte:


374

ma quello che sto facendo è puramente sincrono

È possibile utilizzare HttpClientper le richieste sincrone bene:

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

Per quanto riguarda il motivo per cui dovresti usare HttpClientover WebRequest, beh, HttpClientè il nuovo bambino sul blocco e potrebbe contenere miglioramenti rispetto al vecchio client.


27
L'uso sincrono dei metodi asincroni non bloccherebbe potenzialmente il tuo thread dell'interfaccia utente? Potresti prendere in considerazione qualcosa del genere string responseString = Task.Run(() => responseContent.ReadAsStringAsync()).Result;invece se devi renderlo sincrono.
terrestre il

13
@earthling, sì, Task.Runinvoca l'attività da un ThreadPool, ma lo stai chiamando .Resultuccidendo tutti i vantaggi da questo e bloccando il thread in cui lo hai chiamato .Result(che di solito è il thread dell'interfaccia utente principale).
Darin Dimitrov,

35
Secondo questo post ( blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx ) chiamare in .Resultquesto modo può esaurire il threadpool e causare deadlock.
Pete Garafano,

16
Questo codice si bloccherà sempre se eseguito all'interno di un'attività creata sul thread dell'interfaccia utente principale da unnew TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()
Wim Coenen,

24
Quindi, come posso usare HttpClient in modo sincrono dal thread dell'interfaccia utente? Supponiamo di voler bloccare intenzionalmente il thread dell'interfaccia utente (o sto scrivendo un'app console) fino a quando non ottengo la risposta HTTP ... Quindi, se Wait (), Result (), ecc. Possono causare deadlock, qual è la soluzione definitiva per questo senza il rischio di deadlock e senza l' utilizzo di altre classi come WebClient?
Dexter,

26

Reitererei la risposta di Donny V. e quella di Josh

"L'unico motivo per cui non utilizzerei la versione asincrona è se stessi cercando di supportare una versione precedente di .NET che non ha già integrato il supporto asincrono."

(e voto positivo se avessi la reputazione.)

Non riesco a ricordare l'ultima volta, se mai, ero grato del fatto che HttpWebRequest abbia generato eccezioni per codici di stato> = 400. Per aggirare questi problemi è necessario rilevare immediatamente le eccezioni e mapparle su alcuni meccanismi di risposta senza eccezioni nel tuo codice ... noioso, noioso e soggetto a errori in sé. Che si tratti di comunicare con un database o di implementare un proxy Web su misura, è 'quasi' sempre auspicabile che il driver Http dica semplicemente al codice dell'applicazione cosa è stato restituito e lascia a te decidere come comportarsi.

Quindi è preferibile HttpClient.


1
Sono stato sorpreso che di per HttpClientsé è un involucro HttpWebRequest(che in effetti cattura internamente quegli WebExceptionoggetti e fa la conversione in a HttpResponseMessageper te). Avrei pensato che sarebbe stato più semplice costruire un nuovo client interamente da zero.
Dai

4
Ci sono molte buone ragioni come non voler riscrivere l'intera base di codice solo per una chiamata http di livello molto basso che non è nemmeno critica per le prestazioni (ma introdurrebbe asincrono in un milione di posti).
FrankyBoy

In .net core 2 se si desidera valutare in modo dinamico un'espressione con DynamicExpressionParser, potrebbe non essere possibile utilizzare asincrono; gli indicizzatori di proprietà non possono usare asincrono; nella mia situazione ho bisogno di valutare dinamicamente una stringa come "GetDefaultWelcomeMessage [\" InitialMessage \ "]" dove questo metodo crea una HttpCall e la sintassi dell'indice è preferibile alla sintassi del metodo "Util.GetDefaultWelcomeMessage (\" InitialMessage \ ")"
eugen

7

Se stai costruendo una libreria di classi, forse gli utenti della tua libreria vorrebbero utilizzare la tua libreria in modo asincrono. Penso che sia il motivo principale proprio lì.

Inoltre, non sai come verrà utilizzata la tua libreria. Forse gli utenti elaboreranno molte e molte richieste, e farlo in modo asincrono lo aiuterà a ottenere prestazioni più veloci ed efficienti.

Se riesci a farlo semplicemente, cerca di non gravare sugli utenti della tua libreria cercando di rendere il flusso asincrono quando puoi occupartene.

L'unico motivo per cui non utilizzerei la versione asincrona è se stessi cercando di supportare una versione precedente di .NET che non ha già integrato il supporto asincrono.


Vedo, quindi rendere la libreria di classi asincrona e consentire agli utenti del sistema di decidere se usarla in modo asincrono o utilizzare wait e usarla in modo sincrono?
Ketchup,

erm, wait aiuta a rendere certe chiamate asincrone restituendo il controllo al chiamante.
Josh Smeaton,

6

Nel mio caso la risposta accettata non ha funzionato. Stavo chiamando l'API da un'applicazione MVC che non aveva azioni asincrone.

Ecco come sono riuscito a farlo funzionare:

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

Quindi l'ho chiamato così:

Helper.RunSync(new Func<Task<ReturnTypeGoesHere>>(async () => await AsyncCallGoesHere(myparameter)));

1
Thx @Darkonekt ... Questo funziona perfettamente per me. Solo HttpClient.SendAsync (...). Il risultato non funziona mai all'interno del gestore AspNet (.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();
}

Poi

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

se usi quella classe passa il tuo metodo asincrono come parametro puoi chiamare i metodi asincroni dai metodi di sincronizzazione in modo sicuro.

è spiegato qui: https://cpratt.co/async-tips-tricks/


-1

Tutte le risposte sembrano concentrarsi sull'uso HttpClientsincrono invece di dare una risposta effettiva alla domanda.

HttpClientè più di un semplice gestore di richieste / risposte, quindi può gestire alcune peculiarità di reti diverse. Vale a dire nel mio caso lavorare con il proxy NTLM che richiede negoziazione, invio di più richieste / risposte con token e credenziali tra client e server proxy per l'autenticazione. HttpClient(using HttpClientHandler) sembra avere un meccanismo incorporato che gestisce che restituisce risorse oltre il proxy con una chiamata di metodo.


La tua risposta non spiega come utilizzare HttpClient in modo asincrono.
user275801,

@ user275801 Questo è un commento stupido. Nessuno lo ha chiesto. È asincrono per impostazione predefinita.
Bizniztime,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.