Comment utiliser HttpWebRequest (.NET) de manière asynchrone?

Comment puis-je utiliser HttpWebRequest (.NET, C#) de manière asynchrone?

142
demandé sur Ferruccio 2008-10-14 23:22:27

8 réponses

Utilisation HttpWebRequest.BeginGetResponse()

HttpWebRequest webRequest;

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

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

la fonction de rappel est appelée lorsque l'opération asynchrone est terminée. Vous devez au moins appeler EndGetResponse() à partir de cette fonction.

116
répondu Jon B 2010-10-27 20:12:07

considérant la réponse:

HttpWebRequest webRequest;

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

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

vous pouvez envoyer le pointeur de requête ou tout autre objet comme ceci:

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

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

salutations

63
répondu xlarsx 2010-12-13 22:31:56

tout le monde jusqu'à présent a eu tort, parce que BeginGetResponse() fait quelque travail sur le fil courant. De la documentation :

la méthode BeginGetResponse nécessite des tâches de configuration synchrones pour complet (résolution DNS, proxy détection et connexion socket TCP, par exemple) avant que cette méthode ne devienne asynchrone. La suite, cette méthode ne doit jamais être appelée sur une interface utilisateur (UI) thread parce qu'il pourrait prendre beaucoup de temps (jusqu'à plusieurs minutes en fonction des paramètres du réseau) pour compléter la synchronie initiale tâches de configuration avant qu'une exception pour une erreur soit lancée ou la méthode réussir.

donc pour faire ce droit:

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

vous pouvez alors faire ce que vous avez besoin de faire avec la réponse. Par exemple:

HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
    var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
    Console.Write(body);
});
58
répondu Isak 2012-12-20 00:09:23

de loin, la façon la plus simple est d'utiliser TaskFactory.FromAsync from TPL . C'est littéralement un couple de lignes de code lorsqu'il est utilisé en conjonction avec le nouveau async/attente mots clés:

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

si vous ne pouvez pas utiliser le compilateur C#5, alors vous pouvez accomplir ce qui précède en utilisant la tâche .Suite avec méthode:

Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse,
                                    request.EndGetResponse,
                                    null)
    .ContinueWith(task =>
    {
        var response = (HttpWebResponse) task.Result;
        Debug.Assert(response.StatusCode == HttpStatusCode.OK);
    });
54
répondu Nathan Baulch 2014-04-11 05:15:07

j'ai fini par utiliser BackgroundWorker, il est certainement asynchrone contrairement à certaines des solutions ci-dessus, il poignées de retour au fil GUI pour vous, et il est très facile à comprendre.

il est également très facile de gérer les exceptions, car ils finissent dans la méthode RunWorkerCompleted, mais assurez-vous de lire ce qui suit: unhandled exceptions in BackgroundWorker

J'ai utilisé WebClient mais évidemment vous pouvez utiliser HttpWebRequest.Obtenir réponse si vous vouliez.

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
répondu eggbert 2017-05-23 12:18:23

.NET a changé depuis que beaucoup de ces réponses ont été postées, et j'aimerais fournir une réponse plus à jour. Utilisez une méthode async pour démarrer un Task qui s'exécute sur un thread de fond:

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

pour utiliser la méthode async:

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

mise à jour:

Cette solution ne fonctionne pas pour les applications UWP qui utilisent WebRequest.GetResponseAsync() au lieu de WebRequest.GetResponse() , et elle n'appelle pas le Dispose() , le cas échéant. @dragansr a une bonne solution alternative qui aborde ces questions.

4
répondu tronman 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
répondu Sten Petrov 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
répondu dragansr 2018-03-12 11:47:29