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

Jas*_*son 149 .net c# asynchronous httprequest

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

Jon*_*n B 123

使用 HttpWebRequest.BeginGetResponse()

HttpWebRequest webRequest;

void StartWebRequest()
{
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}

void FinishWebRequest(IAsyncResult result)
{
    webRequest.EndGetResponse(result);
}
Run Code Online (Sandbox Code Playgroud)

异步操作完成时调用回调函数.您至少需要EndGetResponse()从此功能调用.

  • BeginGetResponse对异步使用没有用.在尝试联系资源时似乎阻止了.尝试拔下网络电缆或给它一个格式错误的uri,然后运行此代码.相反,您可能需要在您提供的第二个线程上运行GetResponse. (15认同)
  • 您应该添加`webRequest.Proxy = null`以显着加快请求. (3认同)
  • @AshleyHenderson - 你能给我一个样品吗? (2认同)

xla*_*rsx 65

考虑到答案:

HttpWebRequest webRequest;

void StartWebRequest()
{
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}

void FinishWebRequest(IAsyncResult result)
{
    webRequest.EndGetResponse(result);
}
Run Code Online (Sandbox Code Playgroud)

您可以发送请求指针或任何其他对象,如下所示:

void StartWebRequest()
{
    HttpWebRequest webRequest = ...;
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest);
}

void FinishWebRequest(IAsyncResult result)
{
    HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse;
}
Run Code Online (Sandbox Code Playgroud)

问候

  • 对于没有超出"请求"变量范围的选项的+1,但您可以进行转换而不是使用"as"关键字.将抛出InvalidCastException,而不是混淆NullReferenceException (7认同)

Isa*_*sak 62

到目前为止,每个人都错了,因为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);
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过响应执行所需操作.例如:

HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
    var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
    Console.Write(body);
});
Run Code Online (Sandbox Code Playgroud)

  • 耶稣.这是一些丑陋的代码.为什么异步代码不可读? (15认同)
  • 你能不能使用await调用HttpWebRequest的GetResponseAsync方法(假设你的函数是异步的)?我对C#很新,所以这可能是完全的乱码... (2认同)
  • @Gatis有两个级别的异步调用 - wrapperAction.BeginInvoke()是对lambda表达式的第一个异步调用,它调用request.BeginGetResponse(),这是第二个异步调用.正如Isak所指出的,BeginGetResponse()需要一些同步设置,这就是他将其包装在另一个异步调用中的原因. (2认同)

Nat*_*lch 58

到目前为止,最简单的方法是使用TaskFactory.FromAsyncTPL.当与新的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);
Run Code Online (Sandbox Code Playgroud)

如果您不能使用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);
    });
Run Code Online (Sandbox Code Playgroud)


egg*_*ert 7

我最终使用了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();
Run Code Online (Sandbox Code Playgroud)


tro*_*man 5

自从发布了许多这些答案以来,.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;
}
Run Code Online (Sandbox Code Playgroud)

要使用异步方法:

String response = await MakeRequestAsync("http://example.com/");
Run Code Online (Sandbox Code Playgroud)

更新:

此解决方案不适用于WebRequest.GetResponseAsync()替代使用的UWP应用程序WebRequest.GetResponse(),并且它不会Dispose()在适当的地方调用方法.@dragansr有一个很好的替代解决方案来解决这些问题.

  • @tronman在异步等效项可用时在任务中运行阻止方法是强烈反对使用的反模式。尽管它确实解除了对调用线程的阻塞,但对于网络托管方案而言,它并没有做任何扩展,因为您只是将工作移至另一个线程,而不是使用IO完成端口来实现异步。 (4认同)
  • 需要明确的是,这是100%同步/阻塞代码。要使用异步,需要使用WebRequest.GetResponseAsync()和StreamReader.ReadToEndAync()。 (3认同)

dra*_*nsr 5

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);
}
Run Code Online (Sandbox Code Playgroud)