¿Cómo puedo usar HttpWebRequest (.NET, C #) de forma asincrónica?
¿Cómo puedo usar HttpWebRequest (.NET, C #) de forma asincrónica?
Respuestas:
Utilizar HttpWebRequest.BeginGetResponse()
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
Se llama a la función de devolución de llamada cuando se completa la operación asincrónica. Necesita al menos llamar EndGetResponse()
desde esta función.
webRequest.Proxy = null
para acelerar la solicitud dramáticamente.
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 se han equivocado, porque BeginGetResponse()
funciona un poco en el hilo actual. De la documentación :
El método BeginGetResponse requiere algunas tareas de configuración síncrona para completar (resolución DNS, detección de proxy y conexión de socket TCP, por ejemplo) antes de que este método se vuelva asíncrono. Como resultado, este método nunca debe llamarse en un subproceso de interfaz de usuario (UI) porque puede llevar un tiempo considerable (hasta varios minutos dependiendo de la configuración de la red) completar las tareas iniciales de configuración síncrona antes de que se produzca una excepción por un error o El método tiene éxito.
Para hacer esto bien:
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);
}
Luego puede hacer lo que necesite 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 desde el TPL . Es literalmente un par de líneas de código cuando se usa junto con las nuevas palabras clave async / 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);
Si no puede usar el compilador C # 5, lo anterior se puede lograr usando el método Task.ContinueWith :
Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse,
request.EndGetResponse,
null)
.ContinueWith(task =>
{
var response = (HttpWebResponse) task.Result;
Debug.Assert(response.StatusCode == HttpStatusCode.OK);
});
Terminé usando BackgroundWorker, definitivamente es asíncrono 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();
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 ha cambiado desde que se publicaron muchas de estas respuestas, y me gustaría proporcionar una respuesta más actualizada. Use un método asíncrono para iniciar un Task
que se ejecutará en un hilo de fondo:
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;
}
Para usar el método asíncrono:
String response = await MakeRequestAsync("http://example.com/");
Actualizar:
Esta solución no funciona para aplicaciones UWP que usan en WebRequest.GetResponseAsync()
lugar de WebRequest.GetResponse()
, y no llama a los Dispose()
métodos cuando corresponde. @dragansr tiene una buena solución alternativa que aborda estos problemas.
WebRequest.GetResponseAsync()
y StreamReader.ReadToEndAync()
necesita ser usado y esperado.
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);
}
}