c# - 使用 IHttpAsyncHandler 异步调用 WebService

标签 c# .net asp.net asynchronous ihttpasynchandler

这是基本设置。我们有一个 ASP.Net WebForms 应用程序,其页面包含一个需要访问外部 Web 服务的 Flash 应用程序。由于(我推测的安全性)Flash 的限制(别问我,我根本不是 Flash 专家),我们无法直接从 Flash 连接到 Web 服务。解决方法是在 ASP.Net 中创建一个 Flash 应用程序将调用的代理,后者将依次调用 WebService 并将结果转发回 Flash 应用程序。

虽然该网站的流量非常高,但问题是,如果 Web 服务完全挂起,那么 ASP.Net 请求线程将开始备份,这可能会导致严重的线程不足。为了解决这个问题,我决定使用 IHttpAsyncHandler这是为这个确切目的而设计的。在其中,我将使用 WebClient 异步调用 Web 服务并将响应转发回来。网上关于如何正确使用 IHttpAsyncHandler 的示例很少,所以我只想确保我没有做错。我的用法基于此处显示的示例:http://msdn.microsoft.com/en-us/library/ms227433.aspx

这是我的代码:

internal class AsynchOperation : IAsyncResult
{
    private bool _completed;
    private Object _state;
    private AsyncCallback _callback;
    private readonly HttpContext _context;

    bool IAsyncResult.IsCompleted { get { return _completed; } }
    WaitHandle IAsyncResult.AsyncWaitHandle { get { return null; } }
    Object IAsyncResult.AsyncState { get { return _state; } }
    bool IAsyncResult.CompletedSynchronously { get { return false; } }

    public AsynchOperation(AsyncCallback callback, HttpContext context, Object state)
    {
        _callback = callback;
        _context = context;
        _state = state;
        _completed = false;
    }

    public void StartAsyncWork()
    {
        using (var client = new WebClient())
        {
            var url = "url_web_service_url";
            client.DownloadDataCompleted += (o, e) =>
            {
                if (!e.Cancelled && e.Error == null)
                {
                    _context.Response.ContentType = "text/xml";
                    _context.Response.OutputStream.Write(e.Result, 0, e.Result.Length);
                }
                _completed = true;
                _callback(this);
            };
            client.DownloadDataAsync(new Uri(url));
        }
    }
}

public class MyAsyncHandler : IHttpAsyncHandler
{
    public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
        var asynch = new AsynchOperation(cb, context, extraData);
        asynch.StartAsyncWork();
        return asynch;
    }

    public void EndProcessRequest(IAsyncResult result)
    {
    }

    public bool IsReusable
    {
        get { return false; }
    }

    public void ProcessRequest(HttpContext context)
    {
    }
}

现在一切正常,我认为它应该可以解决问题,但我不是 100% 确定。此外,创建我自己的 IAsyncResult 似乎有点矫枉过正,我只是想知道是否有一种方法可以利用从 Delegate.BeginInvoke 或其他方法返回的 IAsyncResult。欢迎任何反馈。谢谢!!

最佳答案

哇,是的,如果您使用的是 .NET 4.0,则可以通过利用任务并行库使这一切变得更容易/更清晰。检查一下:

public class MyAsyncHandler : IHttpAsyncHandler
{
    public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
        // NOTE: the result of this operation is void, but TCS requires some data type so we just use bool
        TaskCompletionSource<bool> webClientDownloadCompletionSource = new TaskCompletionSource<bool>();

        WebClient webClient = new WebClient())
        HttpContext currentHttpContext = HttpContext.Current;

        // Setup the download completed event handler
        client.DownloadDataCompleted += (o, e) =>
        {
            if(e.Cancelled)
            {
                // If it was canceled, signal the TCS is cacnceled
                // NOTE: probably don't need this since you have nothing canceling the operation anyway
                webClientDownloadCompletionSource.SetCanceled();
            }
            else if(e.Error != null)
            {
                // If there was an exception, signal the TCS with the exception
                webClientDownloadCompletionSource.SetException(e.Error);
            }
            else
            {
                // Success, write the response
                currentHttpContext.Response.ContentType = "text/xml";
                currentHttpContext.Response.OutputStream.Write(e.Result, 0, e.Result.Length);

                // Signal the TCS that were done (we don't actually look at the bool result, but it's needed)
                taskCompletionSource.SetResult(true);
            }
        };

        string url = "url_web_service_url";

        // Kick off the download immediately
        client.DownloadDataAsync(new Uri(url));

        // Get the TCS's task so that we can append some continuations
        Task webClientDownloadTask = webClientDownloadCompletionSource.Task;

        // Always dispose of the client once the work is completed
        webClientDownloadTask.ContinueWith(
            _ =>
            {
                client.Dispose();
            },
            TaskContinuationOptions.ExecuteSynchronously);

        // If there was a callback passed in, we need to invoke it after the download work has completed
        if(cb != null)
        {
            webClientDownloadTask.ContinueWith(
               webClientDownloadAntecedent =>
               {
                   cb(webClientDownloadAntecedent);
               },
               TaskContinuationOptions.ExecuteSynchronously);
         }

        // Return the TCS's Task as the IAsyncResult
        return webClientDownloadTask;
    }

    public void EndProcessRequest(IAsyncResult result)
    {
        // Unwrap the task and wait on it which will propagate any exceptions that might have occurred
        ((Task)result).Wait();
    }

    public bool IsReusable
    {
        get 
        { 
            return true; // why not return true here? you have no state, it's easily reusable!
        }
    }

    public void ProcessRequest(HttpContext context)
    {
    }
}

关于c# - 使用 IHttpAsyncHandler 异步调用 WebService,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6388793/

相关文章:

c# - 在包含对象作为值的字典上使用 foreach 时出错

.net - 如何确定当前用户是否通过 .NET 中的终端服务登录?

c# - 用于在自定义缓冲区中接收以避免在每个帧/消息上创建新字节数组的 NetMQ API 是什么?

c# - ASP.NET Ajax Accordion header 事件

asp.net - 为什么我的 asp.net 缓存抛出异常?

c# - 'deaden' 可能是一个asp.net超链接吗?

c# - 在调试中禁用应用程序洞察

c# - 使用基类中的 static void Main() 方法作为程序的入口点

.net - 什么是 .net (C#) 的免费(开源)BLAS/LAPACK 库?

c# - 是否有类型安全的方法来引用属性中的类/属性?