如何在没有回调的情况下异步地发出web请求

本文关键字:异步 请求 web 情况下 回调 | 更新日期: 2023-09-27 17:54:25

我有一个在互联网上与第三方对话的服务,如果我想扩大规模,我不希望我的线程在处理新请求时被阻塞等待响应。解决这个问题的一种方法是使用httpRequest。BeginGetResponse方法,并传入回调。问题是这个方法立即返回,我没有任何东西可以返回给我的服务的调用者。一旦进入回调,它就不能做任何有用的事情了。

我想要的是这样的东西

public string CallServiceNonBlocking(string url) {
   var httpRequest = (HttpWebRequest)WebRequest.Create(url);
   HttpResponse res = httpRequest.FetchNonBlocking(); //this does the work but my thread should  be returned to the asp.net thread pool to do other work rather than blocking here
   return res.GetValue(); 
}

注意,我没有。net 4.5,只有。net 4。但是作为参考,我可以看到两个版本的解决方案吗?

如何在没有回调的情况下异步地发出web请求

使用。net 4.5和c# 5.0 async/await编写此代码非常容易。

你也可以让它与。net 4.0/c# 4.0异步工作,如果你需要的话。您需要从AsyncController导出控制器并使用AsyncManager。在"在ASP中使用异步控制器"中描述了基本步骤。净MVC"。

在这种情况下,您的代码可能看起来像这样(未经测试):
static public Task<WebResponse> GetResponseTapAsync(this WebRequest request)
{
    return Task.Factory.FromAsync(
         (asyncCallback, state) =>
             request.BeginGetResponse(asyncCallback, state),
         (asyncResult) =>
             request.EndGetResponse(asyncResult), null);
}
// ...
public class YourController : AsyncController
{
    public void YourMethodAsyc(string url)
    {
        AsyncManager.OutstandingOperations.Increment();
        var request = (HttpWebRequest)WebRequest.Create(url);
        request.GetResponseTapAsync().ContinueWith(responseTask =>
        {
            try
            {
                var stream = responseTask.Result.GetResponseStream();
                using (var streamReader = new StreamReader(stream))
                {
                    // still blocking here, see notes below
                    var data = streamReader.ReadToEnd();
                    AsyncManager.Parameters["data"] = data;
                }
            }
            finally
            {
                AsyncManager.OutstandingOperations.Decrement();
            }
        }, TaskScheduler.FromCurrentSynchronizationContext());
    }
    public ActionResult YourMethodCompleted(string data)
    {
        return View("Data", new ViewModel
        {
            Data = data
        });
    }
}

您可以进一步实现ReadToEndAsync(因为它在。net 4.0中不存在),但您将无法使用using。查看更多详细信息。

理想情况下,如果你需要瞄准ASP。.NET MVC使用。NET 4.0,但在c# 5.0中使用VS2012+开发,您仍然可以使用async/await,微软提供了Microsoft.Bcl.Async库。那么你的代码可能看起来像这样:

public class YourController : AsyncController
{
    async Task<string> YourMethodAsyncImpl(string url)
    {
        var request = (HttpWebRequest)WebRequest.Create(url);
        using (var response = await request.GetResponseAsync()
        using (var streamReader = new StreamReader(response.GetResponseStream())
            return await streamReader.ReadToEndAsync();
    }
    public void YourMethodAsyc(string url)
    {
        AsyncManager.OutstandingOperations.Increment();
        YourMethodAsyncImpl(url).ContinueWith(resultTask =>
        {
            try
            {
                AsyncManager.Parameters["data"] = resultTask.Result;
            }
            finally
            {
                AsyncManager.OutstandingOperations.Decrement();
            }
        }, TaskScheduler.FromCurrentSynchronizationContext());
    }
    public ActionResult YourMethodCompleted(string data)
    {
        return View("Data", new ViewModel
        {
            Data = data
        });
    }
}

我会使用微软的响应式框架。(NuGet Rx-Main)

那就超级简单了。

像这样定义你的函数:

public IObservable<string> CallServiceNonBlocking(string url)
{
    return Observable.Start(() =>
    {
        var httpRequest = (HttpWebRequest)WebRequest.Create(url);
        HttpResponse res = httpRequest.FetchBlocking();
        return res.GetValue(); 
    });
}

然后像这样调用它:

CallServiceNonBlocking(url)
    .Subscribe(response =>
    {
        /* handle response here */
    });

很容易使用ObserveOn使订阅在UI线程或当前上下文等上运行