HttpWebRequest(.NET、C#)を非同期で使用するにはどうすればよいですか?
10 に答える
使用するHttpWebRequest.BeginGetResponse()
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
非同期操作が完了すると、コールバック関数が呼び出されます。少なくともEndGetResponse()
この関数から呼び出す必要があります。
最も簡単な方法は、 TPLからTaskFactory.FromAsyncを使用することです。新しい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 コンパイラを使用できない場合は、Task.ContinueWithメソッドを使用して上記を実行できます。
Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse,
request.EndGetResponse,
null)
.ContinueWith(task =>
{
var response = (HttpWebResponse) task.Result;
Debug.Assert(response.StatusCode == HttpStatusCode.OK);
});
答えを考える:
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);
});
私は 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 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);
}
}