代码之家  ›  专栏  ›  技术社区  ›  Jason

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

  •  147
  • Jason  · 技术社区  · 16 年前

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

    8 回复  |  直到 16 年前
        1
  •  127
  •   Jon B    14 年前

    使用 HttpWebRequest.BeginGetResponse()

    HttpWebRequest webRequest;
    
    void StartWebRequest()
    {
        webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
    }
    
    void FinishWebRequest(IAsyncResult result)
    {
        webRequest.EndGetResponse(result);
    }
    

    异步操作完成时调用回调函数。你至少需要打个电话 EndGetResponse() 从这个函数。

        2
  •  68
  •   Nathan Baulch    11 年前

    到目前为止,最简单的方法是使用 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);
    

    如果不能使用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);
        });
    
        3
  •  67
  •   xlarsx    14 年前

    考虑到答案:

    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;
    }
    

        4
  •  64
  •   Isak    12 年前

    到目前为止,每个人都错了,因为 BeginGetResponse() 在当前线程上执行一些操作。从 documentation :

    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);
    });
    
        5
  •  9
  •   dragansr    7 年前
    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);
    }
    
        6
  •  8
  •   Community CDub    8 年前

    我最终使用了BackgroundWorker,它绝对是异步的,与上面的一些解决方案不同,它为您处理返回GUI线程的问题,并且非常容易理解。

    处理异常也很容易,因为它们最终会出现在RunWorkerCompleted方法中,但请确保阅读以下内容: Unhandled exceptions in 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();
    
        7
  •  7
  •   tronman    7 年前

    自从这些答案中的许多被发布后,.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() Dispose() 方法(如适用)@dragansr有一个很好的替代解决方案来解决这些问题。

        8
  •  3
  •   Sten Petrov    12 年前
    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);
            } 
        }