HOW TO:建立非同步的 HTTP 處理常式
更新:2007 年 11 月
本逐步解說描述如何建立非同步的 HTTP 處理常式。非同步的 HTTP 處理常式可讓您啟動外部處理序 (例如遠端伺服器的方法呼叫) 同時繼續處理,而不需等待外部處理序完成。
在處理非同步 HTTP 處理常式期間,ASP.NET 會將一般用於外部處理序的執行緒放回執行緒集區,直到處理常式接收到來自外部處理序的回呼 (Callback) 為止。這可防止執行緒封鎖並改善效能,因為一次只能執行有限數量的執行緒。如果許多使用者要求依賴外部處理序的同步 HTTP 處理常式,則作業系統會很快就用完執行緒,因為許多執行緒會被封鎖並等待外部處理序。
在下列範例中,會示範非同步 HTTP 處理常式如何處理 ASP.NET 應用程式中副檔名為 .SampleAsync 的檔案要求。這個範例顯示處理常式的程式碼,以及如何將 .SampleAsync 副檔名對應至 ASP.NET 中的處理常式。同時,也說明如何將 .SampleAsync 副檔名對應至 IIS 中的 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 副檔名對應至此處理常式。
如何將 .sample 副檔名對應至 IIS 中的 ASP.NET。
必要條件
若要完成這個逐步解說,您必須要有:
Visual Studio 或 Visual Web Developer。
可以使用 IIS 來執行的 ASP.NET 網站。
IIS 6.0 或 IIS 7.0。
建立非同步的 HTTP 處理常式類別
一開始,您將建立實作非同步處理常式的類別。
建立 HelloWorldAsyncHandler HTTP 處理常式類別
如果您所使用的網站還沒有 App_Code 資料夾,請在網站的根目錄下建立這個資料夾。
在 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 屬性 (Property)、建立 AsyncOperation 類別的新執行個體,以及呼叫 StartAsyncWork 方法。StartAsyncWork 方法接著會將 StartAsyncTask 委派 (Delegate) 加入至 ThreadPool 物件。當執行緒可用時會呼叫 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 中註冊處理常式
如果網站還沒有 Web.config 檔案,請在網站的根目錄下建立一個。
將下列反白顯示的標記加入至 Web.config 檔:
<configuration> <system.web> <httpHandlers> <add verb="*" path="*.SampleAsync" type="HelloWorldAsyncHandler"/> </httpHandlers> </system.web> </configuration>
組態項目會將 HelloWorldAsyncHandler 處理常式當做以 .SampleAsync 為結尾之要求的處理常式來註冊。
您可以使用 IIS 管理員,為 .SampleAsync 副檔名註冊應用程式副檔名對應。如需詳細資訊,請參閱 HOW TO:在 IIS 中設定 HTTP 處理常式副檔名。
在 IIS 7.0 中註冊自訂 HTTP 處理常式
在 IIS 7.0 中,應用程式能夠以傳統 (Classic) 或整合 (Integrated) 模式執行。在傳統模式下,處理要求的方式和在 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 中註冊處理常式
如果網站還沒有 Web.config 檔案,請在網站的根目錄下建立一個。
將下列反白顯示的項目加入至 Web.config 檔。
注意事項: 以正確的路徑取代 aspnet_isapi.dll 檔案的路徑。這個 .dll 檔案位於安裝 .NET Framework 的資料夾中。預設為 C:\WINDOWS\Microsoft.NET\Framework\version。
<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 副檔名對應到該處理常式。
注意事項: 因為您正在註冊的是自訂副檔名,所以 handlers 區段和 httpHandlers 區段中都要註冊處理常式。在傳統模式下,基於回溯相容性的考量,會使用 modules 屬性 (Attribute) 將處理常式會指定為 ISAPI 模組。ASP.NET ISAPI dll 的路徑是使用 scriptProcessor 屬性 (Attribute) 所指定。handlers 區段中需要 name 屬性 (Attribute)。
若要在以整合模式執行的 IIS 7.0 中註冊處理常式
如果網站還沒有 Web.config 檔案,請在網站的根目錄下建立一個。
將下列反白顯示的項目加入至 Web.config 檔。
<configuration> <system.webServer> <handlers> <add verb="*" path="*.SampleAsync" name="HelloWorldAsyncHandler" type="HelloWorldAsyncHandler"/> </handlers> </system.webServer> </configuration>
組態項目會依類別名稱註冊自訂處理常式,並將 .SampleAsync 副檔名對應到該處理常式。
注意事項: 註冊是在 handlers 區段中進行,而不是 httpHandlers 區段中。name 屬性 (Attribute) 是必要項。
測試自訂 HTTP 處理常式
一旦建立並註冊自訂 HTTP 處理常式之後,您就可以進行測試。
測試自訂 HTTP 處理常式
瀏覽至應用程式,並在瀏覽器中輸入以 .SampleAsync 結尾的 URL。
在 HelloWorldAsyncHandler 類別中定義的文字隨即顯示。
請參閱
工作
概念
IIS 5.0 和 6.0 的 ASP.NET 應用程式生命週期概觀