Warum sollten Sie HttpClient für die synchrone Verbindung verwenden?

187

Ich baue eine Klassenbibliothek auf, um mit einer API zu interagieren. Ich muss die API aufrufen und die XML-Antwort verarbeiten. Ich kann die Vorteile der Verwendung HttpClientfür die asynchrone Konnektivität erkennen, aber was ich tue, ist rein synchron, sodass ich keinen signifikanten Vorteil gegenüber der Verwendung sehe HttpWebRequest.

Wenn jemand Licht ins Dunkel bringen kann, würde ich es sehr schätzen. Ich bin nicht einer, der dafür neue Technologien einsetzt.

Ketchup
quelle
3
Ich muss es Ihnen nur ungern sagen, aber ein Anruf über HTTP ist aufgrund der internen Funktionsweise von Windows-Netzwerken (auch als Abschlussports bezeichnet) niemals rein synchron.
TomTom

Antworten:

370

aber was ich tue ist rein synchron

Sie können HttpClientfür synchrone Anfragen ganz gut verwenden:

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

Was den Grund betrifft, warum Sie HttpClientover verwenden sollten WebRequest, HttpClientist das neue Kind auf dem Block und könnte Verbesserungen gegenüber dem alten Client enthalten.

Darin Dimitrov
quelle
27
Würde Ihre synchrone Verwendung der asynchronen Methoden möglicherweise nicht Ihren UI-Thread blockieren? Sie können so etwas wie zu prüfen, string responseString = Task.Run(() => responseContent.ReadAsStringAsync()).Result;statt , wenn Sie müssen diese synchron machen.
Erdling
13
@earthling, ja, Task.Runruft die Aufgabe von einem ThreadPool aus auf, aber Sie rufen sie .Resultauf, um alle Vorteile daraus zu ziehen und den Thread zu blockieren, in dem Sie dies aufgerufen haben .Result(was normalerweise der Haupt-UI-Thread ist).
Darin Dimitrov
35
Laut diesem Beitrag ( blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx ) .Resultkann ein solcher Aufruf den Threadpool erschöpfen und einen Deadlock verursachen.
Pete Garafano
16
Dieser Code blockiert immer, wenn er innerhalb einer Aufgabe ausgeführt wird, die im Haupt-UI-Thread von anew TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()
Wim Coenen am
23
Wie verwende ich HttpClient synchron vom UI-Thread? Angenommen, ich möchte den UI-Thread absichtlich blockieren (oder ich schreibe eine Konsolen-App), bis ich die HTTP-Antwort erhalte ... Wenn also Wait (), Result () usw. Deadlocks verursachen können, was ist die definitive Lösung dafür ohne das Risiko eines Deadlocks und ohne Verwendung anderer Klassen wie WebClient?
Dexter
26

Ich würde die Antwort von Donny V. und Josh wiederholen

"Der einzige Grund, warum ich die asynchrone Version nicht verwenden würde, ist, wenn ich versuchen würde, eine ältere Version von .NET zu unterstützen, die noch keine integrierte asynchrone Unterstützung bietet."

(und positiv, wenn ich den Ruf hatte.)

Ich kann mich nicht an das letzte Mal erinnern, wenn überhaupt, war ich dankbar dafür, dass HttpWebRequest Ausnahmen für Statuscodes> = 400 ausgelöst hat. Um diese Probleme zu umgehen, müssen Sie die Ausnahmen sofort abfangen und sie einigen Nicht-Ausnahme-Antwortmechanismen zuordnen in Ihrem Code ... langweilig, langweilig und fehleranfällig an sich. Unabhängig davon, ob Sie mit einer Datenbank kommunizieren oder einen maßgeschneiderten Webproxy implementieren, ist es „fast“ immer wünschenswert, dass der HTTP-Treiber Ihrem Anwendungscode nur mitteilt, was zurückgegeben wurde, und es Ihnen überlässt, zu entscheiden, wie Sie sich verhalten.

Daher ist HttpClient vorzuziehen.

trev
quelle
1
Ich war überrascht, dass es HttpClientsich um einen Wrapper handelt HttpWebRequest(der diese WebExceptionObjekte tatsächlich intern erfasst und die Konvertierung in a HttpResponseMessagefür Sie vornimmt). Ich hätte gedacht, es wäre einfacher, einen neuen Client komplett von Grund auf neu zu erstellen.
Dai
3
Es gibt viele gute Gründe, warum Sie nicht Ihre gesamte Codebasis nur für einen http-Aufruf auf sehr niedriger Ebene neu schreiben möchten, der nicht einmal leistungskritisch ist (aber an einer Million Stellen Async einführen würde).
FrankyBoy
Wenn Sie in .net Core 2 einen Ausdruck mit DynamicExpressionParser dynamisch auswerten möchten, ist die Verwendung von Async möglicherweise nicht möglich. Eigenschaftsindexer können nicht asynchron verwenden. In meiner Situation muss ich eine Zeichenfolge wie "GetDefaultWelcomeMessage [\" InitialMessage \ "]" dynamisch auswerten, wobei diese Methode einen HttpCall erstellt und die Indexsyntax der Methodensyntax "Util.GetDefaultWelcomeMessage (\" InitialMessage \ ")"
eugen vorzuziehen ist
7

Wenn Sie eine Klassenbibliothek erstellen, möchten die Benutzer Ihrer Bibliothek Ihre Bibliothek möglicherweise asynchron verwenden. Ich denke, das ist genau dort der größte Grund.

Sie wissen auch nicht, wie Ihre Bibliothek verwendet werden soll. Vielleicht werden die Benutzer viele, viele Anfragen bearbeiten, und wenn Sie dies asynchron tun, können Sie schneller und effizienter arbeiten.

Wenn Sie dies einfach tun können, versuchen Sie, die Benutzer Ihrer Bibliothek nicht zu belasten, indem Sie versuchen, den Ablauf asynchron zu gestalten, wenn Sie sich um sie kümmern können.

Der einzige Grund, warum ich die asynchrone Version nicht verwenden würde, ist, wenn ich versuchen würde, eine ältere Version von .NET zu unterstützen, die noch keine integrierte asynchrone Unterstützung bietet.

Josh Smeaton
quelle
Ich verstehe, also machen Sie die Klassenbibliothek asynchron und lassen Sie die Benutzer des Systems entscheiden, ob sie asynchron oder warte und synchron verwenden?
Ketchup
ähm, warten hilft, bestimmte Anrufe asynchron zu machen, indem die Kontrolle an den Anrufer zurückgegeben wird.
Josh Smeaton
6

In meinem Fall hat die akzeptierte Antwort nicht funktioniert. Ich habe die API von einer MVC-Anwendung aufgerufen, die keine asynchronen Aktionen hatte.

So habe ich es geschafft:

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

Dann habe ich es so genannt:

Helper.RunSync(new Func<Task<ReturnTypeGoesHere>>(async () => await AsyncCallGoesHere(myparameter)));
Jonathan Alfaro
quelle
1
Thx @Darkonekt ... Das funktioniert perfekt für mich. Nur das Ergebnis HttpClient.SendAsync (...). Funktioniert nie in 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();
}

Dann

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

Wenn Sie diese Klasse verwenden und Ihre asynchrone Methode als Parameter übergeben, können Sie die asynchronen Methoden auf sichere Weise aus den Synchronisierungsmethoden aufrufen.

Es wird hier erklärt: https://cpratt.co/async-tips-tricks/

Lean Bonaventura
quelle
-1

Alle Antworten scheinen sich auf die HttpClientsynchrone Verwendung zu konzentrieren, anstatt die Frage tatsächlich zu beantworten.

HttpClientist mehr als ein einfacher Anforderungs- / Antwort-Handler, mit dem einige Besonderheiten verschiedener Netzwerke behandelt werden können. In meinem Fall nämlich mit einem NTLM-Proxy arbeiten, für den eine Verhandlung erforderlich ist, und mehrere Anfragen / Antworten mit Token und Anmeldeinformationen zwischen Client und Proxyserver zur Authentifizierung senden. HttpClient(using HttpClientHandler) scheint einen eingebauten Mechanismus zu haben, der mit einem Methodenaufruf Ressourcen jenseits des Proxys zurückgibt.

Bizniztime
quelle
Ihre Antwort erklärt auch nicht, wie HttpClient asynchron verwendet wird.
user275801
@ user275801 Das ist ein dummer Kommentar. Das hat niemand gefragt. Es ist standardmäßig asynchron.
Bizniztime