ASP.NET使用管道模型(PipleLines)处理HTTP请求
大多數人認為ASP.NET僅僅只是頁面——使用模板來創建HTML頁面然后返回給瀏覽器。但是這僅僅只是ASP.NET使用HTTP管道模型處理WEB程序很小的一方面。管道模型是類似于Web Services的一種在服務器端處理ASP.NET頁面的框架技術。作為一名高級的ASP.NET的開發者,你必須清楚管道模型是如何工作的。這篇文章就是解釋和闡述HTTP管道模型是如何處理HTTP請求的。
一、管道對象模型
在System.Web的命名空間中處理HTTP的請求主要使用管道模型。一般的管道模型的結構如圖-1。在管道模型開始運行前,HTTP的請求首先被傳到HttpRuntime類的一個實例中,然后這個HttpRuntime的對象開始檢查請求并找出這個請求被發送到的那個應用程序(在管道程序看來,一個虛擬目錄就是一個應用程序)。然后管道模型就使用一個HttpApplicationFactory對象來找出或者創建一個HttpApplication對象來處理這個請求,一個HttpApplication可以包含一系列HTTP module對象(派生自IHttpModule接口)。HTTP modules作為一個過濾器可以在HTTP請求和響應信息穿過管道模型時檢查和修改這些信息的內容。然后HttpApplication對象就使用HTTP handler factory來找出或產生一個HTTP handler對象。HTTP handlers是HTTP通信的最后一步,它主要用于處理請求信息(request)和響應信息(response)。注:HTTP handlers和 handler factory分別派生自IHttpHandler接口和IHttpHandlerFactory接口。
?
圖-1
一個HttpApplication包括它的modules、handler在同一時刻只能處理一個Request請求。如果多重request請求同時到達一個相同的application時,多重HttpApplication對象將會被使用。
管道模型使用一個HttpContext對象去描述聲明每一個成對的request/response信息。這個對象在HttpApplicaiton和handler之間來回傳遞。每一個module也能訪問當前的HttpContext。HttpContext對象通過屬性來描述聲明HTTP的request和response信息(分別創建HttpRequest類和HttpResponse類的對象);同樣,HttpContext對象也能通過屬性來描述聲明安全信息和每一個call、session和application。圖-2展示了部分HttpContext類常用的屬性。
ASP.NET的HTTP管道模型是可擴展的,你可以實現自己的HTTP module、handler以及handler factory。你也可以直接繼承HttpApplication類。
?
?
?
| 屬性名 | 描述 |
| Application | 每一個application的request信息 |
| Application Instance | 正在處理request請求的Application對象 |
| Cache | 每一個application的緩存信息 |
| Handler | 正在處理request請求的Handler對象 |
| Items | 每一個request請求信息 |
| Request | HTTP request 信息 |
| Response | HTTP response 信息 |
| Server | Utility functions |
| Session | Per-user cross-request state |
| User | User information |
圖-2:HttpContext類的常用屬性
?
二、管道處理模型
ASP.NET的HTTP管道程序運行在IIS上來接收傳遞到進程中的request請求(被完整地傳送到其他的web服務器上)。當IIS接收到一個HTTP的request請求時,它首先會通過目標URL檢查文件的擴展名,如果文件的名被可執行代碼關聯,IIS就會調用這些代碼來處理request請求。文件的擴展名被映射成可執行代碼存儲在IIS的metabase標記中。當ASP.NET被安裝后,它會通過一個動態鏈接庫aspnet_isapi.dll將各種不同的文件擴展名添加到matabase標記中,包括.apx和.asmx。
當IIS收到一個某一個頁面文件發送的HTTP request請求時,它會調用aspnet_isapi.dll中的代碼:使用一個命名管道將IIS服務器上的inetinfo.exe發送來的request請求轉發到ASP.NET工作進程的一個實例:aspnet_wp.exe中。(在Windows Server 2003中, IIS 6.0kernel-mode模式的HTTP listener, 允許 request請求不通過inetinfo.exe而直接從操作系統轉發到工作進程。)工作進程使用一個HttpRuntime類的實例來處理request請求。圖-3 展示了完整的機制。
圖-3
HTTP管道程序在工作進程的實例中處理request請求。默認地,在某一時刻僅僅只能有一個工作進程工作(如果你的web服務器有多個CPU,你可以配置管道程序使用多個工作進程),這是一個在本地IIS上很重要的一個改變,它使用不同的工作進程主要為了隔離不同的application程序。同時管道程序的各個工作進程也完全地被AppDomain所隔離,你可以將AppDomain看作是進程中的一個子進程。管道程序向一個AppDomain中的所有虛擬目錄發送HTTP request請求。換句話說,每一個虛擬目錄被作為一個單獨的應用程序對待。這還有另外一個本地IIS值得注意的改變就是允許多個虛擬目錄成為同一個application的一部分。
?ASP.NET支持基于很多標準的循環工作進程,這些標準包括空閑時間、requests serviced的數量、requests隊列的數量以及物理內存的耗費量。全局.NET配置文件和machine配置文件初始化這些數值(好象processModel的元素)。當一個aspnet_wp.exe的實例 crosses one of these thresholds, aspnet_isapi.dll會運行一個新的工作進程并開始發送request請求。舊的實例在它處理完request請求后會自動終止。循環工作進程會提升可靠性通過在這些進程耗盡資源之前殺死它們。
三、HTTP Handlers
HTTP handlers 是一個繼承自IHttpHandler接口的簡單類,以下是該類的詳細代碼:
interface IHttpHandler
{
?// called to process request and generate response
?void ProcessRequest(HttpContext ctx);
?// called to see if handler can be pooled
?bool IsReuseable { get; }
}
Handlers也可以繼承自IHttpAsyncHandler接口,如果想要它們支持異步調用。
HttpApplicaiton對象會調用ProcessRequest方法,通過handler來處理當前的HTTP請求和產生response響應。在此期間IsReuseable屬性會被訪問為類測定hanlder是否可以被再使用。
圖-4的代碼實現了一個簡單的可以重用的HTTP句柄,它可以響應所有的request請求并返回當前的時間在一個XML標記中。你也可以使用使用HttpContext對象的response屬性來設置response信息的MIME屬性從而輸出自己的內容。using System; using System.Web; ? namespace Pipeline { ? ?public class TimeHandler : IHttpHandler ?{ ??? void ProcessRequest(HttpContext ctx) ??? { ????? ctx.Response.ContentType = "text/xml"; ????? ctx.Response.Write("<now>"); ????? ctx.Response.Write( ?????????????????? DateTime.Now.ToString()); ????? ctx.Response.Write("</now>"); ??? } ??? bool IsReuseable { get { return true; } } ?} }圖-4:TimeHandler
?
?
?
當HTTP handler類被實現時,它一定是被配置好的。配置會分為三個階段:第一、你應該將編譯好的代碼放到ASP.NET工作進程能夠找到的地方。一般地,已編譯好的.NET文件(一般是dll文件)應該位于web服務器的虛擬目錄下的bin文件夾中或位于全局編譯緩存中(GAC)。
第二步:在HTTP request請求到來時,你應該讓HTTP的管道程序執行你的代碼。你可以通過在你虛擬目錄下的web.config文件中去添加<httpHandlers>標簽。如下:
<configuration>
?<system.web>
?<httpHandlers>
?? <add verb="GET" path="*.time"
???? type="Pipeline.TimeHandler,
???? Pipeline"
?? />
?</httpHandlers>
?</system.web>
</configuration>
???以上的代碼可以作為附加的信息添加到.config的配置文件中。例如,這個web.config文件會告訴asp.net的HTTP管道程序處理所有.time文件的GET請求通過調用編譯文件中的Pipeline.TimeHandler類。
最后,這些.time文件的request請求會被IIS轉發到aspnet_isapi.dll,以便這些請求可以被管道程序第一時間處理。而且這些request請求也會在IIS的metabase中添加一個新的文件mapping映射。不過還有更簡單的方式,就是直接使用IIS管理臺將虛擬目錄的文件擴展名映射到Application的配置對話框中。如圖-5:
?
圖-5
?
?
除了實現已有的客戶端handler外,你也可以寫自己的handler factoriey。一個handler factoriey就是一個實現了IHttpHandlerFactory的類。Handler factiory的配置方法和普通handlers一樣,唯一不同的是原先web.config文件中的handler類都將被factory類所代替。
?
四、標準Handlers
????一些高級的ASP.NET技術,例如pages和Web Services都是通過頂層HTTP handler直接創建的。以下是通過.config文件來配置<httpHandlers>部分:
<httpHandlers> entries:
<httpHandlers>
?<add verb="*" path="*.ashx"
?type="System.Web.UI.SimpleHandlerFactory"
?/>
?<add verb="*" path="*.aspx"
??? type="System.Web.UI.PageHandlerFactory"
?? />
?? <add verb="*" path="*.asmx"
??? type="System.Web.Services.Protocols.
??? WebServiceHandlerFactory ... "
?? />
?</httpHandlers>
????第一個實體映射擴展名為.ashx的文件到SimpleHandlerFactory類,使得一個HTTP handler factory知道如何從.ashx源文件中安裝、編譯和執行一個IHttpHandler。然后結果對象就可以被HTTP管道程序直接使用。
?? ?圖-6展示了使用.ashx文件重寫的一個TimeHandler例子。@WebHandler部分告訴SimpleHandlerFactory Http handler類的名字在源代碼編譯后。這么做最大的好處就在它的配置非常簡單:你只需要將所有的.ashx文件拷貝到虛擬目錄下,而不需要再創建或修改web.config文件或在.NET安裝完后再更新IIS。
<%@ WebHandler language="C#" ??? class="Pipeline.TimeHandler" %> ? using System; using System.Web; ? namespace Pipeline { ? ?public class TimeHandler : IHttpHandler ?{ ??? void ProcessRequest(HttpContext ctx) ??? { ????? // set response message MIME type ????? ctx.Response.ContentType = "text/xml"; ????? // write response message body ????? ctx.Response.Write("<now>"); ????? ctx.Response.Write( ?????????????????? DateTime.Now.ToString()); ????? ctx.Response.Write("</now>"); ??? } ??? bool IsReuseable { get { return true; } } ?} }圖-6
?
第二個<httpHandlers>部分的實體將.aspx文件擴展名映射到PageHandlerFactory類,主要為了讓HTTP handler factory知道如何將.aspx的源代碼編譯成一個System.Web.UI.Page-derived類。這個Page類實現了IHttpHandler借口,因此最終對象可以被HTTP管道程序直接使用。
第三個實體將.asmx的擴展名映射到WebServiceHandlerFactory類,這么做主要為了讓一個HTTP handler factory知道如何將一個.asmx文件中的源代碼編譯并實例化。然后它會綁定一個標準的HTTP handler(默認的為SyncSessionlessHandler)實例并使用反射機制將SOAP信息轉化成方法的調用參數。最后,最終對象就可以被HTTP管道程序直接使用了。
???這里需要值得注意的是PageHandlerFactory、WebServiceHandlerFactory和SimpleHandlerFactory類并不能在每一個request請求上都編譯.aspx、.asmx和.ashx文件。作為替代,這些編譯好的代碼會被緩存在ASP.NET安裝目錄下的臨時文件中。并且當源代碼改變時,這些代碼僅僅只會被編譯一次。
?
五、HTTP Modules
????HTTP handlers是HTTP通信的最終部分。Handler類的實例專門用來接收HTTP請求并產生response響應。HTTP modules作為過濾器在request和response信息穿過管道程序時處理它們(可以是檢查并修改這些信息的內容)。管道程序可以用這些HTTP modules特別安全地實現自己的底層處理程序。
HTTP modules 是實現IHttpModule接口的簡單類:
interface IHttpModule
{
?// called to attach module to app events
?void Init(HttpApplication app);
?// called to clean up
?void Dispose()
}
當module被第一次創建時,Init方法會被HttpApplication對象調用,它可以通過HttpApplication對象將一個或多個事件handlers綁定到事件上。
圖-7的代碼展示了一個HTTP module如何處理HttpApplication對象的BeginRequest和EndRequest事件。在這個例子中,Init方法使用了常見的.NET技術將module的OnBeginRequst和OnEndRequest作為事件句柄綁定到HttpApplication對象上。OnBeginRequest主要作用是獲取存儲當前時間并將時間存放到變量start中。而OnEndRequest主要作用是計算OnBeginRequest和OnEndRequest之間的運行時間差并將這段時間添加到客戶端HTTP header中。
?
OnEndRequest方法的最大優勢在于第一個參數實際上傳遞的module綁定的HttpApplication對象。并且當前的信息會做為一個HttpApplication對象的屬性(Http-Context)被OnEndRequest方法使用。
using System; using System.Web; ? namespace Pipeline { ? ?public class ElapsedTimeModule : IHttpModule ?{ ??? DateTime start; ??? public void Init(HttpApplication app) ??? { ????? // register for pipeline events ????? app.BeginRequest += ????????? new EventHandler(this.OnBeginRequest); ????? app.EndRequest += ????????? new EventHandler(this.OnEndRequest); ??? } ??? public void Dispose() {} ? ??? public void OnBeginRequest(object o, ?????????????????????????????? EventArgs args) ??? { ????? // record time when request started ????? start = DateTime.Now; ??? } ? ??? public void OnEndRequest(object o, ???????????????????????????? EventArgs args) ??? { ????? // measure elapsed time ????? TimeSpan elapsed = ????????????? DateTime.Now - start; ? ????? // get access to app and context ????? HttpApplication app = ????????????? (HttpApplication) o; ????? HttpContext ctx = app.Context; ? ????? // add custom header to HTTP response ????? ctx.Response.AppendHeader( ?????????????????? "ElapsedTime", ?????????????????? elapsed.ToString()); ??? } ?} }圖-7
在一個HTTP module類被實現和使用之間,必須對它做一定的配置。配置的步驟包括兩步。首先你應該將編譯好的module代碼放到web服務器上的站點目錄下的bin文件夾中以至于ASP.NET的工作進程能夠找到它。
??? 然后你應該在你的web.config文件中添加<httpModules>部分,如下:
<configuration>
?<system.web>
?<httpModules>
?? <add
??? name="Elapsed"
??? type="Pipeline.ElapsedTimeModule, Pipeline"
?? />
?</httpModules>
?</system.web>
</configuration>
???這個例子中,web.config文件會告訴ASP.NET的HTTP管道程序將Pipeline.ElapsedTimeModule綁定到每一個HttpApplication對象用來處理這個虛擬目錄下的service請求。
總結
以上是生活随笔為你收集整理的ASP.NET使用管道模型(PipleLines)处理HTTP请求的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis与关系型数据库的同步问题
- 下一篇: asp.net ajax控件工具集 Au