Як я можу використовувати HttpWebRequest (.NET, C #) асинхронно?
Як я можу використовувати HttpWebRequest (.NET, C #) асинхронно?
Відповіді:
Використовуйте HttpWebRequest.BeginGetResponse()
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
Функція зворотного дзвінка викликається, коли асинхронна операція завершена. Вам потрібно принаймні зателефонувати EndGetResponse()
з цієї функції.
webRequest.Proxy = null
щоб швидко пришвидшити запит.
Враховуючи відповідь:
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-сокета), перш ніж цей метод стане асинхронним. Як результат, цей спосіб ніколи не повинен викликатись у потоці користувальницького інтерфейсу (UI), оскільки це може зайняти значний час (до декількох хвилин, залежно від мережевих налаштувань), щоб виконати початкові завдання синхронного налаштування перед тим, як буде викинуто виняток для помилки або метод вдається.
Отже, щоб зробити це правильно:
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 . Це буквально пара рядків коду при використанні разом із новими ключовими словами асинхрон / очікування :
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, то вищезазначене може бути виконано за допомогою методу Task.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();
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 змінився з моменту опублікування багатьох цих відповідей, і я хотів би надати більш сучасну відповідь. Використовуйте метод асинхронізації, щоб почати 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 має гарне альтернативне рішення, яке вирішує ці проблеми.
WebRequest.GetResponseAsync()
і StreamReader.ReadToEndAync()
потрібно використовувати і чекали.
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);
}
}