Comment utiliser HttpWebRequest (.NET) de manière asynchrone?
Comment puis-je utiliser HttpWebRequest (.NET, C#) de manière asynchrone?
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.
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
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);
});
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);
});
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();
.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.
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);
}
}
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);
}