2008-10-14 7 views
138

Wie kann ich HttpWebRequest (.NET, C#) asynchron verwenden?Wie verwende ich HttpWebRequest (.NET) asynchron?

+1

Verwendung async http://msdn.microsoft.com/en-us /library/system.net.webrequest.endgetrequeststream.aspx –

+1

Ich fragte mich, ob Sie einen rekursiven Thread kommentieren wollten. –

+0

Sie können auch Folgendes sehen, für ein ziemlich vollständiges Beispiel, wie Jason fragt: http://stuff.seans.com/2009/01/05/using-httpwebrequest-for-asynchronous-downloads/ Sean –

Antwort

115

Verwenden HttpWebRequest.BeginGetResponse()

HttpWebRequest webRequest; 

void StartWebRequest() 
{ 
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null); 
} 

void FinishWebRequest(IAsyncResult result) 
{ 
    webRequest.EndGetResponse(result); 
} 

Die Callback-Funktion wird aufgerufen, wenn die asynchrone Operation abgeschlossen ist. Sie müssen mindestens EndGetResponse() von dieser Funktion aufrufen.

+12

BeginGetResponse ist nicht so nützlich für asynchrone Verwendung. Es scheint zu blockieren, während versucht wird, die Ressource zu kontaktieren. Versuchen Sie, das Netzwerkkabel zu entfernen oder es mit einem fehlerhaften URI zu versehen, und führen Sie dann diesen Code aus. Stattdessen müssen Sie GetResponse wahrscheinlich in einem zweiten von Ihnen bereitgestellten Thread ausführen. – Ash

+2

@AshleyHenderson - Könnten Sie mir bitte ein Muster zur Verfügung stellen? – Tohid

+0

@Tohid [hier ist eine vollständige Klasse mit Beispiel] (http://stackoverflow.com/questions/12224602/a-method-for-making-http-requests-on-unity-ios/12606963#12606963) Ich habe verwendet mit Unity3D. – cregox

62

In Anbetracht der Antwort:

HttpWebRequest webRequest; 

void StartWebRequest() 
{ 
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null); 
} 

void FinishWebRequest(IAsyncResult result) 
{ 
    webRequest.EndGetResponse(result); 
} 

Sie könnten den Anforderungszeiger oder ein anderes Objekt wie folgt an:

void StartWebRequest() 
{ 
    HttpWebRequest webRequest = ...; 
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest); 
} 

void FinishWebRequest(IAsyncResult result) 
{ 
    HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse; 
} 

Grüße

+7

+1 für die Option ist, die die Variable 'request' nicht überstreicht, aber Sie könnten eine Umwandlung statt der Verwendung des Schlüsselwortes "as" vornehmen. Eine InvalidCastException würde statt einer NullReferenceException ausgelöst werden. –

3
public void GetResponseAsync (HttpWebRequest request, Action<HttpWebResponse> gotResponse) 
    { 
     if (request != null) { 
      request.BeginGetRequestStream ((r) => { 
       try { // there's a try/catch here because execution path is different from invokation one, exception here may cause a crash 
        HttpWebResponse response = request.EndGetResponse (r); 
        if (gotResponse != null) 
         gotResponse (response); 
       } catch (Exception x) { 
        Console.WriteLine ("Unable to get response for '" + request.RequestUri + "' Err: " + x); 
       } 
      }, null); 
     } 
    } 
57

Jeder war bisher falsch, weil BeginGetResponse() etwas am aktuellen Thread arbeitet. Vom documentation:

Die BeginGetResponse Methode erfordert einige synchronen Setup-Aufgaben zu komplett (DNS-Auflösung, Proxy-Erkennung, und TCP-Socket-Verbindung, zum Beispiel), bevor diese Methode asynchron wird. Als Ergebnis diese Methode sollte nie auf einer Benutzeroberfläche (UI) Thread aufgerufen werden, da es erhebliche Zeit dauern kann (bis zu mehreren Minuten je nach Netzwerkeinstellungen), um die ersten synchronen Setup-Aufgaben vor einer Ausnahme für eine abzuschließen Fehler wird ausgelöst oder die Methode ist erfolgreich.

So, dieses Recht zu tun:

void DoWithResponse(HttpWebRequest request, Action<HttpWebResponse> responseAction) 
{ 
    Action wrapperAction =() => 
    { 
     request.BeginGetResponse(new AsyncCallback((iar) => 
     { 
      var response = (HttpWebResponse)((HttpWebRequest)iar.AsyncState).EndGetResponse(iar); 
      responseAction(response); 
     }), request); 
    }; 
    wrapperAction.BeginInvoke(new AsyncCallback((iar) => 
    { 
     var action = (Action)iar.AsyncState; 
     action.EndInvoke(iar); 
    }), wrapperAction); 
} 

Anschließend können Sie tun, was Sie mit der Antwort benötigen. Zum Beispiel:

HttpWebRequest request; 
// init your request...then: 
DoWithResponse(request, (response) => { 
    var body = new StreamReader(response.GetResponseStream()).ReadToEnd(); 
    Console.Write(body); 
}); 
+2

Konnten Sie die GetResponseAsync-Methode der HttpWebRequest-Funktion nicht nur mit apply aufrufen (vorausgesetzt, Sie haben Ihre Funktion asynchron ausgeführt)? Ich habe sehr neu in C#, so dass dies komplette Jibberish sein kann ... – Brad

+0

GetResponseAsync sieht gut aus, obwohl Sie .NET 4.5 (derzeit Beta) benötigen. – Isak

+12

Jesus. Das ist ein hässlicher Code. Warum kann Async-Code nicht lesbar sein? –

7

ich mit Background endete, ist es auf jeden Fall asynchron im Gegensatz zu einigen der oben genannten Lösungen, Griffe, die für Sie in die GUI-Thread zurückkehrt, und es ist sehr leicht zu verstehen.

Es ist auch sehr einfach, Ausnahmen zu behandeln, wie sie in der RunWorkerCompleted Methode am Ende, aber Sie dies sicher lesen: Unhandled exceptions in BackgroundWorker

ich verwenden WebClient aber offensichtlich konnte man HttpWebRequest.GetResponse verwenden, wenn man will.

var worker = new BackgroundWorker(); 

worker.DoWork += (sender, args) => { 
    args.Result = new WebClient().DownloadString(settings.test_url); 
}; 

worker.RunWorkerCompleted += (sender, e) => { 
    if (e.Error != null) { 
     connectivityLabel.Text = "Error: " + e.Error.Message; 
    } else { 
     connectivityLabel.Text = "Connectivity OK"; 
     Log.d("result:" + e.Result); 
    } 
}; 

connectivityLabel.Text = "Testing Connectivity"; 
worker.RunWorkerAsync(); 
47

mit Abstand einfachste Weg ist, durch TaskFactory.FromAsync vom TPL verwenden. Es ist buchstäblich ein paar Zeilen Code, wenn sie in Verbindung mit den neuen async/await Schlüsselwort verwendet:

var request = WebRequest.Create("http://www.stackoverflow.com"); 
var response = (HttpWebResponse) await Task.Factory 
    .FromAsync<WebResponse>(request.BeginGetResponse, 
          request.EndGetResponse, 
          null); 
Debug.Assert(response.StatusCode == HttpStatusCode.OK); 

Wenn Sie die C# 5 Compiler dann kann die oben erreicht werden, mit der Task.ContinueWith Methode verwenden können:

Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, 
            request.EndGetResponse, 
            null) 
    .ContinueWith(task => 
    { 
     var response = (HttpWebResponse) task.Result; 
     Debug.Assert(response.StatusCode == HttpStatusCode.OK); 
    }); 
+0

Seit .NET 4 ist dieser TAP-Ansatz vorzuziehen. Siehe ein ähnliches Beispiel von MS - "How to: EAP-Muster in eine Aufgabe Wrap" (http://msdn.microsoft.com/en-us/library/ee622454.aspx) –

+0

Weg einfacher als die anderen Möglichkeiten –

2

.NET hat sich geändert, seit viele dieser Antworten veröffentlicht wurden, und ich möchte eine aktuellere Antwort geben. Verwenden, um eine Asynchron-Methode auf ein Task zu beginnen, die auf einem Hintergrund-Thread ausgeführt werden:

private async Task<String> MakeRequestAsync(String url) 
{  
    String responseText = await Task.Run(() => 
    { 
     try 
     { 
      HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; 
      WebResponse response = request.GetResponse();    
      Stream responseStream = response.GetResponseStream(); 
      return new StreamReader(responseStream).ReadToEnd();    
     } 
     catch (Exception e) 
     { 
      Console.WriteLine("Error: " + e.Message); 
     } 
     return null; 
    }); 

    return responseText; 
} 

die Async Methode zu verwenden:

String response = await MakeRequestAsync("http://example.com/"); 
+0

Vielen Dank! Habe versucht, ein asynchrones Beispiel zu finden, viele Beispiele, die einen alten Ansatz verwenden, der zu komplex ist. – WDUK

+0

Blockiert dies nicht einen Thread für jede Antwort? es scheint ziemlich anders zu sein als z.B. https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-wrap-eap-patterns-in-a-task –

+0

@PeteKirkham Ein Hintergrund Thread tut die Anfrage, nicht die UI-Thread Das Ziel besteht darin, den UI-Thread nicht zu blockieren. Jede Methode, die Sie für eine Anfrage auswählen, blockiert den Thread, der die Anfrage bearbeitet. Das Microsoft-Beispiel, auf das Sie verweisen, versucht, mehrere Anforderungen zu erstellen, aber sie erstellen immer noch eine Aufgabe (einen Hintergrundthread) für die Anforderungen. – tronman

Verwandte Themen