Comment puis-je utiliser HttpWebRequest (.NET, C #) de manière asynchrone?
Comment puis-je utiliser HttpWebRequest (.NET, C #) de manière asynchrone?
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.
webRequest.Proxy = null
pour accélérer considérablement la demande.
Compte tenu de 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 celui-ci:
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
Jusqu'à présent, tout le monde s'est trompé, car BeginGetResponse()
il travaille sur le fil actuel. De la documentation :
La méthode BeginGetResponse nécessite l'exécution de certaines tâches de configuration synchrones (résolution DNS, détection de proxy et connexion de socket TCP, par exemple) avant que cette méthode ne devienne asynchrone. Par conséquent, cette méthode ne doit jamais être appelée sur un thread d'interface utilisateur (UI) car cela peut prendre un temps considérable (jusqu'à plusieurs minutes selon les paramètres réseau) pour terminer les tâches de configuration synchrones initiales avant qu'une exception pour une erreur ne soit levée ou la méthode réussit.
Alors pour bien faire:
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 dont vous avez besoin 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);
});
Le moyen de loin le plus simple consiste à utiliser TaskFactory.FromAsync à partir du TPL . Il s'agit littéralement de quelques lignes de code lorsqu'il est utilisé en conjonction avec les nouveaux mots clés async / await :
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, les opérations ci-dessus peuvent être effectuées à l'aide de la méthode Task.ContinueWith :
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 définitivement asynchrone contrairement à certaines des solutions ci-dessus, il gère le retour au thread GUI pour vous, et il est très facile à comprendre.
Il est également très facile de gérer les exceptions, car elles se retrouvent dans la méthode RunWorkerCompleted, mais assurez-vous de lire ceci: Exceptions non gérées dans BackgroundWorker
J'ai utilisé WebClient mais vous pouvez évidemment utiliser HttpWebRequest.GetResponse si vous le souhaitez.
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();
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);
}
.NET a changé depuis que plusieurs de ces réponses ont été publiées, et j'aimerais fournir une réponse plus à jour. Utilisez une méthode async pour démarrer un Task
qui s'exécutera sur un thread d'arrière-plan:
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 asynchrone:
String response = await MakeRequestAsync("http://example.com/");
Mettre à jour:
Cette solution ne fonctionne pas pour les applications UWP qui utilisent à la WebRequest.GetResponseAsync()
place de WebRequest.GetResponse()
, et n'appelle pas les Dispose()
méthodes le cas échéant. @dragansr a une bonne solution alternative qui résout ces problèmes.
WebRequest.GetResponseAsync()
et StreamReader.ReadToEndAync()
doivent être utilisés et attendus.
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);
}
}