Pergunta

Como posso usar HttpWebRequest (.NET, C #) de forma assíncrona?

Foi útil?

Solução

Use HttpWebRequest.BeginGetResponse()

HttpWebRequest webRequest;

void StartWebRequest()
{
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}

void FinishWebRequest(IAsyncResult result)
{
    webRequest.EndGetResponse(result);
}

A função de retorno de chamada é chamado quando a operação assíncrona for concluída. Você precisa pelo menos chamada EndGetResponse() deste função.

Outras dicas

Considerando a resposta:

HttpWebRequest webRequest;

void StartWebRequest()
{
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}

void FinishWebRequest(IAsyncResult result)
{
    webRequest.EndGetResponse(result);
}

Você poderia enviar o ponteiro pedido ou qualquer outro 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;
}

Greetings

Todo mundo até agora tem sido errado, porque BeginGetResponse() faz algum trabalho no segmento atual. A partir da documentação :

O método BeginGetResponse requer algumas tarefas de configuração síncrona para completo (resolução de DNS, detecção de proxy, e conexão de soquete TCP, por exemplo) antes de este método torna-se assíncrono. Como um resultado, este método não deve ser chamado em uma interface de usuário (UI) Rosca porque pode levar um tempo considerável (até vários minutos dependendo das configurações de rede) para completar a síncrona inicial tarefas de configuração antes de uma exceção para um erro é lançado ou o método bem-sucedido.

Então, para fazer isso mesmo:

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);
}

Você pode, então, fazer o que você precisa com a resposta. Por exemplo:

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

De longe a maneira mais fácil é usando TaskFactory.FromAsync do TPL . É literalmente um par de linhas de código quando usado em conjunto com o novo async / await palavras-chave:

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 você não pode usar o C # 5 compilador, o texto acima pode ser realizada utilizando o 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);
    });

Acabei usando BackgroundWorker, é definitivamente assíncrona ao contrário de algumas das soluções acima, ele lida com o retorno para o segmento GUI para você, e é muito fácil de entender.

Também é muito fácil de exceções do punho, como eles acabam no método RunWorkerCompleted, mas certifique-se de ler este: exceções sem tratamento em BackgroundWorker

Eu costumava WebClient mas, obviamente, você poderia usar HttpWebRequest.GetResponse se você queria.

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 mudou desde que muitas dessas respostas foram publicadas, e eu gostaria de dar uma resposta mais up-to-date. Use um método assíncrono para iniciar um Task que será executado em uma discussão de fundo:

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 o método assíncrono:

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

Update:

Esta solução não funciona para aplicativos UWP que usam WebRequest.GetResponseAsync() vez de WebRequest.GetResponse(), e não chamar os métodos Dispose() se for o caso. @dragansr tem uma boa solução alternativa que aborda estas questões.

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top