8 ответов:
использовать
HttpWebRequest.BeginGetResponse()HttpWebRequest webRequest; void StartWebRequest() { webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null); } void FinishWebRequest(IAsyncResult result) { webRequest.EndGetResponse(result); }функция обратного вызова вызывается после завершения асинхронной операции. Вам нужно хотя бы позвонить
EndGetResponse()от этой функции.
учитывая, ответ:
HttpWebRequest webRequest; void StartWebRequest() { webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null); } void FinishWebRequest(IAsyncResult result) { webRequest.EndGetResponse(result); }вы можете отправить указатель запроса или любой другой объект, как это:
void StartWebRequest() { HttpWebRequest webRequest = ...; webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest); } void FinishWebRequest(IAsyncResult result) { HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse; }поздравления
все до сих пор ошибались, потому что
BeginGetResponse()выполняет некоторую работу над текущим потоком. Из документация:метод BeginGetResponse требует выполнения некоторых синхронных задач настройки завершите (разрешение DNS, обнаружение прокси, и соединение гнезда TCP, например) до того, как этот метод станет асинхронным. В результате, этот метод никогда не должен вызываться в потоке пользовательского интерфейса потому что это может занять значительное время (до несколько минут в зависимости от настроек сети) для завершения начальной синхронной задачи установки перед выдачей исключения для ошибки или метода преуспевает.
Итак, чтобы сделать это правильно:
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); }вы можете сделать то, что вам нужно с ответом. Например:
HttpWebRequest request; // init your request...then: DoWithResponse(request, (response) => { var body = new StreamReader(response.GetResponseStream()).ReadToEnd(); Console.Write(body); });
На сегодняшний день самый простой способ сделать это с помощью TaskFactory.FromAsync С TPL. Это буквально пара строк кода при использовании в сочетании с новым async / await ключевые слова:
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);Если вы не можете использовать компилятор C#5, то выше можно выполнить с помощью задач.ContinueWith способ:
Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null) .ContinueWith(task => { var response = (HttpWebResponse) task.Result; Debug.Assert(response.StatusCode == HttpStatusCode.OK); });
Я в конечном итоге использовал BackgroundWorker, он определенно асинхронный в отличие от некоторых из вышеперечисленных решений, он обрабатывает возврат к потоку GUI для вас, и это очень легко понять.
также очень легко обрабатывать исключения, поскольку они заканчиваются в методе RunWorkerCompleted, но убедитесь, что вы прочитали это:необработанные исключения в BackgroundWorker
я использовал WebClient, но, очевидно, вы могли бы использовать HttpWebRequest.GetResponse если вы желаемый.
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 изменился с тех пор, как многие из этих ответов были опубликованы, и я хотел бы предоставить более актуальный ответ. Используйте асинхронный метод для запуска
Taskчто будет работать на фоновом потоке: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; }чтобы использовать асинхронный метод:
String response = await MakeRequestAsync("http://example.com/");обновление:
это решение не работает для приложений UWP, которые используют
WebRequest.GetResponseAsync()вместоWebRequest.GetResponse(), и не вызыватьDispose()методы, где это уместно. @dragansr имеет хорошую альтернативу решение, которое решает эти вопросы.
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); }
Comments