Como posso usar o HttpWebRequest (.NET, C #) de forma assíncrona?
Como posso usar o HttpWebRequest (.NET, C #) de forma assíncrona?
Respostas:
Usar 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 é chamada quando a operação assíncrona estiver concluída. Você precisa pelo menos ligar a EndGetResponse()
partir desta função.
webRequest.Proxy = null
para acelerar drasticamente a solicitação.
Considerando a resposta:
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
Você pode enviar o ponteiro da solicitação 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;
}
saudações
Todo mundo até agora está errado, porque BeginGetResponse()
faz algum trabalho no segmento atual. A partir da documentação :
O método BeginGetResponse requer que algumas tarefas de configuração síncrona sejam concluídas (resolução DNS, detecção de proxy e conexão de soquete TCP, por exemplo) antes que esse método se torne assíncrono. Como resultado, esse método nunca deve ser chamado em um encadeamento da interface do usuário, pois pode levar um tempo considerável (até vários minutos, dependendo das configurações da rede) para concluir as tarefas de configuração síncrona inicial antes que uma exceção para um erro seja lançada ou o método é bem sucedido.
Então, para fazer isso direito:
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 fazer o que precisar 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 é usar o TaskFactory.FromAsync da TPL . É literalmente algumas linhas de código quando usadas em conjunto com as novas palavras-chave 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);
Se você não pode usar o compilador C # 5, o procedimento acima pode ser realizado usando 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 o BackgroundWorker, é definitivamente assíncrono, ao contrário de algumas das soluções acima, ele lida com o retorno ao thread da GUI para você e é muito fácil de entender.
Também é muito fácil lidar com exceções, pois elas acabam no método RunWorkerCompleted, mas certifique-se de ler o seguinte: Exceções não tratadas no BackgroundWorker
Eu usei o WebClient, mas obviamente você poderia usar HttpWebRequest.GetResponse, se quisesse.
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);
}
O .NET mudou desde que muitas dessas respostas foram postadas e eu gostaria de fornecer uma resposta mais atualizada. Use um método assíncrono para iniciar um Task
que será executado em um encadeamento em segundo plano:
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/");
Atualizar:
Esta solução não funciona para aplicativos UWP que usam em WebRequest.GetResponseAsync()
vez de WebRequest.GetResponse()
e não chama os Dispose()
métodos quando apropriado. O @dragansr tem uma boa solução alternativa que resolve esses problemas.
WebRequest.GetResponseAsync()
e StreamReader.ReadToEndAync()
precisa ser usado e aguardado.
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);
}
}