Bagaimana saya bisa menggunakan HttpWebRequest (.NET, C #) secara tidak sinkron?
Bagaimana saya bisa menggunakan HttpWebRequest (.NET, C #) secara tidak sinkron?
Jawaban:
Menggunakan HttpWebRequest.BeginGetResponse()
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
Fungsi panggilan balik dipanggil saat operasi asinkron selesai. Anda harus setidaknya menelepon EndGetResponse()
dari fungsi ini.
webRequest.Proxy = null
untuk mempercepat permintaan secara dramatis.
Mempertimbangkan jawabannya:
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
Anda dapat mengirim pointer permintaan atau objek lain seperti ini:
void StartWebRequest()
{
HttpWebRequest webRequest = ...;
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest);
}
void FinishWebRequest(IAsyncResult result)
{
HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse;
}
Salam pembuka
Semua orang sejauh ini salah, karena BeginGetResponse()
beberapa bekerja pada utas saat ini. Dari dokumentasi :
Metode BeginGetResponse memerlukan beberapa tugas pengaturan yang sinkron untuk diselesaikan (resolusi DNS, deteksi proxy, dan koneksi soket TCP, misalnya) sebelum metode ini menjadi tidak sinkron. Akibatnya, metode ini tidak boleh dipanggil pada utas antarmuka pengguna (UI) karena mungkin butuh waktu yang cukup lama (hingga beberapa menit tergantung pada pengaturan jaringan) untuk menyelesaikan tugas pengaturan sinkron awal sebelum pengecualian untuk kesalahan dilemparkan atau metode ini berhasil.
Jadi untuk melakukan ini dengan benar:
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);
}
Anda kemudian dapat melakukan apa yang perlu dilakukan dengan respons. Sebagai contoh:
HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.Write(body);
});
Sejauh ini cara termudah adalah dengan menggunakan TaskFactory.FromAsync dari TPL . Ini benar-benar beberapa baris kode ketika digunakan bersama dengan kata kunci async / menunggu baru :
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);
Jika Anda tidak dapat menggunakan kompiler C # 5 maka hal di atas dapat dilakukan dengan menggunakan metode Task.ContinueWith :
Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse,
request.EndGetResponse,
null)
.ContinueWith(task =>
{
var response = (HttpWebResponse) task.Result;
Debug.Assert(response.StatusCode == HttpStatusCode.OK);
});
Saya akhirnya menggunakan BackgroundWorker, itu pasti asinkron tidak seperti beberapa solusi di atas, ia menangani kembali ke utas GUI untuk Anda, dan sangat mudah dimengerti.
Ini juga sangat mudah untuk menangani pengecualian, karena mereka berakhir di metode RunWorkerCompleted, tetapi pastikan Anda membaca ini: Pengecualian yang tidak ditangani di BackgroundWorker
Saya menggunakan WebClient tetapi jelas Anda bisa menggunakan HttpWebRequest.GetResponse jika Anda mau.
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 telah berubah sejak banyak jawaban ini diposkan, dan saya ingin memberikan jawaban yang lebih mutakhir. Gunakan metode async untuk memulai Task
yang akan berjalan di utas latar:
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;
}
Untuk menggunakan metode async:
String response = await MakeRequestAsync("http://example.com/");
Memperbarui:
Solusi ini tidak berfungsi untuk aplikasi UWP yang menggunakan WebRequest.GetResponseAsync()
alih-alih WebRequest.GetResponse()
, dan tidak memanggil Dispose()
metode yang sesuai. @dragansr memiliki solusi alternatif yang baik yang mengatasi masalah ini.
WebRequest.GetResponseAsync()
dan StreamReader.ReadToEndAync()
perlu digunakan dan ditunggu.
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);
}
}