C# 如何异步使用 HttpWebRequest (.NET)?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/202481/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use HttpWebRequest (.NET) asynchronously?
提问by Jason
How can I use HttpWebRequest (.NET, C#) asynchronously?
如何异步使用 HttpWebRequest (.NET, C#)?
采纳答案by Jon B
Use HttpWebRequest.BeginGetResponse()
用 HttpWebRequest.BeginGetResponse()
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
The callback function is called when the asynchronous operation is complete. You need to at least call EndGetResponse()
from this function.
异步操作完成时调用回调函数。你至少需要EndGetResponse()
从这个函数调用。
回答by xlarsx
Considering the answer:
考虑到答案:
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
You could send the request pointer or any other object like this:
您可以发送请求指针或任何其他对象,如下所示:
void StartWebRequest()
{
HttpWebRequest webRequest = ...;
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest);
}
void FinishWebRequest(IAsyncResult result)
{
HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse;
}
Greetings
你好
回答by Sten Petrov
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);
}
}
回答by Isak
Everyone so far has been wrong, because BeginGetResponse()
does some work on the current thread. From the documentation:
到目前为止,每个人都错了,因为BeginGetResponse()
在当前线程上做了一些工作。从文档:
The BeginGetResponse method requires some synchronous setup tasks to complete (DNS resolution, proxy detection, and TCP socket connection, for example) before this method becomes asynchronous. As a result, this method should never be called on a user interface (UI) thread because it might take considerable time (up to several minutes depending on network settings) to complete the initial synchronous setup tasks before an exception for an error is thrown or the method succeeds.
BeginGetResponse 方法需要在此方法变为异步之前完成一些同步设置任务(例如 DNS 解析、代理检测和 TCP 套接字连接)。因此,不应在用户界面 (UI) 线程上调用此方法,因为在引发错误异常之前完成初始同步设置任务可能需要相当长的时间(最多几分钟,取决于网络设置)或方法成功。
So to do this right:
所以要正确地做到这一点:
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);
}
You can then do what you need to with the response. For example:
然后,您可以对响应执行您需要的操作。例如:
HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.Write(body);
});
回答by eggbert
I ended up using BackgroundWorker, it is definitely asynchronous unlike some of the above solutions, it handles returning to the GUI thread for you, and it is very easy to understand.
我最终使用了BackgroundWorker,与上述一些解决方案不同,它绝对是异步的,它为您处理返回到GUI线程,并且非常容易理解。
It is also very easy to handle exceptions, as they end up in the RunWorkerCompleted method, but make sure you read this: Unhandled exceptions in BackgroundWorker
处理异常也很容易,因为它们最终出现在 RunWorkerCompleted 方法中,但请务必阅读以下内容:BackgroundWorker 中的未处理异常
I used WebClient but obviously you could use HttpWebRequest.GetResponse if you wanted.
我使用了 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();
回答by Nathan Baulch
By far the easiest way is by using TaskFactory.FromAsyncfrom the TPL. It's literally a couple of lines of code when used in conjunction with the new async/awaitkeywords:
到目前为止,最简单的方法是使用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);
If you can't use the C#5 compiler then the above can be accomplished using the Task.ContinueWithmethod:
如果您不能使用 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);
});
回答by tronman
.NET has changed since many of these answers were posted, and I'd like to provide a more up-to-date answer. Use an async method to start a Task
that will run on a background thread:
自从发布了许多这些答案以来,.NET 已经发生了变化,我想提供一个更新的答案。使用 async 方法启动Task
将在后台线程上运行的 a:
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;
}
To use the async method:
使用异步方法:
String response = await MakeRequestAsync("http://example.com/");
Update:
更新:
This solution does not work for UWP apps which use WebRequest.GetResponseAsync()
instead of WebRequest.GetResponse()
, and it does not call the Dispose()
methods where appropriate. @dragansr has a good alternative solution that addresses these issues.
此解决方案不适用于使用WebRequest.GetResponseAsync()
代替 的UWP 应用WebRequest.GetResponse()
,并且不会Dispose()
在适当的情况下调用方法。@dragansr 有一个很好的替代解决方案来解决这些问题。
回答by dragansr
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);
}