本文隻是對HttpModule和HttpHandler做最初步的了解。非菜鳥級别人士可直接無視。
ASP.NET 中Http的請求流程
1. 使用者發出的用戶端請求達到伺服器後,會被服務端的inetinfo.exe 程序捕獲,該程序将該http請求轉交給asp.net_isapi.dll程序,然後通過http pipeline 管道(具體這是什麼東西我也不清楚)傳送給aspnet_wp.exe程序來處理。接下來就到了.net framework的httpruntime進行中心,處理完畢後,就發送給使用者的浏覽器。
2. 當http請求傳送給httpruntime 的時候,首先會進入一個叫做HttpApplication Factory的容器之中,從這個名字上很容易看出是一個工廠,該工廠産生一個HttpApplication的執行個體,該執行個體儲存有該請求的所有資訊。然後,http請求進入HttpModule容器之中,然後進入HttpHandlerFactory中,調用相應的HttpHandler,執行其ProcessRequest方法,完成整個請求的處理。其中httpHandler是真正的請求進行中心。Httpmodule隻是在該進行中心做處理之前對其進行一些額外的處理,篩選等等。
3. 大體的流程如下所示
HttpRequest------ inetinfo.exe--- aspnet_isapi.dll ---- http pipeline --aspnet_wp.exe ---http runtime----Http Application ---Http Application ---HttpModule---HttpHandler Factory ---HttpHandler -----HttpHandler.Process Request()
以上内容大多取自【我也想飛翔-tangself】的HttpModule的認識一文,大家可以 看原作: http://www.cnblogs.com/tangself/archive/2011/03/28/1998007.html
4. 執行ProcessRequest 的過程其實也就是整個ASP.NET page 頁面的生命周期。
ASP.NET 的Page類實作了IHttpHandler,而我們每一個頁面都是繼承與Page的,那麼也就是說每一個頁面都是一個HttpHandler,這個Handler就會處理目前頁面的請求。看來,應該是預設将該Handler和該頁面路徑比對起來。這個我沒有考證,隻是我的推測。
ProcessRequest 首先會調用this.FrameworkInitialize(); 以建構控件樹。然後預初始化OnPreInit,然後 調用PerformPreInit()完成預初始化,然後開始初始化Init(),注意執行初始化的時候 已經執行了TraceViewState ,已經開始對資料進行了跟蹤,如果資料成為dirty資料,則會被存儲到statebag中。具體什麼時候開啟的,我現在還不太清楚。接下來,如果是回傳,則會加載控件的頁面視圖狀态,處理回傳資料(ProcessPostData) ,如果是第一加載,則會跳過這一部分。随後執行預加載操作OnPreLoad,然後執行OnLoad(LoadRecursive)。如果是回傳,會發送回傳變化通知,處理回傳事件方法RaisePostbackEvent。随後執行OnLoadCompleted 。接下來進入預呈現OReRender,儲存個性化資料和視圖狀态(所謂的視圖狀态儲存,在這個部分實際上就是調用SaveState,将原有的鍵值對資料儲存到一個ArrayList中,序列化輸出到頁面上是之後的事情了)。然後執行呈現方法Render,随後頁面加載完畢執行OnUnload操作。
更加詳細可靠的内容大家可以移步這裡
1. ASP.NET程式設計模型之ASP.NET頁面生命周期圖解 http://developer.51cto.com/art/200908/141235.htm
2. ASP.NET VIEW STATE 詳解(講的很詳細,由淺入深,老外的。。。)
http://www.cnblogs.com/xianrongbin/articles/2240851.html
HttpModule初步接觸
我們可以實作自己的HttpModule,隻要繼承IHttpModule即可。
IHttpModul接口如下
Using System;
Namespace System.Web
{
Public interface IHttpModule
{
// 銷毀不再被HttpModule使用的資源
void Dispose();
// 初始化一個Module,為捕獲HttpRequest做準備
void Init(HttpApplication context);
{}
}
下面是我自己寫的Module,實際上和 http://www.cnblogs.com/tangself/archive/2011/03/28/1998007.html差不多。。。
public class MyHttpModule:IHttpModule
{
public MyHttpModule()
{
}
public void Dispose()
{
}
/// <summary>
/// 準備初始化一個module,為截獲httprequest做準備
/// </summary>
/// <param name="context"></param>
public void Init(HttpApplication context)
{
//通過兩個事件訂閱,加入自己的處理
context.BeginRequest+=new EventHandler(this.Application_BeginReqest);
context.EndRequest+=new EventHandler(this.Application_EndReqest);
}
public void Application_BeginReqest(object sender,EventArgs e)
{
HttpApplication app = sender as HttpApplication;
if(app!=null)
{
HttpContext context = app.Context;
context.Response.Write("this is in module begin request ");
}
}
public void Application_EndReqest(object sender, EventArgs e)
{
HttpApplication httpApp = sender as HttpApplication;
if(httpApp!=null)
{
HttpContext context = httpApp.Context;
context.Response.Write("this is in module end request");
}
}
}
這個類我建在了AppCode中,是以預設沒有名空間
然後在web.config的system.web節點下添加如下配置
<httpModules>
<add name="myHttpModule" type="MyHttpModule"/>
</httpModules>
建立hellomodule頁面,通路,頁面輸出如下
可以看出請求該頁面前先執行了該Module中的方法。然後才轉交給了頁面這個HttpHandler。如果沒有這個頁面,我們随便通路一個位址,那麼就會資源未找到的錯誤。
我們在建立一個HttpModule跟原來的一樣,隻不過命名為HttpModule2,添加配置。
<httpModules>
<!--<先會經過這個鍊式的module>-->
<add name="myHttpModule" type="MyHttpModule"/>
<add name="myHttpModule2" type="MyHttpModule2"/>
</httpModules>
通路頁面可以發現,整個請求過程中先經過了myHttpModule 處理,在經過了myHttpModule1的處理。
注:type字段填寫的是該module的名空間.類名,這樣,就可以通過反射的方式進行調用。其實這也就是以來注入吧。
接下來我們在大概看一下HttpHandler
HttpHandler初步接觸
我們也可以實作自己的HttpHandler。隻要繼承IHttpHandler即可。
IHttpHandler定義如下
public interface IHttpHandler
{
// Methods
void ProcessRequest(HttpContext context);
// Properties
bool IsReusable { get; }
}
下邊是我自己實作的HttpHandler
public class MyHandler : IHttpHandler
{
public MyHandler()
{
}
/// <summary>
/// 根據webconfig中的httpHandler節點的path的配置,當通路該節點時,就會自動生成相應的type執行個體,調用該handler的
/// ProcessRequest方法
/// 是以,我們可以通過解析路徑的方法,讓路徑對應一個handler中的某個方法,可以使用發射工廠
/// </summary>
/// <param name="context"></param>
public void ProcessRequest(HttpContext context)
{
Uri uri= context.Request.Url;
context.Response.Write("<br/>"+uri.OriginalString);
context.Response.Write("<br/><h1>Hello,this is my http handler</h1>");
}
public bool IsReusable
{
get { return true; }
}
}
然後在配置檔案中添加如下配置
<httpHandlers>
<!--處理該aspx終點的輸入請求為保留檔案夾app_code中,程式集的定義可忽略 -->
<add verb="*" path="HelloHandler" type="MyHandler"/>
</httpHandlers>
然後在浏覽器中通路HelloHandler,如下所示
我們也可以将該path對應一個aspx頁面,當然這樣就像是給這個路徑指定了兩個HttpHandler。建立一個HelloHandler.aspx頁面,添加内容【 this is in page!!!!!!!!!!!】,然後添加web.Config的Handler配置,我們看看效果
看來page對應的Handler沒有生效。。。。
HttpHandler和HttpModule小小總結
在回過頭來想,最初那個Page Resource Not Fond 的錯誤,其實并不是 Page Not Found而是請求路徑對應的HttpHandler并沒有找到。例如我們在配置檔案做如下配置
<httpHandlers>
<!--處理該aspx終點的輸入請求為保留檔案夾app_code中,程式集的定義可忽略 -->
<add verb="*" path="HelloHandler.aspx" type="MyHandler"/>
<add verb="*" path="my" type="MyHandler"/>
</httpHandlers>
<httpModules>
<!--<先會經過這個鍊式的module>-->
<add name="myHttpModule" type="MyHttpModule"/>
<add name="myHttpModule2" type="MyHttpModule2"/>
</httpModules>
請求http://localhost:1684/HandlerStudy/my
輸出内容如下
可以看出來,仍然先經過了連個Module的處理,伺服器認為該路徑對應是有資源的,并沒有出現Page not found 的錯誤。然後,找到該路徑所配置的HttpHandler 執行相應的ProcesRequest方法。
那麼綜合前邊Asp.net 流程的分析,我們就不難得出 如下結論。
當Http 請求進入到HttpRuntime 時,首先會交由一個HttpModule的處理鍊一次處理,處理先後順序和配置順序一緻(其實也應該是事件訂閱的先後順序),該HttpModule處理完畢後,HttpHandler Factory 會擷取所有注入的HttpHandler,根據路徑比對出相應的Handler,利用反射生成相應Handler執行個體,調用相應ProcessRequest方法。
檢視PageHandlerFactory 代碼發現
public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string virtualPath, string path)
{
return this.GetHandlerHelper(context, requestType, VirtualPath.CreateNonRelative(virtualPath), path);
}
private IHttpHandler GetHandlerHelper(HttpContext context, string requestType, VirtualPath virtualPath, string physicalPath)
{
Page page = BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(Page), context, true) as Page;
if (page == null)
{
return null;
}
page.TemplateControlVirtualPath = virtualPath;
return page;
}
應該基本上就是這樣。
Ok,關于HttpHandler和HttpModule的初步學習就這麼多。對其二者的使用還需要做進一步深入了解。還有就是Page 本身也是一個Handler,是在什麼時候,又是如何注冊到Handler Factory中的,這個問題也需要查一下。