Pourquoi utiliser HttpClient pour une connexion synchrone

189

Je construis une bibliothèque de classes pour interagir avec une API. Je dois appeler l'API et traiter la réponse XML. Je peux voir les avantages de l'utilisation HttpClientde la connectivité asynchrone, mais ce que je fais est purement synchrone, donc je ne vois aucun avantage significatif par rapport à l'utilisation HttpWebRequest.

Si quelqu'un peut faire la lumière, je l'apprécierais beaucoup. Je ne suis pas du genre à utiliser les nouvelles technologies pour le plaisir.

Ketchup
la source
3
Je déteste vous le dire, mais un appel via HTTP n'est jamais purement synchrone en raison du fonctionnement interne du réseau Windows (aka ports d'achèvement).
TomTom

Réponses:

374

mais ce que je fais est purement synchrone

Vous pouvez très bien utiliser HttpClientpour les requêtes synchrones:

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 ce qui concerne la raison pour laquelle vous devriez utiliser HttpClientover WebRequest, eh bien, HttpClientle nouvel enfant est sur le point et pourrait contenir des améliorations par rapport à l'ancien client.

Darin Dimitrov
la source
27
Votre utilisation synchrone des méthodes asynchrones ne bloquerait-elle pas potentiellement votre thread d'interface utilisateur? Vous voudrez peut-être envisager quelque chose comme string responseString = Task.Run(() => responseContent.ReadAsStringAsync()).Result;si vous devez rendre ce synchrone.
earthling
13
@earthling, oui, Task.Runappelle la tâche à partir d'un ThreadPool, mais vous l'appelez .Resulten supprimant tous les avantages et en bloquant le thread dans lequel vous l'avez appelé .Result(qui se trouve généralement être le thread principal de l'interface utilisateur).
Darin Dimitrov le
35
Selon cet article ( blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx ), un .Resulttel appel peut épuiser le pool de threads et provoquer un blocage.
Pete Garafano
16
Ce code sera toujours bloqué s'il est exécuté dans une tâche créée sur le thread principal de l'interface utilisateur par unnew TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()
Wim Coenen
24
Alors, comment utiliser HttpClient de manière synchrone à partir du thread d'interface utilisateur? Disons que je veux délibérément bloquer le thread de l'interface utilisateur (ou j'écris une application console) jusqu'à ce que j'obtienne la réponse HTTP ... Donc, si Wait (), Result (), etc. peuvent provoquer des blocages, quelle est la solution définitive pour cela sans le risque de blocage et sans utiliser d'autres classes comme WebClient?
Dexter
26

Je réitérerais la réponse de Donny V. et celle de Josh

"La seule raison pour laquelle je n'utiliserais pas la version asynchrone est si j'essayais de prendre en charge une version plus ancienne de .NET qui n'a pas déjà intégré la prise en charge asynchrone."

(et voter pour si j'avais la réputation.)

Je ne me souviens pas de la dernière fois, si jamais, j'étais reconnaissant du fait que HttpWebRequest ait lancé des exceptions pour les codes d'état> = 400. Pour contourner ces problèmes, vous devez attraper les exceptions immédiatement et les mapper à certains mécanismes de réponse sans exception dans votre code ... ennuyeux, fastidieux et sujet aux erreurs en soi. Qu'il s'agisse de communiquer avec une base de données ou d'implémenter un proxy Web sur mesure, il est «presque» toujours souhaitable que le pilote Http dise simplement à votre code d'application ce qui a été renvoyé et vous laisse le soin de décider comment vous comporter.

Par conséquent, HttpClient est préférable.

trev
la source
1
J'ai été surpris que HttpClientlui - même soit un wrapper HttpWebRequest(qui en effet, intercepte ces WebExceptionobjets en interne et effectue la conversion en un HttpResponseMessagepour vous). J'aurais pensé qu'il serait plus facile de créer un nouveau client entièrement à partir de zéro.
Dai
4
Il y a beaucoup de bonnes raisons comme ne pas vouloir réécrire toute votre base de code juste pour un appel http de très bas niveau qui n'est même pas critique en termes de performances (mais qui introduirait l'async à un million de places).
FrankyBoy
Dans .net core 2, si vous souhaitez évaluer dynamiquement une expression avec DynamicExpressionParser, il peut ne pas être possible d'utiliser async; les indexeurs de propriété ne peuvent pas utiliser async; dans ma situation, j'ai besoin d'évaluer dynamiquement une chaîne comme "GetDefaultWelcomeMessage [\" InitialMessage \ "]" où cette méthode crée un HttpCall et la syntaxe d'index est préférable à la syntaxe de méthode "Util.GetDefaultWelcomeMessage (\" InitialMessage \ ")"
eugen
7

Si vous créez une bibliothèque de classes, les utilisateurs de votre bibliothèque souhaiteraient peut-être utiliser votre bibliothèque de manière asynchrone. Je pense que c'est la principale raison ici.

Vous ne savez pas non plus comment votre bibliothèque sera utilisée. Peut-être que les utilisateurs traiteront beaucoup, beaucoup de demandes, et le faire de manière asynchrone l'aidera à fonctionner plus rapidement et plus efficacement.

Si vous pouvez le faire simplement, essayez de ne pas mettre le fardeau sur les utilisateurs de votre bibliothèque qui tentent de rendre le flux asynchrone lorsque vous pouvez vous en occuper pour eux.

La seule raison pour laquelle je n'utiliserais pas la version asynchrone est si j'essayais de prendre en charge une ancienne version de .NET qui n'a pas déjà intégré la prise en charge asynchrone.

Josh Smeaton
la source
Je vois, donc rendre la bibliothèque de classes asynchrone, et permettre aux utilisateurs du système de décider de l'utiliser de manière asynchrone, ou d'utiliser await et de l'utiliser de manière synchrone?
Ketchup
erm, await permet de rendre certains appels asynchrones en rendant le contrôle à l'appelant.
Josh Smeaton
6

Dans mon cas, la réponse acceptée n'a pas fonctionné. J'appelais l'API à partir d'une application MVC qui n'avait aucune action asynchrone.

Voici comment j'ai réussi à le faire fonctionner:

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

Ensuite, je l'ai appelé comme ceci:

Helper.RunSync(new Func<Task<ReturnTypeGoesHere>>(async () => await AsyncCallGoesHere(myparameter)));
Jonathan Alfaro
la source
1
Thx @Darkonekt ... Cela fonctionne parfaitement pour moi. Seul le HttpClient.SendAsync (...). Result ne fonctionne jamais dans 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();
}

ensuite

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

si vous utilisez cette classe, passez votre méthode async comme paramètre, vous pouvez appeler les méthodes async à partir des méthodes de synchronisation de manière sûre.

c'est expliqué ici: https://cpratt.co/async-tips-tricks/

Bonaventura maigre
la source
-1

Toutes les réponses semblent se concentrer sur l'utilisation HttpClientsynchrone au lieu de donner une réponse réelle à la question.

HttpClientest plus qu'un simple gestionnaire de requêtes / réponses, il peut donc gérer certaines particularités de différents réseaux. À savoir dans mon cas, travailler avec un proxy NTLM qui nécessite une négociation, l'envoi de plusieurs demandes / réponses avec des jetons et des informations d'identification entre le client et le serveur proxy pour s'authentifier. HttpClient(using HttpClientHandler) semble avoir un mécanisme intégré qui gère le retour des ressources au-delà du proxy avec un seul appel de méthode.

Bizniztime
la source
Votre réponse n'explique pas non plus comment utiliser HttpClient de manière asynchrone.
user275801 le
@ user275801 C'est un commentaire stupide. Personne n'a demandé cela. Il est asynchrone par défaut.
Bizniztime le