Pregunta

¿Cómo puedo usar HttpWebRequest (.NET, C #) de forma asincrónica?

¿Fue útil?

Solución

Use 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 asíncrona. Al menos debe llamar a EndGetResponse () desde esta función.

Otros consejos

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

Hasta ahora todo el mundo ha estado equivocado, porque BeginGetResponse () hace algún trabajo en el hilo actual. De la documentación :

  

El método BeginGetResponse requiere algunas tareas de configuración síncrona para   completa (resolución de 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 hilo de interfaz de usuario (UI)   porque puede llevar un tiempo considerable (hasta varios minutos   dependiendo de la configuración de red) para completar la sincronización síncrona inicial   configurar tareas antes de que se produzca una excepción por un error o el método   tiene éxito.

Así que 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);
}

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 de TPL . Es literalmente un par de líneas de código cuando se usa junto con el nuevo async / await palabras clave:

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 Task.ContinueWith método:

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 subproceso de la GUI, 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 proporcionar 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<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/");

Actualización :

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

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);
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top