¿Cómo usar HttpWebRequest (.NET) de forma asíncrona?

¿Cómo puedo usar HttpWebRequest (.NET, C #) de forma asíncrona?

Use HttpWebRequest.BeginGetResponse()

 HttpWebRequest webRequest; void StartWebRequest() { webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null); } void FinishWebRequest(IAsyncResult result) { webRequest.EndGetResponse(result); } 

La función de callback se invoca cuando se completa la operación asíncrona. Necesitas al menos llamar a EndGetResponse() desde esta función.

Considerando la respuesta:

 HttpWebRequest webRequest; void StartWebRequest() { webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null); } void FinishWebRequest(IAsyncResult result) { webRequest.EndGetResponse(result); } 

Puede enviar el puntero de solicitud o cualquier otro objeto como este:

 void StartWebRequest() { HttpWebRequest webRequest = ...; webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest); } void FinishWebRequest(IAsyncResult result) { HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse; } 

Saludos

Todos hasta ahora han estado equivocados, porque BeginGetResponse() hace algo de trabajo en el hilo actual. De la documentación :

El método BeginGetResponse requiere algunas tareas de configuración síncronas para completar (resolución de DNS, detección de proxy y conexión de socket TCP, por ejemplo) antes de que este método se vuelva asincrónico. Como resultado, nunca se debe llamar a este método en un subproceso de interfaz de usuario (IU) porque puede llevar un tiempo considerable (hasta varios minutos según la configuración de red) completar las tareas de configuración síncronas iniciales antes de que se genere una excepción de error. el método tiene éxito.

Entonces para hacer esto bien:

 void DoWithResponse(HttpWebRequest request, Action 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); } 

Luego puede hacer lo que necesita con la respuesta. Por ejemplo:

 HttpWebRequest request; // init your request...then: DoWithResponse(request, (response) => { var body = new StreamReader(response.GetResponseStream()).ReadToEnd(); Console.Write(body); }); 

Con mucho, la forma más fácil es usar TaskFactory.FromAsync del TPL . Literalmente se trata de un par de líneas de código cuando se utiliza junto con las nuevas palabras clave async / await :

 var request = WebRequest.Create("http://www.stackoverflow.com"); var response = (HttpWebResponse) await Task.Factory .FromAsync(request.BeginGetResponse, request.EndGetResponse, null); Debug.Assert(response.StatusCode == HttpStatusCode.OK); 

Si no puede usar el comstackdor C # 5, lo anterior se puede lograr utilizando el método Task.ContinueWith :

 Task.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null) .ContinueWith(task => { var response = (HttpWebResponse) task.Result; Debug.Assert(response.StatusCode == HttpStatusCode.OK); }); 

Terminé usando BackgroundWorker, definitivamente es asincrónico a diferencia de algunas de las soluciones anteriores, maneja el retorno al hilo de la GUI por usted, y es muy fácil de entender.

También es muy fácil manejar excepciones, ya que terminan en el método RunWorkerCompleted, pero asegúrese de leer esto: excepciones no controladas en BackgroundWorker

Usé WebClient pero obviamente podrías usar HttpWebRequest.GetResponse si quisieras.

 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 ha cambiado desde que se publicaron muchas de estas respuestas, y me gustaría brindar una respuesta más actualizada. Use un método asíncrono para iniciar una Task que se ejecutará en un hilo de fondo:

 private async Task 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; } 

Para usar el método async:

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

Actualizar:

Esta solución no funciona para las aplicaciones UWP que usan WebRequest.GetResponseAsync() lugar de WebRequest.GetResponse() , y no llama a los métodos Dispose() donde corresponda. @dragansr tiene una buena solución alternativa que aborda estos problemas.

 public void GetResponseAsync (HttpWebRequest request, Action 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 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 GetStringAsync(string url) { var bytes = await GetBytesAsync(url); return Encoding.UTF8.GetString(bytes, 0, bytes.Length); }