如何:创建异步 HTTP 处理程序

更新:2007 年 11 月

本演练阐释如何创建异步 HTTP 处理程序。使用异步 HTTP 处理程序,您可以在启动一个外部进程(如对远程服务器的方法调用)的同时继续执行该处理程序。该处理程序可以继续运行,而不必等待外部进程完成。

在异步 HTTP 处理程序的处理过程中,ASP.NET 将通常用于外部进程的线程放回线程池中,直到处理程序接收到来自外部进程的回调。由于只能同时执行有限数量的线程,因此这样可以避免阻止线程并改善性能。如果许多用户都在请求依赖于外部进程的同步 HTTP 处理程序,那么操作系统可能很快就会用完所有线程,因为大量线程被阻止,正在等待外部进程。

本演练中的示例演示了一个异步 HTTP 处理程序,该处理程序在 ASP.NET 应用程序中处理对文件扩展名为 .SampleAsync 的文件的请求。该示例显示该处理程序的代码,然后演示如何在 ASP.NET 中将 .SampleAsync 扩展名映射到该处理程序。该示例还演示如何在 IIS 中将 .SampleAsync 扩展名映射到 ASP.NET,以便 IIS 将以 .SampleAsync 结尾的请求转发到 ASP.NET。

有关 ASP.NET 运行库如何与 IIS 6.0 交互的更多信息,请参见 IIS 5.0 和 6.0 的 ASP.NET 应用程序生命周期概述。有关 ASP.NET 与 IIS 7.0 集成的更多信息,请参见 IIS 7.0 的 ASP.NET 应用程序生命周期概述

本演练涉及以下任务:

  • 如何创建 HTTP 处理程序类的代码。该类必须实现 ProcessRequest 方法和 IsReusable 属性。

  • 如何在 Web.config 文件中注册处理程序并将 .SampleAsync 文件扩展名映射到该处理程序。

  • 如何在 IIS 中将 .sample 文件扩展名映射到 ASP.NET。

先决条件

若要完成本演练,您需要:

  • Visual Studio 或 Visual Web Developer。

  • 可以使用 IIS 运行的 ASP.NET 网站。

  • IIS 6.0 或 IIS 7.0。

创建异步 HTTP 处理程序类

首先,将创建一个实现异步处理程序的类。

创建 HelloWorldAsyncHandler HTTP 处理程序类

  1. 如果要使用的网站还没有 App_Code 文件夹,请在该站点的根目录下创建这样一个文件夹。

  2. 在 App_Code 目录中,创建一个名为 HelloWorldAsyncHandler 的类,并将下面的代码添加到类文件中:

    Imports Microsoft.VisualBasic
    Imports System.Web
    Imports System.Threading
    
    Public Class HelloWorldAsyncHandler
        Implements IHttpAsyncHandler
    
        Public ReadOnly Property IsReusable() As Boolean Implements System.Web.IHttpHandler.IsReusable
            Get
                Return False
            End Get
        End Property
    
        Public Function BeginProcessRequest( _
            ByVal context As System.Web.HttpContext, _
            ByVal cb As System.AsyncCallback, _
            ByVal extraData As Object) _
            As System.IAsyncResult _
            Implements System.Web.IHttpAsyncHandler.BeginProcessRequest
            context.Response.Write("<p>Begin IsThreadPoolThread is " _
                & Thread.CurrentThread.IsThreadPoolThread & "</p>" & vbCrLf)
            Dim asynch As New AsynchOperation(cb, context, extraData)
            asynch.StartAsyncWork()
            Return asynch
        End Function
    
        Public Sub EndProcessRequest(ByVal result As _
             System.IAsyncResult) _
             Implements System.Web.IHttpAsyncHandler.EndProcessRequest
        End Sub
    
        Public Sub ProcessRequest(ByVal context _
                As System.Web.HttpContext) _
                Implements System.Web.IHttpHandler.ProcessRequest
            Throw New InvalidOperationException()
        End Sub
    End Class
    
    Class AsynchOperation
        Implements IAsyncResult
        Private _completed As Boolean
        Private _state As [Object]
        Private _callback As AsyncCallback
        Private _context As HttpContext
    
        ReadOnly Property IsCompleted() As Boolean _
                Implements IAsyncResult.IsCompleted
            Get
                Return _completed
            End Get
        End Property
    
        ReadOnly Property AsyncWaitHandle() As WaitHandle _
                Implements IAsyncResult.AsyncWaitHandle
            Get
                Return Nothing
            End Get
        End Property
    
        ReadOnly Property AsyncState() As [Object] _
                Implements IAsyncResult.AsyncState
            Get
                Return _state
            End Get
        End Property
    
        ReadOnly Property CompletedSynchronously() As Boolean _
                Implements IAsyncResult.CompletedSynchronously
            Get
                Return False
            End Get
        End Property
    
        Public Sub New(ByVal callback As AsyncCallback, _
                ByVal context As HttpContext, _
                ByVal state As [Object])
            _callback = callback
            _context = context
            _state = state
            _completed = False
        End Sub
    
        Public Sub StartAsyncWork()
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf StartAsyncTask), Nothing)
    
        End Sub
    
        Private Sub StartAsyncTask(ByVal workItemState As [Object])
            _context.Response.Write("<p>Completion IsThreadPoolThread is " & Thread.CurrentThread.IsThreadPoolThread & "</p>" & vbCrLf)
    
            _context.Response.Write("Hello World from Async Handler!")
            _completed = True
            _callback(Me)
    
        End Sub 'StartAsyncTask
    End Class 'AsynchOperation
    
    using System;
    using System.Web;
    using System.Threading;
    
    class HelloWorldAsyncHandler : IHttpAsyncHandler
    {
        public bool IsReusable { get { return false; } }
    
        public HelloWorldAsyncHandler()
        {
        }
        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
        {
            context.Response.Write("<p>Begin IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n");
            AsynchOperation asynch = new AsynchOperation(cb, context, extraData);
            asynch.StartAsyncWork();
            return asynch;
        }
    
        public void EndProcessRequest(IAsyncResult result)
        {
        }
    
        public void ProcessRequest(HttpContext context)
        {
            throw new InvalidOperationException();
        }
    }
    
    class AsynchOperation : IAsyncResult
    {
        private bool _completed;
        private Object _state;
        private AsyncCallback _callback;
        private 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()
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(StartAsyncTask), null);
        }
    
        private void StartAsyncTask(Object workItemState)
        {
    
            _context.Response.Write("<p>Completion IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n");
    
            _context.Response.Write("Hello World from Async Handler!");
            _completed = true;
            _callback(this);
        }
    }
    

    该代码实现 BeginProcessRequest 方法。该方法向当前的 HttpContext 对象的 Response 属性中写入一个字符串,创建一个 AsyncOperation 类的新实例,然后调用 StartAsyncWork 方法。然后,StartAsyncWork 方法向 ThreadPool 对象添加 StartAsyncTask 委托。当有线程可用时,会调用 StartAsyncTask 方法,该方法将另外一个字符串写出到 Response 属性。然后通过调用 AsyncCallback 委托,完成该任务。

在 IIS 6.0 中注册自定义 HTTP 处理程序

创建完自定义 HTTP 处理程序类后,必须在应用程序的 Web.config 文件中注册它。这样,ASP.NET 在收到对 URL 以 .SampleAsync 结尾的资源的请求时,可以找到该处理程序。

注册处理程序的过程会有所不同,取决于使用的是 IIS 6.0 还是 IIS 7.0。本节介绍如何在 IIS 6.0 中注册处理程序。下一节介绍如何在 IIS 7.0 中注册处理程序。

在 IIS 6.0 中注册处理程序

  1. 如果网站还没有 Web.config 文件,请在该站点的根目录下创建一个这样的文件。

  2. 将下面突出显示的标记添加到该 Web.config 文件中:

    <configuration>
      <system.web>
        <httpHandlers>      <add verb="*" path="*.SampleAsync"         type="HelloWorldAsyncHandler"/>    </httpHandlers>
      </system.web>
    </configuration>
    

    配置元素将 HelloWorldAsyncHandler 处理程序注册为对以 .SampleAsync 结尾的请求的处理程序。

  3. 使用 IIS 管理器为 .SampleAsync 文件扩展名注册一个应用程序扩展名映射。有关更多信息,请参见如何:在 IIS 中配置 HTTP 处理程序扩展名

在 IIS 7.0 中注册自定义 HTTP 处理程序

在 IIS 7.0 中,应用程序可以在经典模式或集成模式下运行。在经典模式下,请求的处理方式与在 IIS 6.0 中大致相同。在集成模式下,IIS 7.0 使用管道(管道使其可以与 ASP.NET 共享请求、模块和其他功能)来管理请求。

对于 IIS 7.0,处理程序注册需要在 Web.config 文件或 IIS 管理器中注册处理程序。因为在 IIS 7.0 中进行集中管理,所以应用程序的 Web.config 文件中的更改会在该应用程序的 IIS 管理器接口中反映出来,反之亦然。在后面的过程中,处理程序是在 Web.config 文件中注册的。

对于在经典模式和集成模式下运行的 IIS 7.0,注册处理程序的过程是不相同的。请根据您要使用的 IIS 模式执行相应的过程。

在运行于经典模式下的 IIS 7.0 中注册处理程序

  1. 如果网站还没有 Web.config 文件,请在该站点的根目录下创建一个这样的文件。

  2. 将下面突出显示的元素添加到 Web.config 文件中。

    ms227433.alert_note(zh-cn,VS.90).gif说明:

    为 aspnet_isapi.dll 文件替换正确的路径。该 .dll 文件位于安装 .NET Framework 的文件夹中。默认情况下,此文件夹为 C:\WINDOWS\Microsoft.NET\Framework\版本。

    <configuration>
      <system.web>
        <httpHandlers>      <add verb="*" path="*.SampleAsync"         type="HelloWorldAsyncHandler"/>    </httpHandlers>
      </system.web>
      <system.webServer>
        <handlers>      <add  verb="*" path="*.SampleAsync"        name="HelloWorldAsyncHandler"        type="HelloWorldAsyncHandler"        modules="IsapiModule"/>        scriptProcessor="%path%\aspnet_isapi.dll"    </handlers>
      </system.webServer>
    </configuration>
    

    配置元素按类名注册自定义处理程序,并将 .SampleAsync 文件扩展名映射到该处理程序。

    ms227433.alert_note(zh-cn,VS.90).gif说明:

    由于要注册自定义文件扩展名,因此在 handlers 节和 httpHandlers 节中都要注册该处理程序。为了向后兼容,在经典模式下要使用 modules 属性将该处理程序指定为 ISAPI 模块。使用 scriptProcessor 属性指定 ASP.NET ISAPI dll 的路径。name 属性在 handlers 节中是必需的。

在运行于集成模式下的 IIS 7.0 中注册处理程序

  1. 如果网站还没有 Web.config 文件,请在该站点的根目录下创建一个这样的文件。

  2. 将下面突出显示的元素添加到 Web.config 文件中。

    <configuration>
      <system.webServer>
        <handlers>      <add verb="*" path="*.SampleAsync"        name="HelloWorldAsyncHandler"        type="HelloWorldAsyncHandler"/>    </handlers>
      </system.webServer>
    </configuration>
    

    配置元素按类名注册自定义处理程序,并将 .SampleAsync 文件扩展名映射到该处理程序。

    ms227433.alert_note(zh-cn,VS.90).gif说明:

    注册在 handlers 节中进行,而不是在 httpHandlers 节中进行。需要 name 属性。

测试自定义 HTTP 处理程序

创建并注册自定义 HTTP 处理程序后,可以对该处理程序进行测试。

测试自定义 HTTP 处理程序

  • 浏览至应用程序,然后在浏览器中输入一个以 .SampleAsync 结尾的 URL。

    将显示在 HelloWorldAsyncHandler 类中定义的文本。

请参见

任务

演练:创建同步 HTTP 处理程序

概念

IIS 5.0 和 6.0 的 ASP.NET 应用程序生命周期概述

其他资源

HTTP 处理程序介绍