如何异步使用HttpWebRequest(.NET,C#)?

有帮助吗?

解决方案

使用 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套接字连接,   例如)此方法变为异步之前。结果是,   永远不应该在用户界面(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);
});

到目前为止,最简单的方法是使用来自 TPL 的TaskFactory.FromAsync 。当与新的 async / await <一起使用时,它实际上是几行代码。 / a>关键字:

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();
自从发布了许多这些答案以来,.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/");

<强>更新

此解决方案不适用于使用 WebRequest.GetResponseAsync()而非 WebRequest.GetResponse()的UWP应用,并且它不会调用 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);
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top