ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline[下篇]

作者: Artech  來源: 博客園  發布時間: 2010-09-03 22:20  閱讀: 1146 次  推薦: 0   原文鏈接   [收藏]  

  ASP.NET Process Model索引

  二、ASP.NET Runtime Pipeline

  現在我們真正進入ASP.NET管轄的范疇,下圖基本上囊括整個處理過程涉及的對象,接下來我們一起來討論這一系列的對象如何相互協作去處理Http Request,并最終生成我們所需的Http Response。

  HttpContext

  上面我們介紹了ISAPI在調用ISAPIRuntime的時候將對應的ISAPI ECB Pointer作為參數傳遞給了ProcessRequest方法,這個ECB pointer可以看成是托管環境和非托管環境進行數據交換的唯一通道,Server Variable和Request Parameter通過它傳入ASP.NET作為進一步處理的依據,ASP.NET最后生成的Response通過它傳遞給ISAPI,并進一步傳遞給IIS最終返回到Client端。

  借助這個傳進來的ECB Pointer,我們創建了一個ISAPIWorkerRequest。ISAPIWorkerRequest作為參數傳入HttpRuntime.ProcessRequestNoDemand的調用。HttpRuntime.ProcessRequestNoDemand最終體現在調用ProcessRequestInternal。下面是真個方法的實現:

 
ProcessRequestInternal
private void ProcessRequestInternal(HttpWorkerRequest wr)
{
HttpContext context;

try
{
context
= new HttpContext(wr, false);
}

catch
{
wr.SendStatus(
400, "Bad Request");
wr.SendKnownResponseHeader(
12, "text/html; charset=utf-8");
byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");
wr.SendResponseFromMemory(bytes, bytes.Length);
wr.FlushResponse(
true);
wr.EndOfRequest();

return;
}
wr.SetEndOfSendNotification(
this._asyncEndOfSendCallback, context);
Interlocked.Increment(
ref this._activeRequestCount);
HostingEnvironment.IncrementBusyCount();

try
{
try
{
this.EnsureFirstRequestInit(context);
}

catch
{
if (!context.Request.IsDebuggingRequest)
{

throw;
}
}
context.Response.InitResponseWriter();
IHttpHandler applicationInstance
= HttpApplicationFactory.GetApplicationInstance(context);
if (applicationInstance == null)
{

throw new HttpException(SR.GetString("Unable_create_app_object"));
}

if (EtwTrace.IsTraceEnabled(5, 1))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, applicationInstance.GetType().FullName,
"Start");
}

if (applicationInstance is IHttpAsyncHandler)
{
IHttpAsyncHandler handler2
= (IHttpAsyncHandler) applicationInstance;
context.AsyncAppHandler
= handler2;
handler2.BeginProcessRequest(context,
this._handlerCompletionCallback, context);
}

else
{
applicationInstance.ProcessRequest(context);

this.FinishRequest(context.WorkerRequest, context, null);
}
}

catch (Exception exception)
{
context.Response.InitResponseWriter();

this.FinishRequest(wr, context, exception);
}
}

   對象上面的代碼沒有必要深究,我們只需要了解大體的執行流程就可以了,下面這一段偽代碼基本上體現整個執行過程:

 
HttpContext context = new HttpContext(wr, false);
IHttpHandler applicationInstance
= HttpApplicationFactory.GetApplicationInstance(context);

  首先通過創建的ISAPIWorkerRequest創建按一個HttpContext對象,隨后通過HttpApplicationFactory.GetApplicationInstance創建一個IHttpHandler對象(一般情況下就是一個HttpApplication對象)。

  正如他的名字體現的,HttpContext體現當前Request的上下文信息,它的生命周期知道整個Request處理結束或者處理超時。通過HttpContext對象我們可以訪問屬于當前Request的一系列常用的對象:Server,Session,Cache,Application,Request,Response,Trace,User,Profile等等。此外我們可以認為將一些數據放在Items屬性中作為狀態管理的一種方式,不過這種狀態管理和其他一些常用的方式,比如Session,Cache,Application,Cookie等,具有根本性的不同之處是其生命周期僅僅維持在當前Request的Context中。

  HttpApplication

  就像其名稱體現的一樣,HttpApplication基本上可以看成是真個ASP.NET Application的體現。HttpApplication和置于虛擬根目錄的Gloabal.asax對應。通過HttpApplicationFactory.GetApplicationInstance創建一個基于Gloabal.asax的HttpApplication對象。在HttpApplicationFactory.GetApplicationInstance方法返回創建的HttpApplication對象之前,會調用一個名為InitInternal的內部方法,該方法會做一些列的初始化的操作,在這些初始化操作中,最典型的一個初始化方法為InitModules(),該方法的主要的目的就是查看Config中注冊的所有HttpModule,并根據配置信息加載相應的Assembly,通過Reflection創建對應的HttpModule,并將這些Module加到HttpApplication 的_moduleCollection Filed中。

  HttpApplication本身并包含對Request的任何處理,他的工作方式是通過在不同階段出發不同Event來調用我們注冊的Event Hander。

  下面列出了HttpApplication所有的Event,并按照觸發的時間先后順序排列:

  • BeginRequest:
  • AuthenticateRequest & Post AuthenticateRequest
  • AuthorizeRequest & Post AuthorizeRequest
  • ResolveRequestCache & Post ResolveRequestCache
  • PostMapRequestHandler:
  • AcquireRequestState & AcquireRequestState:
  • PreRequestHandlerExecute & Post RequestHandlerExecute:
  • ReleaseRequestState & Post ReleaseRequestState
  • UpdateRequestCache & PostUpdateRequestCache
  • EndRequest:

  ASP.NET Application, AppDomain and HttpApplication

  對于一個ASP.NET Application來說,一個Application和一個虛擬目錄相對應,那么是不是一個Application 對應著一個AppDomain呢?一個Application是否就唯一對應一個Httpapplication對象呢?答案是否定的。

  我們首先來看看Application和HttpApplication的關系,雖然我們對一個Application的Request最終都由一個HttpApplication對象來承載。但不能說一個Application就唯一對應一個固定的HttpApplication對象。原因很簡單,ASP.NET天生具有多線程的特性,需要通過相應不同的Client的訪問,如果我們只用一個HttpApplication來處理這些并發的請求,會對Responsibility造成嚴重的影響,通過考慮到Performance的問題,ASP.NET對HttpApplication的使用采用Pool的機制:當Request到達,ASP.NET會現在HttpApplication Pool中查找未被使用的HttpApplication對象,如果沒有,則創建之,否則從Pool直接提取。對于Request處理完成的HttpApplication對象,不會馬上銷毀,而是把它放回到Pool中供下一個Request使用。

  對于Application和AppDomain的關系,可能你會說一個Application肯定只用運行在一個AppDomain之中。在一般情況下這句話無可厚非,但是這卻忽略了一種特殊的場景:在當前Application正在處理Request的時候,我們把web.config以及其他一些相關文件修改了,而且這種改變是可以馬上被ASP.NET檢測到的,為了使我們的變動能夠及時生效,對于改動后的第一個Request,ASP.NET會為期創建一個新的AppDomain,而對于原來的AppDomain,也許還在處理修改前的Request,所有原來的Appdomain會持續到將原來的Request處理結束之后,所以對于一個Application,可能出現多個AppDomain并存的現象。

  HttpModule

  我們上面提到HttpApplication就是一個ASP.NET Application的體現,HttpApplication本身并不提供對Request的處理功能,而是通過在不同階段出發不同的Event。我們能做的只能是根據我們具體的需求將我們的功能代碼作為Event Handler注冊到需要的HttpApplication Event上面。注冊這些Event Handler,我們首先想到的肯定就直接在HttpApplication對應的Global.asax中定義我們的EventHandler好了。這是最直接的辦法,而且Global.asax提供一個簡潔的方式是我們的實現顯得簡單:不需要向一般注冊Event一樣將Delegate添加到對應的Event上面,而是直接通過方法名稱和對應的Event匹配的方式直接將對應的方法作為相關的Event Handler。比如Application_ AcquireRequestState就是AcquireRequestState Event handler。

  但是這種方式在很多情況下卻達不到我們的要求,更多地,我們需要的是一種Plug-in的實現方式:我們在外部定義一些Request Processing的功能,需要直接運用到我們的Application之中。通過使用HttpModule封裝這些功能模塊,并將其注冊到我們的Application的發式可以很簡單的實現這種功能。

  HttpModule實現了System.Web.IHttpModule interface,該Interface很簡單,僅僅有兩個成員:

 
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
public interface IHttpModule
{

// Methods
void Dispose();
void Init(HttpApplication context);
}

我們只要在Init方法中注冊相應的HttpApplication Event Handler就可以了:



public class BasicAuthCustomModule : IHttpModule
{

public void Init(HttpApplication application)
{
application.AuthenticateRequest
+=
new EventHandler(this.OnAuthenticateRequest);
}

public void Dispose() { }

public void OnAuthenticateRequest(object source, EventArgs eventArgs)
{

}
}

  所有的HttpModule同machine.config或者Web.config的httpModules Section定義,下面是Machine.config定義的所有httpModule。

 
<httpModules>
<add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />
<add name="Session" type="System.Web.SessionState.SessionStateModule" />
<add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
<add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
<add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" />
<add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</httpModules>

  但是HttpModule如何起作用的,我們來回顧一下上面一節介紹的:HttpApplicationFactory.GetApplicationInstance方法返回創建的HttpApplication對象之前,會調用一個名為InitInternal的內部方法,該方法會做一些列的初始化的操作,在這些初始化操作中,最典型的一個初始化方法為InitModules(),該方法的主要的目的就是查看Config中注冊的所有HttpModule,并根據配置信息加載相應的Assembly,通過Reflection創建對應的HttpModule,并將這些Module加到HttpApplication 的_moduleCollection Filed中,最后依次調用每個HttpModule的Init方法。下面是其實現:

 
private void InitModules()
{

this._moduleCollection = RuntimeConfig.GetAppConfig().HttpModules.CreateModules();
this.InitModulesCommon();
}



private void InitModulesCommon()
{

int count = this._moduleCollection.Count;
for (int i = 0; i < count; i++)
{

this._currentModuleCollectionKey = this._moduleCollection.GetKey(i);
this._moduleCollection[i].Init(this);
}

this._currentModuleCollectionKey = null;
this.InitAppLevelCulture();
}

  HttpHandler

  如果說HttpModule關注的是所有Inbound Request的處理的話,Handler確實關注基于某種類型的ASP.NET Resource的Request。比如一個.apsx的Web Page通過一個System.Web.UI.Page來處理。HttpHandler和他所處理的Resource通過Config中的system.web/handlers section來定義,下面是Machine.config中的定義。

 
<httpHandlers>
<add verb="*" path="trace.axd" type="System.Web.Handlers.TraceHandler" />
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory" />
<add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory" />
<add verb="*" path="*.asmx" type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" validate="false"/>
<add verb="*" path="*.rem" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="false"/>
<add verb="*" path="*.soap" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="false"/>
<add verb="*" path="*.asax" type="System.Web.HttpForbiddenHandler" />
<add verb="*" path="*.ascx" type="System.Web.HttpForbiddenHandler" />
<add verb="GET,HEAD" path="*.dll.config" type="System.Web.StaticFileHandler" />
<add verb="GET,HEAD" path="*.exe.config" type="System.Web.StaticFileHandler" />
<add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler" />
<add verb="*" path="*.cs" type="System.Web.HttpForbiddenHandler" />
<add verb="*" path="*.csproj" type="System.Web.HttpForbiddenHandler" />
<add verb="*" path="*.vb" type="System.Web.HttpForbiddenHandler" />
<add verb="*" path="*.vbproj" type="System.Web.HttpForbiddenHandler" />
<add verb="*" path="*.webinfo" type="System.Web.HttpForbiddenHandler" />
<add verb="*" path="*.asp" type="System.Web.HttpForbiddenHandler" />
<add verb="*" path="*.licx" type="System.Web.HttpForbiddenHandler" />
<add verb="*" path="*.resx" type="System.Web.HttpForbiddenHandler" />
<add verb="*" path="*.resources" type="System.Web.HttpForbiddenHandler" />
<add verb="GET,HEAD" path="*" type="System.Web.StaticFileHandler" />
<add verb="*" path="*" type="System.Web.HttpMethodNotAllowedHandler" />
</httpHandlers>

  需要注意的是,我們不但可以單純地定義一個實現了System.Web.IHttpHandler的Type,也可以定義一個實現了System.Web.IHttpHandlerFactory 的Type。System.Web.UI.Page是一個典型的Httphandler,相信對此大家已經很熟悉了。在最后還說說另一個典型的HttpHandler:System.Web.HttpForbiddenHandler,從名稱我們不難看出,它用于那些禁止訪問的Resource,現在應該知道了為了Global.asax不同通過IIS訪問了吧。

0
0
 
標簽:IIS ASP.NET
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()