Come posso usare HttpWebRequest (.NET, C #) in modo asincrono?
Come posso usare HttpWebRequest (.NET, C #) in modo asincrono?
Risposte:
Uso HttpWebRequest.BeginGetResponse()
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
La funzione di richiamata viene chiamata al termine dell'operazione asincrona. Devi almeno chiamare EndGetResponse()
da questa funzione.
webRequest.Proxy = null
per accelerare notevolmente la richiesta.
Considerando la risposta:
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
È possibile inviare il puntatore della richiesta o qualsiasi altro oggetto come questo:
void StartWebRequest()
{
HttpWebRequest webRequest = ...;
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest);
}
void FinishWebRequest(IAsyncResult result)
{
HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse;
}
Saluti
Finora tutti hanno sbagliato, perché BeginGetResponse()
alcuni funzionano sul thread corrente. Dalla documentazione :
Il metodo BeginGetResponse richiede il completamento di alcune attività di configurazione sincrone (risoluzione DNS, rilevamento proxy e connessione socket TCP, ad esempio) prima che questo metodo diventi asincrono. Di conseguenza, questo metodo non deve mai essere chiamato su un thread dell'interfaccia utente (UI) perché potrebbe richiedere molto tempo (fino a diversi minuti a seconda delle impostazioni di rete) per completare le attività di configurazione sincrone iniziali prima che venga generata un'eccezione per un errore o il metodo ha esito positivo.
Quindi per farlo bene:
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);
}
È quindi possibile fare ciò che è necessario con la risposta. Per esempio:
HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.Write(body);
});
Di gran lunga il modo più semplice è utilizzare TaskFactory.FromAsync dal TPL . È letteralmente un paio di righe di codice quando utilizzato in combinazione con le nuove parole chiave asincrono / wait :
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);
Se non è possibile utilizzare il compilatore C # 5, è possibile eseguire quanto sopra utilizzando il metodo Task.ContinueWith :
Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse,
request.EndGetResponse,
null)
.ContinueWith(task =>
{
var response = (HttpWebResponse) task.Result;
Debug.Assert(response.StatusCode == HttpStatusCode.OK);
});
Ho finito con BackgroundWorker, è decisamente asincrono a differenza di alcune delle soluzioni precedenti, gestisce il ritorno al thread della GUI per te ed è molto facile da capire.
È anche molto facile gestire le eccezioni, poiché finiscono nel metodo RunWorkerCompleted, ma assicurati di leggere questo: Eccezioni non gestite in BackgroundWorker
Ho usato WebClient ma ovviamente potresti usare HttpWebRequest.GetResponse se lo desideri.
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 è cambiato da quando sono state pubblicate molte di queste risposte e vorrei fornire una risposta più aggiornata. Utilizzare un metodo asincrono per avviare un Task
che verrà eseguito su un thread in background:
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;
}
Per utilizzare il metodo asincrono:
String response = await MakeRequestAsync("http://example.com/");
Aggiornare:
Questa soluzione non funziona per le app UWP che usano al WebRequest.GetResponseAsync()
posto di WebRequest.GetResponse()
e non chiama i Dispose()
metodi dove appropriato. @dragansr ha una buona soluzione alternativa che risolve questi problemi.
WebRequest.GetResponseAsync()
e StreamReader.ReadToEndAync()
devono essere utilizzati e atteso.
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);
}
}