Jak używać HttpWebRequest (. NET) asynchronicznie?

Jak mogę używać HttpWebRequest (. Net, C#) asynchronicznie?

Author: Ferruccio, 2008-10-14

8 answers

Użycie HttpWebRequest.BeginGetResponse()

HttpWebRequest webRequest;

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

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

Funkcja wywołania zwrotnego jest wywoływana po zakończeniu operacji asynchronicznej. Musisz chociaż zadzwonić.EndGetResponse() z tej funkcji.

 116
Author: Jon B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-27 20:12:07

Rozważając odpowiedź:

HttpWebRequest webRequest;

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

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

Możesz wysłać wskaźnik żądania lub dowolny inny obiekt podobny do tego:

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

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

Pozdrowienia

 63
Author: xlarsx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-13 22:31:56

Do tej pory wszyscy się mylili, ponieważ BeginGetResponse() trochę pracuje nad bieżącym wątkiem. Z dokumentacji :

Metoda BeginGetResponse wymaga pewnych zadań konfiguracji synchronicznej do kompletne (rozdzielczość DNS, wykrywanie proxy i połączenie z gniazdem TCP, na przykład) zanim ta metoda stanie się asynchroniczna. W rezultacie, ta metoda nigdy nie powinna być wywoływana w wątku interfejsu użytkownika (UI) ponieważ może to zająć sporo czasu (do kilku minut w zależności od ustawień sieci), aby zakończyć początkową synchronizację ustawianie zadań przed wyrzuceniem wyjątku dla błędu lub metoda / align = "left" /

Więc zrobić to dobrze:

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

Możesz wtedy zrobić to, co musisz z odpowiedzią. Na przykład:

HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
    var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
    Console.Write(body);
});
 58
Author: Isak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-20 00:09:23

Najprostszym sposobem jest użycie TaskFactory.FromAsync z TPL . Jest to dosłownie kilka linijek kodu w połączeniu z nowymi asynchronicznymi/oczekującymi słowami kluczowymi:

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

Jeśli nie możesz użyć kompilatora C#5, to powyższe można wykonać za pomocą zadania .ContinueWith metoda:

Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse,
                                    request.EndGetResponse,
                                    null)
    .ContinueWith(task =>
    {
        var response = (HttpWebResponse) task.Result;
        Debug.Assert(response.StatusCode == HttpStatusCode.OK);
    });
 54
Author: Nathan Baulch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-11 05:15:07

Skończyłem z użyciem BackgroundWorker, jest zdecydowanie asynchroniczny w przeciwieństwie do niektórych z powyższych rozwiązań, obsługuje powrót do wątku GUI dla Ciebie i jest bardzo łatwy do zrozumienia.

Obsługa wyjątków jest również bardzo łatwa, ponieważ kończą się one w metodzie RunWorkerCompleted, ale upewnij się, że przeczytałeś to: nieobsługiwane wyjątki w BackgroundWorker

Użyłem WebClient, ale oczywiście można użyć HttpWebRequest.GetResponse jeśli chcesz.

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();
 7
Author: eggbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:23

. NET zmienił się od czasu opublikowania wielu z tych odpowiedzi i chciałbym podać bardziej aktualną odpowiedź. Użyj metody asynchronicznej, aby uruchomić Task, która będzie działać w tle wątku:

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

Aby użyć metody asynchronicznej:

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

Update:

To rozwiązanie nie działa dla aplikacji UWP, które używają WebRequest.GetResponseAsync() zamiast WebRequest.GetResponse(), i nie wywołuje metod Dispose() tam, gdzie jest to właściwe. @dragansr ma dobre alternatywne rozwiązanie, które rozwiązuje te problemy.

 4
Author: tronman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-13 15:52:49
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);
        } 
    }
 3
Author: Sten Petrov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-08 06:02:34
public static async Task<byte[]> GetBytesAsync(string url) {
    var request = (HttpWebRequest)WebRequest.Create(url);
    using (var response = await request.GetResponseAsync())
    using (var content = new MemoryStream())
    using (var responseStream = response.GetResponseStream()) {
        await responseStream.CopyToAsync(content);
        return content.ToArray();
    }
}

public static async Task<string> GetStringAsync(string url) {
    var bytes = await GetBytesAsync(url);
    return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
 1
Author: dragansr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-12 11:47:29