ASP.NET底層與各個組件的初步認識與理解 (轉載)

 

ASP.NET底層的初步認識與理解html

 

最近在國外的網站亂走一通,發現一些比較好的文章,收集整理加於本身的理解,做爲筆記形式記錄下來,讓之後本身有個回憶。web

ASP.NET是一個很是強大的構建Web應用的平臺,它提供了極大的靈活性和能力以至於能夠用它來構建全部類型的Web應用.絕大多數的人只熟悉高層的框架如WebForms和WebServices-這些都在ASP.NET層次結構在最高層.在這篇文章中我將會討論ASP.NET的底層機制並解釋請求(request)是怎麼從Web服務器傳送到ASP.NET運行時而後如何經過ASP.NET管道來處理請求.api

ASP.NET是一個請求處理引擎.它接收一個發送過來的請求,把它傳給內部的管道直到終點,做爲一個開發人員的你能夠在這裏附加一些代碼來處理請求.這個引擎是和HTTP/Web服務器徹底分隔的.事實上,HTTP運行時是一個組件,使你能夠擺脫IIS或者任何其餘的服務器程序,將你本身的程序寄宿在內.數組

運行時提供了一個複雜但同時很是優雅的在管道中路由請求的機制.其中有不少相關的對象,大多數都是可擴展的(經過繼承或者事件接口),在幾乎全部的處理流程上都是如此.因此這個框架具備高度可擴展性.經過這個機制,掛接到很是底層的接口(好比緩存,認證和受權)都變得可能了.你甚至能夠在預處理或者處理後過濾內容,也能夠簡單的將符合特殊標記的請求直接路由你的代碼或者另外一個URL上.存在着許多不一樣的方法來完成同一件事,可是全部這些方法都是能夠簡單直接地實現的,同時還提供了靈活性,能夠獲得最好的性能和開發的簡單性.瀏覽器

整個ASP.NET引擎是徹底創建在託管代碼上的,全部的擴展功能也是經過託管代碼擴展來提供的.這是對.NET框架具備構建複雜並且高效的框架的能力的最好的證實.ASP.NET最使人印象深入的地方是深思熟慮的設計,使得框架很是的容易使用,又能提供掛接到請求處理的幾乎全部部分的能力.緩存

ASP.NET在微軟的平臺上就是經過ISAPI擴展來和IIS進行交互的,這個擴展寄宿着.NET運行時和ASP.NET運行時.ISAPI提供了核心的接口,ASP.NET使用非託管的ISAPI代碼經過這個接口來從Web服務器獲取請求,併發送響應回客戶端.ISAPI提供的內容能夠經過通用對象(例如HttpRequest和HttpResponse)來獲取,這些對象經過一個定義良好並有很好訪問性的接口來暴露非託管數據.安全

當用戶發送一個URL請求時,在Web服務器端,IIS5或6,得到這個請求.在最底層,ASP.NET和IIS經過ISAPI擴展進行交互.在ASP.NET環境中這個請求一般被路由到一個擴展名爲.aspx的頁面上,可是這個流程是怎麼工做的徹底依賴於處理特定擴展名的HTTP Handler是怎麼實現的.在IIS中.aspx經過’應用程序擴展’(又稱爲腳本映射)被映射到ASP.NET的ISAPI擴展DLL-aspnet_isapi.dll.每個請求都須要經過一個被註冊到aspnet_isapi.dll的擴展名來觸發ASP.NET(來處理這個請求).服務器

ISAPI是底層的非託管Win32 API.ISAPI定義的接口很是簡單而且是爲性能作了優化的.它們是很是底層的-處理指針和函數指針表來進行回調-可是它們提供了最底層和麪向效率的接口,使開發者和工具提供商能夠用它來掛接到IIS上.由於ISAPI很是底層因此它並不適合來開發應用級的代碼,並且ISAPI傾向於主要被用於橋接接口,向上層工具提供應用服務器類型的功能.網絡

下面來介紹HttpRuntime,HttpContext,HttpApplicationsession

當一個請求到來時,它被路由到ISAPIRuntime.ProcessRequest()方法.這個方法調用HttpRuntime.ProcessRequest方法,它做一些重要的事情

爲請求建立一個新的HttpContext實例 
獲取一個HttpApplication實例
調用HttpApplication.Init()方法來設置管道的事件
Init()方法觸發開始ASP.NET管道處理的HttpApplication.ResumeProcessing()方法

首先一個新的HttpContext對象被建立並用來傳遞ISAPIWorkerRequest,這個上下文在整個請求的生命週期總都是可用的並總能夠經過靜態屬性.
HttpContext.Currect來訪問.正像名字所暗示的那樣,HttpContext對象表明了當前活動請求的上下文由於他包含了在請求生命週期中全部典型的你須要訪問的重要對象:Request,Response,Application,Server,Cache.在請求處理的任什麼時候候HttpContext.Current給你訪問全部這些的能力.

HttpContext對象也包含一個很是有用的Items集合,你能夠用它來保存針對特定請求的數據.上下文對象在請求週期的開始時被建立,在請求結束時被釋放,全部在Items集合中保存的數據只在這個特定的請求中可用.一個很好的使用的例子是請求日誌機制,當你經過想經過在Global.asax中掛接Application_BeginRequest和Application_EndRequest方法記錄請求的開始和結束時間(象在列表3中顯示的那樣).HttpContext對你就很是有用了-若是你在請求或頁面處理的不一樣部分須要數據,你自由的使用它.

protected void Application_BeginRequest(Object sender, EventArgs e)
{
 if (App.Configuration.LogWebRequests)
 {
  Context.Items.Add("WebLog_StartTime",DateTime.Now);
 }
}

protected void Application_EndRequest(Object sender, EventArgs e)
{
 if (App.Configuration.LogWebRequests) 
 {
  try 
  { 
   TimeSpan Span = DateTime.Now.Subtract((DateTime) Context.Items["WebLog_StartTime"] );
   int MiliSecs = Span.TotalMilliseconds;
   WebRequestLog.Log(App.Configuration.ConnectionString, true, MilliSecs);
  } 
 }
}

HttpApplication

每一個請求都被路由到一個HttpApplication對象上.HttpApplicationFactory類根據應用程序的負載爲你的ASP.NET應用建立一個HttpApplication對象池併爲每一個請求分發HttpApplication對象的引用.對象池的大小受machine.config文件中ProcessModel鍵中的MaxWorkerThreads設置限制.
HttpApplication是你的Web程序的外部包裝器,並且它被映射到在Global.asax裏面定義的類上.它是進入HttpRuntime的第一個入口點.若是你查看Global.asax(或者對應的代碼類)你會發現這個類直接繼承自HttpApplication:
HttpApplication的主要職責是做爲Http管道的事件控制器,因此它的接口主要包含的是事件.事件掛接是很是普遍的,大概包括如下這些:
BeginRequest
AuthenticateRequest
AuthorizeRequest
ResolveRequestCache 
AquireRequestState 
PreRequestHandlerExecute 
PostRequestHandlerExecute 
ReleaseRequestState 
UpdateRequestCache 
EndRequest

HttpModule和HttpHandler二者都是在HttpApplication.Init()函數調用的一部分中被載入並附加到調用鏈上

httpApplication它自己對發送給應用程序的數據一無所知-它只是一個經過事件來通信的消息對象.它觸發事件並經過HttpContext對象來向被調用函數傳遞消息.實際的當前請求的狀態數據由前面提到的HttpContext對象維護.它提供了全部請求專有的數據並從進入管道開始到結束一直跟隨請求

一旦管道被啓動,HttpApplication開始象圖六那樣一個個的觸發事件.每一個事件處理器被觸發,若是事件被掛接,這些處理器將執行它們本身的任務.這個處理的主要任務是最終調用掛接到此特定請求的HttpHandler.處理器(handler)是ASP.NET請求的核心處理機制,一般也是全部應用程序級別的代碼被執行的地方.記住ASP.NET頁面和Web服務框架都是做爲HttpHandler實現,這裏也是處理請求的的核心之處.模塊(module)趨向於成爲一個傳遞給處理器(handler)的上下文的預處理或後處理器.ASP.NET中典型的默認處理器包括預處理的認證,緩存以及後處理中各類不一樣的編碼機制.

雖然HttpModule看上去很像ISAPI過濾器,它們都檢查每一個經過ASP.NET應用的請求,可是它們只檢查映射到單個特定的ASP.NET應用或虛擬目錄的請求,也就是隻能檢查映射到ASP.NET的請求.這樣你能夠檢查全部ASPX頁面或者其餘任何映射到ASP.NET的擴展名.

實現一個HTTP模塊是很是簡單的:你必須實現之包含兩個函數(Init()和Dispose())的IHttpModule接口.傳進來的事件參數中包含指向HTTPApplication對象的引用,這給了你訪問HttpContext對象的能力.在這些方法上你能夠掛接到HttpApplication事件上.例如,若是你想掛接AuthenticateRequest事件到一個模塊上

 

總的來講w3wp.exe調用.NET類庫進行具體處理,順序以下:ISAPIRuntim, HttpRuntime, HttpApplicationFactory, HttpApplication, HttpModule, HttpHandlerFactory, HttpHandler


有時間再對每一個對象正進深刻理解.

 

 

 

下面最早介紹HttpRuntime的Web.config裏的配置

 

<httpRuntime
   executionTimeout = "number" 
   maxRequestLength = "number" 
   requestLengthDiskThreshold = "number" 
   useFullyQualifiedRedirectUrl = "[True|False]" 
   minFreeThreads = "number" 
   minLocalRequestFreeThreads = "number" 
   appRequestQueueLimit = "number"
   enableKernelOutputCache = "[True|False]" 
   enableVersionHeader = "[True|False]" 
   apartmentThreading = "[True|False]"
   requireRootedSaveAsPath = "[True|False]"
   enable = "[True|False]" 
   sendCacheControlHeader = "[True|False]" 
   shutdownTimeout = "number"
   delayNotificationTimeout = "number"
   waitChangeNotification = "number" 
   maxWaitChangeNotification = "number" 
   enableHeaderChecking = "[True|False]" 
/>

 

經過上面的配置說明, 下面是在Web.Config裏節點的設置

 

<configuration>
  <system.web>
  <httpRuntime maxRequestLength="4000"
    enable = "True"
    requestLengthDiskThreshold="512
    useFullyQualifiedRedirectUrl="True"
    executionTimeout="45"
    versionHeader="1.1.4128"/>
  </system.web>
</configuration>

 

 

IIS 所收到的對某 Microsoft ASP.NET 頁面的每一個請求都被移交給 ASP.NET HTTP 管線。HTTP 管線由一系列託管對象組成,這些對象按順序處理該請求,並完成從 URL 到普通 HTML 文本的轉換。HTTP 管線的入口點是 HttpRuntime 類。ASP.NET 基礎結構爲輔助進程中所承載的每一個 AppDomain 建立此類的一個實例請注意,該輔助進程爲當前正在運行的每一個 ASP.NET 應用程序維護一個不一樣的 AppDomain。

要激活 HTTP 管道,能夠建立一個 HttpRuntime 類的新實例,而後調用其 ProcessRequest 方法。一個完整的頁面請求會包括下面的流程:
首先被WWW服務器截獲(inetinfo.exe進程), 該進程首先判斷頁面後綴, 而後根據IIS中配置決定調用具體的擴展程序。aspx就會調用aspnet_isapi.dll, 
而後由aspnet_isapi.dll發送給w3wp.exe(iis 工做者進程,IIS6.0中叫作 w3wq.exe,IIS5.0中叫作 aspnet_wp.exe)。

接下來在w3wp.exe調用.NET類庫進行具體處理,順序以下:ISAPIRuntim, HttpRuntime, HttpApplicationFactory, HttpApplication, HttpModule, HttpHandlerFactory, HttpHandler

ISAPIRuntime:主要做用是調用一些非託管代碼生成HttpWorkerRequest對象,HttpWorkerRequest對象包含當前請求的全部信息,而後傳遞給HttpRuntime
HttpRuntime:根據HttpWorkerRequest對象生成HttpContext,HttpContext包含request、response等屬性, 再調用HttpApplicationFactory來生成IHttpHandler, 調用HttpApplication對象執行請求
HttpApplicationFactory: 生成一個HttpApplication對象
HttpApplication:進行HttpModule的初始化,HttpApplication建立針對此Http請求的 HttpContext對象
HttpModule: 當一個HTTP請求到達HttpModule時,整個ASP.NET Framework系統還並無對這個HTTP請求作任何處理,也就是說此時對於HTTP請求來說,HttpModule是一個HTTP請求的「必經之路」,因此能夠在這個HTTP請求傳遞到真正的請求處理中心(HttpHandler)以前附加一些須要的信息在這個HTTP請求信息之上,或者針對截獲的這個HTTP請求信息做一些額外的工做,或者在某些狀況下乾脆終止知足一些條件的HTTP請求,從而能夠起到一個Filter過濾器的做用。
HttpHandlerFactory:把用戶request 轉發到HttpHandlerFactory,再由HttpHandlerFactory實例化HttpHandler對象來相應request
HttpHandle:Http處理程序,處理頁面請求

從上面看出HttpRuntime其中有一個ProcessRequest 方法
public static void ProcessRequest(HttpWorkerRequest wr);  //驅動全部 ASP.NET Web 處理執行。僞代碼以下:

 

public static void HttpRuntime.ProcessRequest(HttpWorkerRequest wr)
 {
   // 檢查當前調用者有沒有做爲ASP.NET宿主(Host)的權限
   InternalSecurityPermissions.AspNetHostingPermissionLevelMedium.Demand(); 

   if(wr == null)
   {
     throw new ArgumentNullException("custom");
   }

   RequestQueue queue = HttpRuntime._theRuntime._requestQueue;

   if(queue != null)
   {
     // 將參數中的Web頁面請求放入請求隊列中, 並從隊列中使用FIFO策略獲取一個頁面請求
     wr = queue.GetRequestToExecute(wr);
   }

   if(wr != null)
   {     
     HttpRuntime.CalculateWaitTimeAndUpdatePerfCounter(wr); // 更新性能計數器     
      HttpRuntime.ProcessRequestNow(wr); // 實際完成頁面請求工做
   }
 }

 

ProcessRequestNow函數則直接調用缺省HttpRuntime實例的ProcessRequestInternal函數完成實際頁面請求工做,僞代碼以下:

internal static void HttpRuntime.ProcessRequestNow(HttpWorkerRequest wr)
{
   HttpRuntime._theRuntime.ProcessRequestInternal(wr);
}

ProcessRequestInternal函數邏輯稍微複雜一些,大體可分爲四個部分:
檢查當前HttpRuntime實例是否第一次被調用,若是是第一次調用則經過FirstRequestInit函數初始化
調用HttpResponse.InitResponseWriter函數初始化頁面請求的返回對象HttpWorkerRequest.Response
調用HttpApplicationFactory.GetApplicationInstance函數獲取當前 Web 應用程序實例
使用Web應用程序實例完成實際的頁面請求工做
僞代碼以下:


private void HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
 {
   // 構造 HTTP 調用上下文對象
   HttpContext ctxt = new HttpContext(wr, 0); 

   // 設置發送結束異步回調函數
   wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, ctxt);

   // 更新請求計數器
   Interlocked.Increment(&(this._activeRequestCount));

   try
   {
     // 檢查當前HttpRuntime實例是否第一次被調用
     if(this._beforeFirstRequest)
     {
       lock(this)
       {
         // 使用 Double-Checked 模式 避免冗餘鎖定
         if(this._beforeFirstRequest)
         {
           this._firstRequestStartTime = DateTime.UtcNow;
           this.FirstRequestInit(ctxt); // 初始化當前 HttpRuntime 運行時環境
           this._beforeFirstRequest = false;
         }
       }
     }

     // 根據配置文件設置,扮演具備較高特權的角色
     ctxt.Impersonation.Start(true, false);
     try
     {
       // 初始化頁面請求的返回對象
       ctxt.Response.InitResponseWriter();
     }
     finally
     {
       ctxt.Impersonation.Stop();
     }

     // 獲取當前 Web 應用程序實例
     IHttpHandler handler = HttpApplicationFactory.GetApplicationInstance(ctxt);

     if (handler == null)
     {
       throw new HttpException(HttpRuntime.FormatResourceString("Unable_create_app_object"));
     }

     // 使用Web應用程序實例完成實際的頁面請求工做
     if((handler as IHttpAsyncHandler) != null)
     {
       IHttpAsyncHandler asyncHandler = ((IHttpAsyncHandler) handler);
       ctxt.AsyncAppHandler = asyncHandler;
       // 使用異步處理機制
       asyncHandler.BeginProcessRequest(ctxt, this._handlerCompletionCallback, ctxt);
     }
     else
     {
       handler.ProcessRequest(ctxt);
       this.FinishRequest(ctxt.WorkerRequest, ctxt, null);
     }
   }
   catch(Exception E)
   {
     ctxt.Response.InitResponseWriter();
     this.FinishRequest(wr, ctxt, E);
   }
 }

HttpRuntime.ProcessRequestInternal函數中調用了HttpApplicationFactory.GetApplicationInstance函數獲取當前 Web 應用程序實例。至少HttpRuntime已經完完成,將轉進HttpApplicationFactory階段流程。你們能夠看到,圍繞HttpRuntime的函數都有一個HttpWorkerRequest參數,下面簡單介紹一下這個參數的做用。在ISAPIRuntime階段,調用一些非託管代碼生成HttpWorkerRequest對象,該對象包含當前請求的全部信息,而後傳遞給HttpRuntime,這裏生成的HttpWorkerRequest對象能夠直接在咱們的頁面裏調用.
IServiceProvider provider=(IServiceProvider)HttpContext.Current;
HttpWorkerRequest wr=(HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));
而後能夠經過wr來調用裏面的方法。關於HttpWorkerRequest類裏面包含的方法,從其元數據裏面能夠看到,以下:


public abstract class HttpWorkerRequest
    {
        // 摘要:
        //     指定 AcceptHTTP 標頭的索引號。
        public const int HeaderAccept = 20;
        //
        // 摘要:
        //     指定 Accept-CharsetHTTP 標頭的索引號。
        public const int HeaderAcceptCharset = 21;
        //
        // 摘要:
        //     指定 Accept-EncodingHTTP 標頭的索引號。
        public const int HeaderAcceptEncoding = 22;
        //
        // 摘要:
        //     指定 Accept-LanguageHTTP 標頭的索引號。
        public const int HeaderAcceptLanguage = 23;
        //
        // 摘要:
        //     指定 Accept-RangesHTTP 標頭的索引號。
        public const int HeaderAcceptRanges = 20;
        //
        // 摘要:
        //     指定 AgeHTTP 標頭的索引號。
        public const int HeaderAge = 21;
        //
        // 摘要:
        //     指定 AllowHTTP 標頭的索引號。
        public const int HeaderAllow = 10;
        //
        // 摘要:
        //     指定 AuthorizationHTTP 標頭的索引號。
        public const int HeaderAuthorization = 24;
        //
        // 摘要:
        //     表示 HTTPCache-ControlHTTP 標頭的索引。
        public const int HeaderCacheControl = 0;
        //
        // 摘要:
        //     指定 ConnectionHTTP 標頭的索引號。
        public const int HeaderConnection = 1;
        //
        // 摘要:
        //     指定 Content-EncodingHTTP 標頭的索引號。
        public const int HeaderContentEncoding = 13;
        //
        // 摘要:
        //     指定 Content-LanguageHTTP 標頭的索引號。
        public const int HeaderContentLanguage = 14;
        //
        // 摘要:
        //     指定 Content-LengthHTTP 標頭的索引號。
        public const int HeaderContentLength = 11;
        //
        // 摘要:
        //     指定 Content-LocationHTTP 標頭的索引號。
        public const int HeaderContentLocation = 15;
        //
        // 摘要:
        //     指定 Content-MD5HTTP 標頭的索引號。
        public const int HeaderContentMd5 = 16;
        //
        // 摘要:
        //     指定 Content-RangeHTTP 標頭的索引號。
        public const int HeaderContentRange = 17;
        //
        // 摘要:
        //     指定 Content-TypeHTTP 標頭的索引號。
        public const int HeaderContentType = 12;
        //
        // 摘要:
        //     指定 CookieHTTP 標頭的索引號。
        public const int HeaderCookie = 25;
        //
        // 摘要:
        //     指定 DateHTTP 標頭的索引號。
        public const int HeaderDate = 2;
        //
        // 摘要:
        //     指定 ETagHTTP 標頭的索引號。
        public const int HeaderEtag = 22;
        //
        // 摘要:
        //     指定 ExceptHTTP 標頭的索引號。
        public const int HeaderExpect = 26;
        //
        // 摘要:
        //     指定 ExpiresHTTP 標頭的索引號。
        public const int HeaderExpires = 18;
        //
        // 摘要:
        //     指定 FromHTTP 標頭的索引號。
        public const int HeaderFrom = 27;
        //
        // 摘要:
        //     指定 HostHTTP 標頭的索引號。
        public const int HeaderHost = 28;
        //
        // 摘要:
        //     指定 If-MatchHTTP 標頭的索引號。
        public const int HeaderIfMatch = 29;
        //
        // 摘要:
        //     指定 If-Modified-SinceHTTP 標頭的索引號。
        public const int HeaderIfModifiedSince = 30;
        //
        // 摘要:
        //     指定 If-None-MatchHTTP 標頭的索引號。
        public const int HeaderIfNoneMatch = 31;
        //
        // 摘要:
        //     指定 If-RangeHTTP 標頭的索引號。
        public const int HeaderIfRange = 32;
        //
        // 摘要:
        //     指定 If-Unmodified-SinceHTTP 標頭的索引號。
        public const int HeaderIfUnmodifiedSince = 33;
        //
        // 摘要:
        //     指定 Keep-AliveHTTP 標頭的索引號。
        public const int HeaderKeepAlive = 3;
        //
        // 摘要:
        //     指定 Last-ModifiedHTTP 標頭的索引號。
        public const int HeaderLastModified = 19;
        //
        // 摘要:
        //     指定 LocationHTTP 標頭的索引號。
        public const int HeaderLocation = 23;
        //
        // 摘要:
        //     指定 Max-ForwardsHTTP 標頭的索引號。
        public const int HeaderMaxForwards = 34;
        //
        // 摘要:
        //     指定 PragmaHTTP 標頭的索引號。
        public const int HeaderPragma = 4;
        //
        // 摘要:
        //     指定 Proxy-AuthenticateHTTP 標頭的索引號。
        public const int HeaderProxyAuthenticate = 24;
        //
        // 摘要:
        //     指定 Proxy-AuthorizationHTTP 標頭的索引號。
        public const int HeaderProxyAuthorization = 35;
        //
        // 摘要:
        //     指定 RangeHTTP 標頭的索引號。
        public const int HeaderRange = 37;
        //
        // 摘要:
        //     指定 RefererHTTP 標頭的索引號。
        public const int HeaderReferer = 36;
        //
        // 摘要:
        //     指定 Retry-AfterHTTP 標頭的索引號。
        public const int HeaderRetryAfter = 25;
        //
        // 摘要:
        //     指定 ServerHTTP 標頭的索引號。
        public const int HeaderServer = 26;
        //
        // 摘要:
        //     指定 Set-CookieHTTP 標頭的索引號。
        public const int HeaderSetCookie = 27;
        //
        // 摘要:
        //     指定 TEHTTP 標頭的索引號。
        public const int HeaderTe = 38;
        //
        // 摘要:
        //     指定 TrailerHTTP 標頭的索引號。
        public const int HeaderTrailer = 5;
        //
        // 摘要:
        //     指定 Transfer-EncodingHTTP 標頭的索引號。
        public const int HeaderTransferEncoding = 6;
        //
        // 摘要:
        //     指定 UpgradeHTTP 標頭的索引號。
        public const int HeaderUpgrade = 7;
        //
        // 摘要:
        //     指定 User-AgentHTTP 標頭的索引號。
        public const int HeaderUserAgent = 39;
        //
        // 摘要:
        //     指定 VaryHTTP 標頭的索引號。
        public const int HeaderVary = 28;
        //
        // 摘要:
        //     指定 ViaHTTP 標頭的索引號。
        public const int HeaderVia = 8;
        //
        // 摘要:
        //     指定 WarningHTTP 標頭的索引號。
        public const int HeaderWarning = 9;
        //
        // 摘要:
        //     指定 WWW-AuthenticateHTTP 標頭的索引號。
        public const int HeaderWwwAuthenticate = 29;
        //
        // 摘要:
        //     指定請求的緣由。
        public const int ReasonCachePolicy = 2;
        //
        // 摘要:
        //     指定請求的緣由。
        public const int ReasonCacheSecurity = 3;
        //
        // 摘要:
        //     指定請求的緣由。
        public const int ReasonClientDisconnect = 4;
        //
        // 摘要:
        //     指定請求的緣由。默認值爲 System.Web.HttpWorkerRequest.ReasonResponseCacheMiss。
        public const int ReasonDefault = 0;
        //
        // 摘要:
        //     指定請求的緣由。
        public const int ReasonFileHandleCacheMiss = 1;
        //
        // 摘要:
        //     指定請求的緣由。
        public const int ReasonResponseCacheMiss = 0;
        //
        // 摘要:
        //     指定 MaximumHTTP 請求標頭的索引號。
        public const int RequestHeaderMaximum = 40;
        //
        // 摘要:
        //     指定 MaximumHTTP 響應標頭的索引號。
        public const int ResponseHeaderMaximum = 30;

        // 摘要:
        //     初始化 System.Web.HttpWorkerRequest 類的新實例。
        protected HttpWorkerRequest();

        // 摘要:
        //     獲取 Machine.config 文件的完整物理路徑。
        //
        // 返回結果:
        //     Machine.config 文件的物理路徑。
        public virtual string MachineConfigPath { get; }
        //
        // 摘要:
        //     獲取 ASP.NET 二進制文件的安裝目錄的物理路徑。
        //
        // 返回結果:
        //     ASP.NET 二進制文件的物理目錄。
        public virtual string MachineInstallDirectory { get; }
        //
        // 摘要:
        //     獲取當前請求的 Windows 跟蹤 ID 的對應事件跟蹤。
        //
        // 返回結果:
        //     當前 ASP.NET 請求的跟蹤 ID。
        public virtual Guid RequestTraceIdentifier { get; }
        //
        // 摘要:
        //     獲取根 Web.config 文件的完整物理路徑。
        //
        // 返回結果:
        //     根 Web.config 文件的物理路徑。
        public virtual string RootWebConfigPath { get; }

        // 摘要:
        //     終止與客戶端的鏈接。
        public virtual void CloseConnection();
        //
        // 摘要:
        //     由運行庫使用以通知 System.Web.HttpWorkerRequest 當前請求的請求處理已完成。
        public abstract void EndOfRequest();
        //
        // 摘要:
        //     將全部掛起的響應數據發送到客戶端。
        //
        // 參數:
        //   finalFlush:
        //     若是這將是最後一次刷新響應數據,則爲 true;不然爲 false。
        public abstract void FlushResponse(bool finalFlush);
        //
        // 摘要:
        //     返回當前正在執行的服務器應用程序的虛擬路徑。
        //
        // 返回結果:
        //     當前應用程序的虛擬路徑。
        public virtual string GetAppPath();
        //
        // 摘要:
        //     返回當前正在執行的服務器應用程序的物理路徑。
        //
        // 返回結果:
        //     當前應用程序的物理路徑。
        public virtual string GetAppPathTranslated();
        //
        // 摘要:
        //     在派生類中被重寫時,返回當前 URL 的應用程序池 ID。
        //
        // 返回結果:
        //     始終返回 null。
        public virtual string GetAppPoolID();
        //
        // 摘要:
        //     獲取從客戶端讀入的字節數。
        //
        // 返回結果:
        //     包含讀取的字節數的 Long。
        public virtual long GetBytesRead();
        //
        // 摘要:
        //     在派生類中被重寫時,從客戶端發出的請求獲取證書字段(以 X.509 標準指定)。
        //
        // 返回結果:
        //     包含整個證書內容流的字節數組。
        public virtual byte[] GetClientCertificate();
        //
        // 摘要:
        //     獲取證書頒發者(以二進制格式表示)。
        //
        // 返回結果:
        //     包含以二進制格式表示的證書頒發者的字節數組。
        public virtual byte[] GetClientCertificateBinaryIssuer();
        //
        // 摘要:
        //     在派生類中被重寫時,返回用於編碼客戶端證書的 System.Text.Encoding 對象。
        //
        // 返回結果:
        //     表示爲整數的證書編碼。
        public virtual int GetClientCertificateEncoding();
        //
        // 摘要:
        //     在派生類中被重寫時,獲取與客戶端證書關聯的 PublicKey 對象。
        //
        // 返回結果:
        //     一個 PublicKey 對象。
        public virtual byte[] GetClientCertificatePublicKey();
        //
        // 摘要:
        //     在派生類中被重寫時,則獲取證書開始生效的日期。此日期隨區域設置的不一樣而不一樣。
        //
        // 返回結果:
        //     表示證書生效時間的 System.DateTime 對象。
        public virtual DateTime GetClientCertificateValidFrom();
        //
        // 摘要:
        //     獲取證書到期日期。
        //
        // 返回結果:
        //     表示證書失效日期的 System.DateTime 對象。
        public virtual DateTime GetClientCertificateValidUntil();
        //
        // 摘要:
        //     在派生類中被重寫時,返回當前鏈接的 ID。
        //
        // 返回結果:
        //     始終返回 0。
        public virtual long GetConnectionID();
        //
        // 摘要:
        //     在派生類中被重寫時,返回所請求的 URI 的虛擬路徑。
        //
        // 返回結果:
        //     請求的 URI 的路徑。
        public virtual string GetFilePath();
        //
        // 摘要:
        //     返回請求的 URI 的物理文件路徑(並將其從虛擬路徑轉換成物理路徑:例如,從「/proj1/page.aspx」轉換成「c:\dir\page.aspx」)
        //
        // 返回結果:
        //     請求的 URI 的已轉換的物理文件路徑。
        public virtual string GetFilePathTranslated();
        //
        // 摘要:
        //     返回請求標頭的指定成員。
        //
        // 返回結果:
        //     請求標頭中返回的 HTTP 謂詞。
        public abstract string GetHttpVerbName();
        //
        // 摘要:
        //     提供對請求的 HTTP 版本(如「HTTP/1.1」)的訪問。
        //
        // 返回結果:
        //     請求標頭中返回的 HTTP 版本。
        public abstract string GetHttpVersion();
        //
        // 摘要:
        //     返回與指定的索引相對應的標準 HTTP 請求標頭。
        //
        // 參數:
        //   index:
        //     標頭的索引。例如,System.Web.HttpWorkerRequest.HeaderAllow 字段。
        //
        // 返回結果:
        //     HTTP 請求標頭。
        public virtual string GetKnownRequestHeader(int index);
        //
        // 摘要:
        //     返回指定的 HTTP 請求標頭的索引號。
        //
        // 參數:
        //   header:
        //     標頭的名稱。
        //
        // 返回結果:
        //     在 header 參數中指定的 HTTP 請求標頭的索引號。
        public static int GetKnownRequestHeaderIndex(string header);
        //
        // 摘要:
        //     返回指定的 HTTP 請求標頭的名稱。
        //
        // 參數:
        //   index:
        //     標頭的索引號。
        //
        // 返回結果:
        //     在 index 參數中指定的 HTTP 請求標頭的名稱。
        public static string GetKnownRequestHeaderName(int index);
        //
        // 摘要:
        //     返回指定的 HTTP 響應標頭的索引號。
        //
        // 參數:
        //   header:
        //     HTTP 標頭的名稱。
        //
        // 返回結果:
        //     在 header 參數中指定的 HTTP 響應標頭的索引號。
        public static int GetKnownResponseHeaderIndex(string header);
        //
        // 摘要:
        //     返回指定的 HTTP 響應標頭的名稱。
        //
        // 參數:
        //   index:
        //     標頭的索引號。
        //
        // 返回結果:
        //     在 index 參數中指定的 HTTP 響應標頭的名稱。
        public static string GetKnownResponseHeaderName(int index);
        //
        // 摘要:
        //     提供對請求標頭的指定成員的訪問。
        //
        // 返回結果:
        //     請求標頭中返回的服務器 IP 地址。
        public abstract string GetLocalAddress();
        //
        // 摘要:
        //     提供對請求標頭的指定成員的訪問。
        //
        // 返回結果:
        //     請求標頭中返回的服務器端口號。
        public abstract int GetLocalPort();
        //
        // 摘要:
        //     返回具備 URL 擴展的資源的其餘路徑信息。即對於路徑 /virdir/page.html/tail,GetPathInfo 值爲 /tail。
        //
        // 返回結果:
        //     資源的附加路徑信息。
        public virtual string GetPathInfo();
        //
        // 摘要:
        //     返回 HTTP 請求正文已被讀取的部分。
        //
        // 返回結果:
        //     HTTP 請求正文已被讀取的部分。
        public virtual byte[] GetPreloadedEntityBody();
        //
        // 摘要:
        //     使用指定的緩衝區數據和字節偏移量獲取 HTTP 請求正文當前已被讀取的部分。
        //
        // 參數:
        //   buffer:
        //     要讀取的數據。
        //
        //   offset:
        //     開始讀取的位置的字節偏移量。
        //
        // 返回結果:
        //     HTTP 請求正文已被讀取的部分。
        public virtual int GetPreloadedEntityBody(byte[] buffer, int offset);
        //
        // 摘要:
        //     獲取 HTTP 請求正文當前已被讀取部分的長度。
        //
        // 返回結果:
        //     一個整數,包含當前已讀取的 HTTP 請求正文的長度。
        public virtual int GetPreloadedEntityBodyLength();
        //
        // 摘要:
        //     在派生類中被重寫時,返回 HTTP 協議(HTTP 或 HTTPS)。
        //
        // 返回結果:
        //     若是 System.Web.HttpWorkerRequest.IsSecure() 方法爲 true,則爲 HTTPS;不然,爲 HTTP。
        public virtual string GetProtocol();
        //
        // 摘要:
        //     返回請求 URL 中指定的查詢字符串。
        //
        // 返回結果:
        //     請求查詢字符串。
        public abstract string GetQueryString();
        //
        // 摘要:
        //     在派生類中被重寫時,以字節數組的形式返回響應查詢字符串。
        //
        // 返回結果:
        //     包含響應的字節數組。
        public virtual byte[] GetQueryStringRawBytes();
        //
        // 摘要:
        //     返回附加了查詢字符串的請求標頭中包含的 URL 路徑。
        //
        // 返回結果:
        //     請求標頭的原始 URL 路徑。
        public abstract string GetRawUrl();
        //
        // 摘要:
        //     提供對請求標頭的指定成員的訪問。
        //
        // 返回結果:
        //     客戶端的 IP 地址。
        public abstract string GetRemoteAddress();
        //
        // 摘要:
        //     在派生類中被重寫時,返回客戶端計算機的名稱。
        //
        // 返回結果:
        //     客戶端計算機的名稱。
        public virtual string GetRemoteName();
        //
        // 摘要:
        //     提供對請求標頭的指定成員的訪問。
        //
        // 返回結果:
        //     客戶端的 HTTP 端口號。
        public abstract int GetRemotePort();
        //
        // 摘要:
        //     在派生類中被重寫時,返回請求的緣由。
        //
        // 返回結果:
        //     緣由代碼。默認值爲 ReasonResponseCacheMiss。
        public virtual int GetRequestReason();
        //
        // 摘要:
        //     在派生類中被重寫時,返回本地服務器的名稱。
        //
        // 返回結果:
        //     本地服務器的名稱。
        public virtual string GetServerName();
        //
        // 摘要:
        //     從與請求關聯的服務器變量詞典返回單個服務器變量。
        //
        // 參數:
        //   name:
        //     請求的服務器變量的名稱。
        //
        // 返回結果:
        //     請求的服務器變量。
        public virtual string GetServerVariable(string name);
        //
        // 摘要:
        //     返回一個字符串,該字符串描述指定的 HTTP 狀態代碼的名稱。
        //
        // 參數:
        //   code:
        //     HTTP 狀態代碼。
        //
        // 返回結果:
        //     狀態說明。例如,System.Web.HttpWorkerRequest.GetStatusDescription(System.Int32) (404)
        //     返回「未找到」。
        public static string GetStatusDescription(int code);
        //
        // 摘要:
        //     獲取整個 HTTP 請求正文的長度。
        //
        // 返回結果:
        //     包含整個 HTTP 請求正文的長度的整數。
        public virtual int GetTotalEntityBodyLength();
        //
        // 摘要:
        //     返回非標準的 HTTP 請求標頭值。
        //
        // 參數:
        //   name:
        //     標頭名稱。
        //
        // 返回結果:
        //     標頭值。
        public virtual string GetUnknownRequestHeader(string name);
        //
        // 摘要:
        //     獲取全部非標準的 HTTP 標頭的名稱/值對。
        //
        // 返回結果:
        //     標頭的名稱/值對的數組。
        [CLSCompliant(false)]
        public virtual string[][] GetUnknownRequestHeaders();
        //
        // 摘要:
        //     返回請求的 URI 的虛擬路徑。
        //
        // 返回結果:
        //     請求的 URI 的路徑。
        public abstract string GetUriPath();
        //
        // 摘要:
        //     當在派生類中被重寫時,返回當前鏈接的上下文 ID。
        //
        // 返回結果:
        //     始終返回 0。
        public virtual long GetUrlContextID();
        //
        // 摘要:
        //     在派生類中被重寫時,返回客戶端的模擬標記。
        //
        // 返回結果:
        //     表示客戶端的模擬標記的值。默認值爲 0。
        public virtual IntPtr GetUserToken();
        //
        // 摘要:
        //     獲取請求虛擬路徑的模擬標記。
        //
        // 返回結果:
        //     請求虛擬路徑的標記的非託管內存指針。
        public virtual IntPtr GetVirtualPathToken();
        //
        // 摘要:
        //     返回一個值,該值指示請求是否包含正文數據。
        //
        // 返回結果:
        //     若是請求包含正文數據,則爲 true;不然,爲 false。
        public bool HasEntityBody();
        //
        // 摘要:
        //     返回一個值,該值指示是否已爲當前的請求將 HTTP 響應標頭髮送到客戶端。
        //
        // 返回結果:
        //     若是 HTTP 響應標頭已發送到客戶端,則爲 true;不然,爲 false。
        public virtual bool HeadersSent();
        //
        // 摘要:
        //     返回一個值,該值指示客戶端鏈接是否仍處於活動狀態。
        //
        // 返回結果:
        //     若是客戶端鏈接仍處於活動狀態,則爲 true;不然,爲 false。
        public virtual bool IsClientConnected();
        //
        // 摘要:
        //     返回一個值,該值指示是否全部請求數據均可用,以及是否不須要對客戶端進行進一步讀取。
        //
        // 返回結果:
        //     若是全部請求數據均可用,則爲 true;不然,爲 false。
        public virtual bool IsEntireEntityBodyIsPreloaded();
        //
        // 摘要:
        //     返回一個指示鏈接是否使用 SSL 的值。
        //
        // 返回結果:
        //     若是鏈接是 SSL 鏈接,則爲 true;不然爲 false。默認值爲 false。
        public virtual bool IsSecure();
        //
        // 摘要:
        //     返回與指定虛擬路徑相對應的物理路徑。
        //
        // 參數:
        //   virtualPath:
        //     虛擬路徑。
        //
        // 返回結果:
        //     與 virtualPath 參數中指定的虛擬路徑相對應的物理路徑。
        public virtual string MapPath(string virtualPath);
        //
        // 摘要:
        //     讀取客戶端的請求數據(在還沒有預加載時)。
        //
        // 參數:
        //   buffer:
        //     將數據讀入的字節數組。
        //
        //   size:
        //     最多讀取的字節數。
        //
        // 返回結果:
        //     讀取的字節數。
        public virtual int ReadEntityBody(byte[] buffer, int size);
        //
        // 摘要:
        //     使用指定的要從中讀取數據的緩衝區、字節偏移量和最大字節數從客戶端讀取請求數據(當未預先加載時)。
        //
        // 參數:
        //   buffer:
        //     將數據讀入的字節數組。
        //
        //   offset:
        //     開始讀取的位置的字節偏移量。
        //
        //   size:
        //     最多讀取的字節數。
        //
        // 返回結果:
        //     讀取的字節數。
        public virtual int ReadEntityBody(byte[] buffer, int offset, int size);
        //
        // 摘要:
        //     將 Content-Length HTTP 標頭添加到小於或等於 2 GB 的消息正文的響應。
        //
        // 參數:
        //   contentLength:
        //     響應的長度(以字節爲單位)。
        public virtual void SendCalculatedContentLength(int contentLength);
        //
        // 摘要:
        //     將 Content-Length HTTP 標頭添加到大於 2 GB 的消息正文的響應。
        //
        // 參數:
        //   contentLength:
        //     響應的長度(以字節爲單位)。
        public virtual void SendCalculatedContentLength(long contentLength);
        //
        // 摘要:
        //     將標準 HTTP 標頭添加到響應。
        //
        // 參數:
        //   index:
        //     標頭索引。例如 System.Web.HttpWorkerRequest.HeaderContentLength。
        //
        //   value:
        //     標頭的值。
        public abstract void SendKnownResponseHeader(int index, string value);
        //
        // 摘要:
        //     將指定文件的內容添加到響應並指定文件中的起始位置和要發送的字節數。
        //
        // 參數:
        //   handle:
        //     要發送的文件的句柄。
        //
        //   offset:
        //     文件中的起始位置。
        //
        //   length:
        //     要發送的字節數。
        public abstract void SendResponseFromFile(IntPtr handle, long offset, long length);
        //
        // 摘要:
        //     將指定文件的內容添加到響應並指定文件中的起始位置和要發送的字節數。
        //
        // 參數:
        //   filename:
        //     要發送的文件的名稱。
        //
        //   offset:
        //     文件中的起始位置。
        //
        //   length:
        //     要發送的字節數。
        public abstract void SendResponseFromFile(string filename, long offset, long length);
        //
        // 摘要:
        //     將字節數組中指定數目的字節添加到響應。
        //
        // 參數:
        //   data:
        //     要發送的字節數組。
        //
        //   length:
        //     要發送的字節數(從第一個字節開始)。
        public abstract void SendResponseFromMemory(byte[] data, int length);
        //
        // 摘要:
        //     將內存塊中指定數目的字節添加到響應。
        //
        // 參數:
        //   data:
        //     指向內存塊的非託管指針。
        //
        //   length:
        //     要發送的字節數。
        public virtual void SendResponseFromMemory(IntPtr data, int length);
        //
        // 摘要:
        //     指定響應的 HTTP 狀態代碼和狀態說明,例如 SendStatus(200, "Ok")。
        //
        // 參數:
        //   statusCode:
        //     要發送的狀態代碼
        //
        //   statusDescription:
        //     要發送的狀態說明。
        public abstract void SendStatus(int statusCode, string statusDescription);
        //
        // 摘要:
        //     將非標準 HTTP 標頭添加到響應。
        //
        // 參數:
        //   name:
        //     要發送的標頭的名稱。
        //
        //   value:
        //     標頭的值。
        public abstract void SendUnknownResponseHeader(string name, string value);
        //
        // 摘要:
        //     在發送全部響應數據後註冊可選通知。
        //
        // 參數:
        //   callback:
        //     在發送全部數據(帶外)後調用的通知回調。
        //
        //   extraData:
        //     回調的附加參數。
        public virtual void SetEndOfSendNotification(HttpWorkerRequest.EndOfSendNotification callback, object extraData);

        // 摘要:
        //     表示用於在完成發送響應後通知調用方的方法。
        //
        // 參數:
        //   wr:
        //     當前的 System.Web.HttpWorkerRequest。
        //
        //   extraData:
        //     處理請求所需的任何其餘數據。
        public delegate void EndOfSendNotification(HttpWorkerRequest wr, object extraData);
    }

 

 

 

 

HttpApplication對象是經由HttpApplicationFactory.GetApplicationInstance(並最終調用HttpRuntime.CreateNonPublicInstance)建立的HttpApplicationFactory它的主要任務是使用 URL 信息來查找 URL 虛擬目錄和聚集的 HttpApplication 對象之間的匹配關係。



這個工廠類的行爲歸納爲有如下幾點
一、工廠類維護, HttpApplication 對象池並使用它們來處理應用程序的請求。池的壽命與應用程序的壽命相同。 
二、應用程序的第一個請求到達時,工廠類提取有關應用程序類型的信息(global.asax 類)、設置用於監視更改的文件、建立應用程序狀態並觸發 Application_OnStart 事件。工廠類從池中獲取一個 HttpApplication 實例,並將要處理的請求放入實例中。若是沒有可用的對象,則建立一個新的 HttpApplication 對象。要建立 HttpApplication 對象,須要先完成 global.asax 應用程序文件的編譯。
三、HttpApplication 開始處理請求,而且只能在完成這個請求後才能處理新的請求。若是收到來自同一資源的新請求,則由池中的其餘對象來處理。 
四、應用程序對象容許全部註冊的 HTTP 模塊對請求進行預處理,並找出最適合處理請求的處理程序類型。這經過查找請求的 URL 的擴展和配置文件中的信息來完成。

HttpApplicationFactory.GetApplicationInstance建立HttpApplication實例中有三個關鍵方法:

HttpApplicationFactory._theApplicationFactory.EnsureInited()  該方法檢查HttpApplicationFactory是否被初始化,若是沒有,就經過HttpApplicationFactory.Init()進行初始化。在Init()中,先獲取global.asax文件的完整路徑,而後調用CompileApplication()對global.asax進行編譯。

HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context)  建立特定的HttpApplication實例,觸發ApplicationOnStart事件,執行ASP.global_asax中的Application_Start(object sender, EventArgs e)方法。這裏建立的HttpApplication實例在處理完事件後,就被回收。

HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context) 該方法建立HttpApplication實例並進行初始化,調用System.Web.HttpApplication.InitInternal()方法。建立HttpApplication實例是根據實際的_theApplicationType進行建立。若是Web目錄中沒有global.asa文件,也就是說沒有動態編譯生成ASP.global_asax類型,那就直接實例化  HttpApplication。若是建立了ASP.global_asax類型,那就對ASP.global_asa進行實例化。

建立HttpApplication實例以後就是調用實例的InitInternal方法。
InitInternal方法的主要功能以下:
  1. InitModules():根據Web.Config的設置,建立相應的HttpModules。

  2. HookupEventHandlersForAppplicationAndModules:根據發生的事件,調用HttpApplication實例中相應的事件處理函數。

  3. 建立不少實現IExecutionStep接口的類的實例並添加到當前HttpApplication實例的_execSteps中,等待回調時執行。從這裏咱們能夠看到HttpApplication是以異步的方式處理請求, 對請求的不少處理工做都放入了_execStep等待回調時執行。

     _execStep中主要的處理工做以下:
    1) 對請求的路徑進行安全檢查,禁止非法路徑訪問(ValidatePathExecutionStep)。
    2) 若是設置了UrlMappings, 進行RewritePath(UrlMappingsExecutionStep)。
    3) 執行事件處理函數,好比:BeginRequest、AuthenticateRequest等等。

下面就是獲取處理當前請求的HttpHandler,ASP.NET頁面的動態編譯也是在這裏進行的。至此HttpApplication流程將會轉到HttpHandler流程.也就是說HttpApplication 對象負責查找應該使用哪一種處理程序來處理請求。HttpApplication 對象還負責檢測對動態建立的、表示資源的程序集(如 .aspx 頁面或 .asmx Web 服務)所進行的更改。若是檢測到更改,應用程序對象將確保編譯並加載所請求的資源的最新來源。HttpApplication調用ProcessRequest方法來處理用戶請求,此方法會調用對應的HttpHandler來處理用戶請求,HttpHandler根據用戶請求的文件的擴展名處理請求,並把請求的結果,也就是HTML發送到客戶瀏覽器.

HttpApplication是HttpRuntime所建立的嗎? 並非,HttpRuntime只是向HttpApplicationFactory提出請求,要求返回一個HttpApplication對象。HttpApplicationFactory在接收到請求後,會先檢查是否有已經存在並空閒的對象,若是有就取出一個HttpApplication對象返回給HttpRuntime,若是沒有的話,則要建立一個HttpApplication對象給HttpRunTime。
關於HttpApplication這個類的方法的實現,就再也不一一解釋,須要瞭解的,在類裏面寫上一個HttpApplication單詞,而後右鍵選擇「轉到定義「,就能夠看到裏面的元數據了。

從上面看出global類與HttpApplication十分緊密,其事上,global類是繼承與System.Web.HttpApplication類。

 

public class Global : System.Web.HttpApplication
    {

        protected void Application_Start(object sender, EventArgs e)
        {

        }
        //省略
    }

 


假設在global類中定義變量與對象,咱們在全局中是否可以取得或設置他的值呢?看下面的例子:


public class Global : System.Web.HttpApplication
    {
        public static string UserName = "James Lee";
        protected void Application_Start(object sender, EventArgs e)
        {

        }
        //省略
    }

在頁面中設置:
WebApp.Global.UserName = "AAAA";
在設置前與設置後輸出,你能夠看到值的變化,但對變量的設置,會出現什麼問題?這些就須要你在開發中去考慮了。

若是使用過MVC框架開發的朋友就很熟其路由規則配置,其實就是設置在Global類的Application_Start事件中,以下面:

 

 public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "UpcomingDinners",
                "Dinners/Page/{page}",
                new { controller = "Dinners", action = "Index" }
            );

            routes.MapRoute(
                "Default",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
            );

        }

        protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);
        }
    }

 


其實咱們常常用的Application對象是HttpApplication類的一個屬性,至此HttpApplication已經完成,下次轉到HttpHandler流程了。
隨便談識文斷字,查閱大量的資料,等於收集整理加上本身的看法吧,且當成長過程當中的回憶。

 

 

HttpModule是向實現類提供模塊初始化和處置事件。當一個HTTP請求到達HttpModule時,整個ASP.NET Framework系統還並無對這個HTTP請求作任何處理,也就是說此時對於HTTP請求來說,HttpModule是一個HTTP請求的「必經之路」,因此能夠在這個HTTP請求傳遞到真正的請求處理中心(HttpHandler)以前附加一些須要的信息在這個HTTP請求信息之上,或者針對截獲的這個HTTP請求信息做一些額外的工做,或者在某些狀況下乾脆終止知足一些條件的HTTP請求,從而能夠起到一個Filter過濾器的做用。

 

首先你要實現IHttpModule接口這個接口只有兩個方法,一個是Init方法一個Dispose方法.


using System;

namespace System.Web
{
    // Summary:
    //     Provides module initialization and disposal events to the implementing class.
    public interface IHttpModule
    {
        // Summary:
        //     Disposes of the resources (other than memory) used by the module that implements
        //     System.Web.IHttpModule.
        void Dispose();
        //
        // Summary:
        //     Initializes a module and prepares it to handle requests.
        //
        // Parameters:
        //   context:
        //     An System.Web.HttpApplication that provides access to the methods, properties,
        //     and events common to all application objects within an ASP.NET application
        void Init(HttpApplication context);
    }
}

 

一個HTTP請求在HttpModule容器的傳遞過程當中,會在某一時刻(ResolveRequestCache事件)將這個HTTP請求傳遞給HttpHandler容器。在這個事件以後,HttpModule容器會創建一個HttpHandler的入口實例,可是此時並無將HTTP請求控制權交出,而是繼續觸發AcquireRequestState事件以及PreRequestHandlerExcute事件。在PreRequestHandlerExcute事件以後,HttpModule窗口就會將控制權暫時交給HttpHandler容器,以便進行真正的HTTP請求處理工做。


而在HttpHandler容器內部會執行ProcessRequest方法來處理HTTP請求。在容器HttpHandler處理完畢整個HTTP請求以後,會將控制權交還給HttpModule,HttpModule則會繼續對處理完畢的HTTP請求信息流進行層層的轉交動做,直到返回到客戶端爲止。


HttpModule過程在下面的事件:
BeginRequest    指示請求處理開始。 
AuthenticateRequest PostAuthenticateRequest    封裝請求身份驗證過程。 
AuthorizeRequest   PostAuthorizeRequest    封裝請求受權過程。  
ResolveRequestCache PostResolveRequestCache    封裝檢查是否能利用之前緩存的輸出頁面處理請求的過程。 
PostMapRequestHandler    指示已發現用於處理請求的 HTTP 處理程序。 
AcquireRequestState    PostAcquireRequestState    封裝對請求會話狀態的檢索。 
PostRequestHandlerExecute    指示用於處理請求的 HTTP 處理程序已執行。 
ReleaseRequestState   PostReleaseRequestState    封裝對請求會話狀態的發佈。 
UpdateRequestCache    PostUpdateRequestCache    封裝檢查是否應對請求的資源的輸出進行緩存以備從此重複使用的過程。 
EndRequest    指示請求處理結束。

 

能夠利用HttpModule經過調用HttpApplication.CompleteRequest()方法實現當知足某一個條件時終止這次的HTTP請求。須要注意的是,即便調用了HttpApplication.CompleteRequest()方法終止了一個HTTP請求,ASP.NET Framework仍然會觸發HttpApplication後面的這3個事件:EndRequest事件、PreSendRequestHeaders事件、PreSendRequestContent事件。
若是存在多個自定義的HttpModule的話,當Module1終止了一個HTTP請求,這個HTTP請求將不會再觸發Module2中相應的事件了,但Module2的最後三個事件仍會被觸發。


public class CompleteRequestHttpModule : IHttpModule
    {
        #region IHttpModule 成員
         public void Dispose()
        {} 

        public void Init(HttpApplication application)
        {
            application.BeginRequest += new EventHandler(Application_BeginRequest);
        } 

        void Application_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            application.CompleteRequest();
            application.Context.Response.Write("請求被終止。");
        } 
        #endregion
    }


HttpModule示例:

 過濾http請求


    /// <summary>
    /// 頁面訪問驗證模塊
    /// </summary>
    public class AuthenticationModule : IHttpModule
    {
        #region IHttpModule Members

        public void Dispose()
        {
        }

        public void Init(HttpApplication context)
        {
            context.AcquireRequestState += new EventHandler(context_AcquireRequestState);
        }

        #endregion

        private void context_AcquireRequestState(object sender, EventArgs e)
        {
            HttpContext context = HttpContext.Current;
            string path = context.Request.Path.ToLower();
            // 只處理aspx文件,由於其餘文件沒法得到Session對象,沒法判斷是否已經登陸
            if (path.EndsWith(".aspx"))
            {
                // 若是用戶沒有登陸就會返回false
                if (!UserRules.Instance.IsCurrentUserLogined)
                {
                    // 對於公共文件夾和根目錄的文件不作判斷
                    if (path.StartsWith("/" + AppSettings.PUBLICFOLDERNAME + "/")==false && !(path.LastIndexOf("/") == 0))
                    {
                        // 跳轉到公共頁面首頁                        
                        context.Response.Redirect(AppSettings.PUBLICLOGOUTFILENAME, false);
                        context.ApplicationInstance.CompleteRequest();
                    }
                }
            }
        }
    }

在web.confg中添加httpModules節點註冊事件
<httpModules>
     <add name="AuthenticationModule" type="Business.AuthenticationModule, Business"/>
</httpModules>

 
判斷瀏覽器的版本 


using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

/// <summary>
/// BrowserModule 的摘要說明
/// </summary>
public class BrowserModule : IHttpModule
{
    public BrowserModule()
    {
        //
        // TODO: 在此處添加構造函數邏輯
        //
    }

    #region IHttpModule 成員

    public void Dispose()
    {

    }

    public void Init(HttpApplication context)
    {
        //添加判斷事件
        context.BeginRequest += new EventHandler(context_BeginRequest);
        context.EndRequest += new EventHandler(context_EndRequest);
    }

    void context_EndRequest(object sender, EventArgs e)
    {
        if (isfalse)
        {
            HttpApplication application = (HttpApplication)sender;
            application.Context.Response.Clear();//清空瀏覽器全部的內容
            application.Context.Response.Output.Write("瀏覽器不符合要求");
        }

    }
    public bool isfalse = false;//判斷瀏覽器是否符合要求
    void context_BeginRequest(object sender, EventArgs e)
    {
        HttpApplication application = (HttpApplication)sender;//得到實際發生事件的對象
        HttpBrowserCapabilities browser = application.Context.Request.Browser;//得到瀏覽器的相關信息
        //得到瀏覽器的名字  application.Context.Response.Output.Write(browser.Browser);
        //得到瀏覽器的版本號 application.Context.Response.Output.Write(browser.MajorVersion );
        if (browser.Browser != "IE" || browser.MajorVersion < 7)//判斷瀏覽器的版本是不是IE5以上的
        {
            string strBrowserChooserPage = "";//建立變量裝載瀏覽器不符合條件後要導入的頁面
            try
            {
                strBrowserChooserPage = ConfigurationManager.AppSettings["BrowserChooserPage"];//從配置文件中導入要瀏覽的頁面
            }
            catch (System.Configuration.ConfigurationErrorsException ex)
            {
                throw new Exception(string.Format("請正確配置web.config AppSetting[BrowserChooserPage]節點,系統錯誤提示:{0}", ex.Message));
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
            isfalse = true;//爲True的時候瀏覽器不符合要求,處理完context_BeginRequest事件後,最好在處理context_EndRequest事件的時候判斷是否合適
        }
        else
        {
         
            application.Context.Response.Output.Write("瀏覽器符合要求");
        }
    }
    #endregion
}

註冊web.config事件

實現URL重寫


using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

/// <summary>
/// UrlReWriteHttpModule 的摘要說明
/// </summary>
public class UrlReWriteHttpModule : IHttpModule
{
    public UrlReWriteHttpModule()
    {
        //
        // TODO: 在此處添加構造函數邏輯
        //
    }

    #region IHttpModule 成員

    public void Dispose()
    {
        throw new Exception("The method or operation is not implemented.");
    }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(context_BeginRequest);
    }

    void context_BeginRequest(object sender, EventArgs e)
    {
        HttpContext context = (sender as HttpApplication).Context;
        //string Url1 = context.Request.Path;
        //string Url2 = context.Request.PathInfo;
        //string Url3 = context.Request.Url.ToString();
        //獲得整個的網絡地址
        string fullOrigionalpath = context.Request .Url.ToString();

        if (fullOrigionalpath.Contains("/Books.aspx"))
        {
            context.RewritePath("Products.aspx?Category=Books");//重寫地址,重寫後地址欄中的顯示不變,可是真正返回的頁面是被重寫的頁面
        }
        else if (fullOrigionalpath.Contains("/DVDs.aspx"))
        {
            context.RewritePath("Products.aspx?Category=DVDs");
        }
        else if (fullOrigionalpath.Contains("/DVDs.aspx"))
        {
            context.RewritePath("Products.aspx?Category=CDs.aspx");
        }
    }

    #endregion
}


 註冊web.config事件

-------------------------------------------------完成------------------------------------------------

從上面的例子看出,實際上是一個好簡單的實現方法,就是在init中註冊處理過程事件,把代碼寫完後,在web.congif中HttpModules註冊就完成了.

開發程序千變萬變, 經過這一方法能夠編寫出好多合適本身用的功能,這須要你們去實踐了. 收集與整理,且當成長的回憶.

 

 

 

HttpHandler是HTTP請求的處理中心,真正地對客戶端請求的服務器頁面作出編譯和執行,並將處理事後的信息附加在HTTP請求信息流中再次返回到HttpModule中。HttpHandler與HttpModule不一樣,一旦定義了本身的HttpHandler類,那麼它對系統的HttpHandler的關係將是「覆蓋」關係. HttpHandler功能的實現經過實現IHttpHandler接口來達到.

當一個HTTP請求經同HttpModule容器傳遞到HttpHandler容器中時,ASP.NET Framework會調用HttpHandler的ProcessRequest成員方法來對這個HTTP請求進行真正的處理。以一個ASPX頁面爲例,正是在這裏一個ASPX頁面才被系統處理解析,並將處理完成的結果繼續經由HttpModule傳遞下去,直至到達客戶端。

對於ASPX頁面,ASP.NET Framework在默認狀況下是交給System.Web.UI.PageHandlerFactory這個HttpHandlerFactory來處理的。所謂一個HttpHandlerFactory,所謂一個HttpHandlerFactory,是指當一個HTTP請求到達這個HttpHandler Factory時,HttpHandlerFactory會提供出一個HttpHandler容器,交由這個HttpHandler容器來處理這個HTTP請求。

一個HTTP請求都是最終交給一個HttpHandler容器中的ProcessRequest方法來處理的。 

IHttpHandler接口聲明:

public interface IHttpHandler
{
   bool IsReusable { get; }
   public void ProcessRequest(HttpContext context); //請求處理函數
}

v2.0.50727 下的machine.config中httpHandlers結點是這樣的:<httpHandlers />,並無給出詳細的處理程序,在Web.config中才能看到。


<httpHandlers>
    <remove verb="*" path="*.asmx"/>
    <add verb="*" path="*.asmx" validate="false" type=""/>
    <add verb="*" path="*_AppService.axd" validate="false" type=""/>
    <add verb="GET,HEAD" path="ScriptResource.axd" type=""/>
    <add verb="GET" path="CrystalImageHandler.aspx" type=""/>
</httpHandlers>


能夠看到,在<httpHandlers>結點中將不一樣的文件類型映射給不一樣的Handler去處理. 上面的type部分爲了好顯示我去掉了,其實你打開web.config,能夠看到裏面的內容的.

Verb屬性:指定了處理程序支持的HTTP動做。*-支持全部的HTTP動做;「GET」-支持Get操做;「POST」-支持Post操做;「GET, POST」-支持兩種操做

Path屬性:指定了須要調用處理程序的路徑和文件名(能夠包含通配符).「*」、「*.aspx」

Type屬性:用名字空間、類名稱和程序集名稱的組合形式指定處理程序或處理程序工廠的實際類型. ASP.NET運行時首先搜索bin目錄中的DLL,接着在GAC中搜索 

IHttpHandler工廠

IHttpHandlerFactory的做用是對IHttpHandler進行管理。接口以下:

public interface IHttpHandlerFactory
{
    IHttpHandler GetHandler (HttpContext context,string requestType,string url,string pathTranslated);
    void ReleaseHandler (IHttpHandler handler);
}

GetHandler返回實現IHttpHandler接口的類的實例
ReleaseHandler使工廠能夠重用現有的處理程序實例

HttpHandler的示例:

把硬盤上的圖片以流的方式寫在頁面上 

 

class TestHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            FileStream fs = new FileStream(context.Server.MapPath("test.jpg"), FileMode.Open);
            byte[] b = new byte[fs.Length];
            fs.Read(b, 0, (int)fs.Length);
            fs.Close();
            context.Response.OutputStream.Write(b, 0, b.Length);
        }

        public bool IsReusable
        {
            get
            {
                return true;
            }
        }
    }

 

Web.Config配置文件

      <httpHandlers>

          <add verb="*" path="*" type="ClassLibrary831.TestHandler,ClassLibrary831"></add>

      </httpHandlers>

使用HttpHandler實現圖片防盜鏈  

 

namespace CustomHandler
{
    public class JpgHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            // 獲取文件服務器端物理路徑
            string FileName = context.Server.MapPath(context.Request.FilePath);
            // 若是UrlReferrer爲空,則顯示一張默認的禁止盜鏈的圖片
            if (context.Request.UrlReferrer.Host == null)
            {
                context.Response.ContentType = "image/JPEG";
                context.Response.WriteFile("/error.jpg");
            }
            else
            {
                // 若是 UrlReferrer中不包含本身站點主機域名,則顯示一張默認的禁止盜鏈的圖片
                if (context.Request.UrlReferrer.Host.IndexOf("yourdomain.com") > 0)
                {
                    context.Response.ContentType = "image/JPEG";
                    context.Response.WriteFile(FileName);
                }
                else
                {
                    context.Response.ContentType = "image/JPEG";
                    context.Response.WriteFile("/error.jpg");
                }
            }
        }

       public bool IsReusable{
           get{ return true; }
       }
    }
}

 

 編譯後複製DLL文件到Bin文件夾下,而後在web.config註冊
    <httpHandlers>
      <add path="*.jpg" verb="*" type="CustomHandler.JpgHandler, CustomHandler" />
    </httpHandlers>

經過IhttpHandler實現圖片驗證碼

ashx是什麼文件呢? 其實該文件用於寫web handler. 在web裏面添加一個這樣的文件,能夠看到下列的代碼:

 

<%@ WebHandler Language="C#" Class="Handler" %>

using System;
using System.Web;

public class Handler : IHttpHandler 
{
    
    public void ProcessRequest (HttpContext context) 
    {
        context.Response.ContentType = "text/plain";
        context.Response.Write("Hello World");
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }
}

 

在上面的基礎上修改咱們須要的代碼以下:

 

<%@ WebHandler Language="C#" Class="Handler" %>

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Web;
using System.Web.SessionState;

public class Handler : IHttpHandler, IRequiresSessionState {

    public void ProcessRequest(HttpContext context) {
       context.Response.ContentType = "image/gif";
       //創建Bitmap對象,繪圖
       Bitmap basemap = new Bitmap(200, 60);
       Graphics graph = Graphics.FromImage(basemap);
       graph.FillRectangle(new SolidBrush(Color.White), 0, 0, 200, 60);
       Font font = new Font(FontFamily.GenericSerif, 48, FontStyle.Bold, GraphicsUnit.Pixel);
       Random r = new Random();
       string letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ";
       string letter;
       StringBuilder s = new StringBuilder();
       
       //添加隨機的五個字母
       for (int x = 0; x < 5; x++) {
           letter = letters.Substring(r.Next(0, letters.Length - 1), 1);
           s.Append(letter);
           graph.DrawString(letter, font, new SolidBrush(Color.Black), x * 38, r.Next(0, 15));
       }
       
       //混淆背景
       Pen linePen = new Pen(new SolidBrush(Color.Black), 2);
       for (int x = 0; x < 6; x++)
           graph.DrawLine(linePen, new Point(r.Next(0, 199), r.Next(0, 59)), new Point(r.Next(0, 199), r.Next(0, 59)));
              
       //將圖片保存到輸出流中       
       basemap.Save(context.Response.OutputStream, ImageFormat.Gif);
       context.Session["CheckCode"] = s.ToString();   //若是沒有實現IRequiresSessionState,則這裏會出錯,也沒法生成圖片
       context.Response.End();      
    }

    public bool IsReusable {
       get { return true; }
    }
}

 

最後的就是須要在頁面上引用這個驗證碼了. 建立一個新的aspx頁面.
<img src="Handler.ashx" alt="圖片驗證碼" />

須要注意的就是咱們繼承了一個IRequiresSessionState接口:

public interface IRequiresSessionState{}

Handler類不只須要實現 IHttpHandler接口,爲了在這個Handler類中使用SessionState,還須要實現IRequiresSessionState接口,對於這個接口,MSDN的解釋是這樣的:Specifies that the target HTTP handler requires read and write access to session-state values. This is a marker interface and has no methods.(中文大概的意思:指定當前Http Handler須要對SessionState值的讀寫訪問權。這是一個標記接口,沒有任何方法)。這個接口沒有任何須要實現的方法或屬性,你們只要記得:若是想在HttpHandler中使用SessionState,必須實現這個接口,實際上也就是在類的標頭將這個接口加進去。

收集整理與本身看法總結, 且當回憶, 下次再討論一下HttpModule的理論與方法.

 

 

 此文轉載於 http://www.cnblogs.com/whtydn/archive/2009/10/19/1586002.html

相關文章
相關標籤/搜索