http://referencesource.microsoft.com/php
理解ASP.NET的前提是對ASP.NET管道式設計的深入認識。而ASP.NET Web應用大都是寄宿於IIS上的。html
HTTP請求沿着IIS和ASP.NET管道流動,在這個過程當中完成處理,最後獲得相應的HTTP響應,發送回客戶端。而不一樣的IIS版本,處理方式有着不小的差別。程序員
IIS 5.x 運行在進程InetInfo.exe中,該進程中寄宿着名爲W3SVC(World Wide Web Publishing Service)的windows服務。這個服務主要負責3個任務,以下圖:web
當監測到某個HTTP請求時,IIS首先依據擴展名肯定是靜態資源,亦或動態資源。前者直接返回文件內容,後者經過IIS的映射腳本找到相應的ISAPI動態連接庫。而後這個動態連接庫會被加載入IIS進程,隨後ISAPI會建立工做進程。工做進程運行在託管環境,經過命名管道(Named Pipes)與IIS進程通訊。每一個Web應用都運行在獨立的應用程序域(Application Domain)中,映射一個IIS虛擬目錄。而全部的應用程序域都在同一個工做進程中。數據庫
ISAPI(Internet Server Appliaction Programming Interface)是一套本地的WIN32 API,是IIS和其餘動態Web應用或平臺之間的紐帶。開發者可使用這些接口深刻到IIS,讓IIS支持各類其餘處理程序。ISAPI是自定義Web請求處理中第一個IIS入口點。windows
根據擴展名的不一樣,依據映射腳本選擇不一樣的ISAPI動態連接庫處理:api
asp.net -》 aspnet_isapi.dll緩存
php -》 php4isapi.dllsass
能夠在IIS中配置,好比把*.html文件也當動態資源處理,這樣就能夠在經典模式中對靜態頁作一些驗證、攔截、改寫等操做,如僞靜態或靜態頁生成功能的實現。安全
篩選器則如同在應用中的AOP模式同樣,爲請求處理過程橫向增添一些操做,如日誌記錄,身份驗證等。
1. ISAPI被加載到InetInfo.exe進程中,它和工做進程之間是跨線程通訊,儘管使用命名管道,但仍然會帶來性能的瓶頸。
2. 全部Web應用運行在同一個工做進程,雖然有基於應用程序域的隔離,但不能從根本上解決一個應用對另外一個應用的影響。
1. 引入程序池的機制,能夠建立一個或多個應用程序池,每一個應用程序池對於一個獨立的工做進程。一個應用程序池能夠承載一個或多個Web應用。
2. ISAPI被直接加載到工做進程中
3. W3SVC服務從InetInfo.exe進程中脫離出來,運行在另外一個進程SvcHost.exe中。而元數據庫Metabase依然存在於InetInfo.exe中。
4. 引入名爲HTTP.SYS的HTTP監聽器,以驅動程序的方式運行在windows的內核模式下,是windows TCP/IP網絡子系統的一部分。它已經不屬於IIS了,它的配置信息並無保存在IIS元數據庫(Metabase)中,而是定義在註冊表中。
1. 請求的接收
http.sys組件監聽到HTTP請求後,聯繫W3SVC,後者會根據IIS中的 Metabase 查看基於該 Request 的 Application 屬於哪一個Application Pool。若是該Application Pool不存在,則建立之、不然直接將 Request 發到對應Application Pool 的 Queue中。
2. 請求的傳遞
每一個 Application Pool 都對應着一個Worker Process(w3wp.exe)。W3SVC會依據在IIS Metabase 中維護着的 Application Pool 和w3wp的映射關係,將存在於某個Application Pool Queue中的Request傳遞給對應的worker Process。
3. 請求的處理
worker process不存在時,會自動建立,而在其初始化的時候,會加載ASP.NET ISAPI,從而在w3wp.exe內部,ASP.NET以IIS API extension的方式外加到IIS。
ASP.NET ISAPI進而加載CLR,爲ASP.NET Application建立一個託管的運行環境。
在CLR初始化的時候會加載兩個重要的對象:AppManagerAppDomainFactory和ISAPIRuntime。經過AppManagerAppDomainFactory的Create方法爲Application建立一個Application Domain;經過ISAPIRuntime的ProcessRequest封裝Request,進而將請求轉入到ASP.NET Http Runtime Pipeline。
1. 引入進程激活服務WAS(Windows Process Activation Service)分流了W3SVC的部分功能。WAS爲IIS引入了對非HTTP協議的支持。
2. IIS的配置信息再也不基於Metabase,而是大都存放於XML文件中,基本配置則存放在applicationHost.config。
3. 引入集成管道
IIS7目前有2個模式: 經典模式和集成模式。
經典模式的W3WP.exe工做方式就是IIS 5.x 、IIS 6的模式。即: IIS ISAPI extension,也就是使用 aspnet_isapi.dll。
經典模式中IIS和ASP.NET是兩個獨立的管道,在各自的管轄範圍內,各自具備本身的一套機制對HTTP請求進行處理。兩個管道經過ISAPI實現連通,IIS是第一道屏障,當對HTTP請求進行必要的前期處理(身份驗證等)後,IIS經過ISAPI將請求分發給ASP.NET管道。當ASP.NET在自身管道範圍內完成對HTTP的處理後,處理結果會在返回IIS,IIS對其多後期的處理(日誌記錄、壓縮等)後生成HTTP回覆對請求予以響應。
侷限:
1. 相同操做的重複執行。如兩個管道都要進行身份驗證。
2. 動態文件和靜態文件的處理不一致,靜態文件是不進ASP.NET管道的。那麼ASP.NET管道中的一些功能就不能做用於這些基於靜態文件的請求。
3. IIS難以擴展,由於ISAPI是基於win32的非託管API。
IIS7集成模式則讓IIS集成了.NET功能(再也不依靠以前IIS版本的aspnet_isapi.dll)。
好處:
1. 容許經過本地代碼和託管代碼兩種方式定義IIS Module,這些IIS Module 註冊到IIS中將造成一個通用的請求處理管道,可以處理全部的請求。
2. 將Asp.Net提供的一些強大功能應用到原來難以企及的地方,好比URL重寫功能置於身份驗證以前。
3. 採用相同的方式實現、配置。檢測和支持一些服務器特性,好比Module、Handler映射、定製錯誤配置等。
4. 在集成模式下全部的請求都要通過.Net來處理(包括Html,PHP等),也由於.Net的諸多功能成爲IIS的一部分,性能上也獲得了提高。
變化:
1. Application的初始化被包括在IIS 8.0中, 而在IIS 7.5中 Application 初始化(RC)被做爲一個外帶模塊。
2. IIS 8.0的管理工具已經爲ASP.net 4.5功能更新。
3. IIS 8.0 集成了SSL認證。
4. IIS 8.0 CPU節流已經獲得更新,且包括額外的節流選項。
5. IIS 8.0 集成動態IP地址的限制功能。
6. IIS 8.0 集成了FTP嘗試登錄限制功能。
7. IIS 8.0 在NUMA 上的多核擴展。
----
由非託管代碼生成的HttpWorkerRequest對象,包含當前請求的全部信息。
在經典模式下請求被封裝爲System.Web.Hosting.ISAPIWorkerRequest,而在集成模式下請求則會被封裝爲System.Web.Hosting.IIS7WorkerRequest,它們都是HttpWorkerRequest的子類。
HttpWorkerRequest對象會被傳遞給HttpRuntime,在咱們的頁面中能夠直接經過它取得原始的請求信息。
ASP.NET ISAPI運行在一個非託管環境之中。通過一系列COM級別的class調用,最終的調用降臨到一個託管的、繼承自System.Web.Hosting.ISAPIRuntime類的對象上。ISAPIRuntime 是一個特殊的class,他實現了接口System.Web.Hosting.IISAPIRuntime。這是一個基於COM的Interface,也就是說Caller能夠經過COM的方式調用實現該Interface的Class的對象。在這裏,這個最初的Caller就是ASP.NET ISAPI。ASP.NET ISAPI經過調用System.Web.Hosting.ISAPIRuntime Instance的ProcessRequest方法,進而從非託管的環境進入了託管的環境。
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("08a2c56f-7c16-41c1-a8be-432917a1a2d1")] public interface IISAPIRuntime { void StartProcessing(); void StopProcessing(); [return: MarshalAs(UnmanagedType.I4)] int ProcessRequest([In] IntPtr ecb, [In, MarshalAs(UnmanagedType.I4)] int useProcessModel); void DoGCCollect(); }
經過System.Web.Hosting.IISAPIRuntime 接口中的ProcessRequest方法的簽名,能夠看出該方法包含兩個參數,其中一個是名爲ecb的Unmanaged Pointer,另外一個是useProcessModel。
ECB全稱是Execution Control Block,在整個Http Request Processing過程當中起着很是重要的做用。
ISAPI顧名思義,就是實現了一些基於Internet Server的API。aspnet_isapi.dll實現了這些API,對於IIS來講,它能夠調用這些API進入托管的環境實現對ISAPIRuntime的調用,對於ISAPIRuntime來講,它須要調用ASP.NET ISAPI實現一些必要的功能,好比得到Server Variable的數據,得到經過Post Mehod傳回Server的數據;以及最終將Response的內容返回給ASP.NET ISAPI,並經過ASP.NET ISAPI返回到Client。通常地ISAPIRuntime不能直接調用ASP.NET ISAPI,而是經過一個對象指針實現對其的調用,這個對象就是ECB,ECB實現了對ISAPI的訪問。
特別須要強調的是,ISAPI對ISAPIRutime的調用是異步的,也就是說ISAPI調用ISAPIRutime以後當即返回。這主要是出於Performance和Responsibility考慮的,由於ASP.NET Application天生就是一個多線程的應用,爲了具備更好的響應能力,異步操做是最有效的解決方式。可是這裏就會有一個問題,咱們對ASP.NET 資源的調用本質上是一個Request/Response的Message Exchange Pattern,異步調用每每意味着ISAPI將Request傳遞給ISAPIRuntime,將不能獲得ISAPIRuntime最終生成的Response,這顯然是不能接受的。而ECB解決了這個問題,ISAPI在調用ISAPIRutime的ProcessRequest方法時會將本身對應的ECB的指針傳給它,ISAPIRutime不但能夠將最終生成的Response返回給ISAPI,還能經過ECB調用ISAPI得到一些所需的數據。
ISAPIRutime的ProcessRequest的實現:
[SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] // DevDiv #180492 public int ProcessRequest(IntPtr ecb, int iWRType) { IntPtr pHttpCompletion = IntPtr.Zero; if (iWRType == WORKER_REQUEST_TYPE_IN_PROC_VERSION_2) { pHttpCompletion = ecb; ecb = UnsafeNativeMethods.GetEcb(pHttpCompletion); } ISAPIWorkerRequest wr = null; try { bool useOOP = (iWRType == WORKER_REQUEST_TYPE_OOP); wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP); wr.Initialize(); // check if app path matches (need to restart app domain?) String wrPath = wr.GetAppPathTranslated(); String adPath = HttpRuntime.AppDomainAppPathInternal; if (adPath == null || StringUtil.EqualsIgnoreCase(wrPath, adPath)) { HttpRuntime.ProcessRequestNoDemand(wr); return 0; } else { // need to restart app domain HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString(SR.Hosting_Phys_Path_Changed, adPath, wrPath)); return 1; } } catch(Exception e) { try { WebBaseEvent.RaiseRuntimeError(e, this); } catch {} // Have we called HSE_REQ_DONE_WITH_SESSION? If so, don't re-throw. if (wr != null && wr.Ecb == IntPtr.Zero) { if (pHttpCompletion != IntPtr.Zero) { UnsafeNativeMethods.SetDoneWithSessionCalled(pHttpCompletion); } // if this is a thread abort exception, cancel the abort if (e is ThreadAbortException) { Thread.ResetAbort(); } // IMPORTANT: if this thread is being aborted because of an AppDomain.Unload, // the CLR will still throw an AppDomainUnloadedException. The native caller // must special case COR_E_APPDOMAINUNLOADED(0x80131014) and not // call HSE_REQ_DONE_WITH_SESSION more than once. return 0; } // re-throw if we have not called HSE_REQ_DONE_WITH_SESSION throw; } } int IISAPIRuntime2.ProcessRequest(IntPtr ecb, int iWRType) { return ProcessRequest(ecb, iWRType); }
ISAPI在調用ISAPIRuntime的時候將對應的ISAPI ECB Pointer做爲參數傳遞給了ProcessRequest方法,這個ECB pointer能夠當作是託管環境和非託管環境進行數據交換的惟一通道,Server Variable和Request Parameter經過它傳入ASP.NET做爲進一步處理的依據,ASP.NET最後生成的Response經過它傳遞給ISAPI,並進一步傳遞給IIS最終返回到Client端。
ISAPIRutime的ProcessRequest方法完成下面兩個任務:
1. 經過傳入的ECB和iWRType建立一個叫作ISAPIWorkerRequest的對象
2. 調用HttpRuntime.ProcessRequestNoDemand(wr),真正進入了ASP.NET Runtime Pipeline。
ISAPIWorkerRequest是一個Abstract class,它已經過ECB建立基於當前Request的Context的信息,針對不一樣的IIS版本,具備不一樣的ISAPIWorkerRequest 子類,ProcessRequest經過ISAPI傳入的iWRType來建立不一樣HttpWorkerRequest(internal abstract class ISAPIWorkerRequest : HttpWorkerRequest),從而屏蔽了不一樣IIS的差別。
internal static ISAPIWorkerRequest CreateWorkerRequest(IntPtr ecb, bool useOOP) { ISAPIWorkerRequest wr = null; if (useOOP) { EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, false); wr = new ISAPIWorkerRequestOutOfProc(ecb); } else { int version = UnsafeNativeMethods.EcbGetVersion(ecb) >> 16; if (version >= 7) { EtwTrace.TraceEnableCheck(EtwTraceConfigType.IIS7_ISAPI, ecb); } else { EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero); } if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, true); if (version >= 7) { wr = new ISAPIWorkerRequestInProcForIIS7(ecb); } else if (version == 6) { wr = new ISAPIWorkerRequestInProcForIIS6(ecb); } else { wr = new ISAPIWorkerRequestInProc(ecb); } } return wr; }
[Guid("c96cb854-aec2-4208-9ada-a86a96860cb6")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IPipelineRuntime { IntPtr GetAsyncCompletionDelegate(); IntPtr GetAsyncDisconnectNotificationDelegate(); IntPtr GetDisposeDelegate(); IntPtr GetExecuteDelegate(); IntPtr GetPrincipalDelegate(); IntPtr GetRoleDelegate(); void InitializeApplication(IntPtr appContext); void StartProcessing(); void StopProcessing(); }
public IntPtr GetExecuteDelegate() { if (IntPtr.Zero == _executeDelegatePointer) { lock (_delegatelock) { if (IntPtr.Zero == _executeDelegatePointer) { ExecuteFunctionDelegate d = new ExecuteFunctionDelegate(ProcessRequestNotification); if (null != d) { IntPtr p = Marshal.GetFunctionPointerForDelegate(d); if (IntPtr.Zero != p) { Thread.MemoryBarrier(); _executeDelegate = d; _executeDelegatePointer = p; } } } } } return _executeDelegatePointer; } internal static int ProcessRequestNotification( IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, int flags) { try { return ProcessRequestNotificationHelper(rootedObjectsPointer, nativeRequestContext, moduleData, flags); } catch(Exception e) { ApplicationManager.RecordFatalException(e); throw; } } internal static int ProcessRequestNotificationHelper( IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, int flags) { IIS7WorkerRequest wr = null; HttpContext context = null; RequestNotificationStatus status = RequestNotificationStatus.Continue; RootedObjects root; bool workerRequestWasJustCreated = false; if (rootedObjectsPointer == IntPtr.Zero) { InitializeRequestContext(nativeRequestContext, flags, out wr, out context); workerRequestWasJustCreated = true; if (context == null) { return (int)RequestNotificationStatus.FinishRequest; } root = RootedObjects.Create(); root.HttpContext = context; root.WorkerRequest = wr; root.WriteTransferEventIfNecessary(); context.RootedObjects = root; IIS.MgdSetManagedHttpContext(nativeRequestContext, root.Pointer); } else { root = RootedObjects.FromPointer(rootedObjectsPointer); context = root.HttpContext; wr = root.WorkerRequest as IIS7WorkerRequest; } Debug.Assert(root != null, "We should have a RootedObjects instance by this point."); Debug.Assert(wr != null, "We should have an IIS7WorkerRequest instance by this point."); using (root.WithinTraceBlock()) { if (workerRequestWasJustCreated) { AspNetEventSource.Instance.RequestStarted(wr); } int currentModuleIndex; bool isPostNotification; int currentNotification; IIS.MgdGetCurrentNotificationInfo(nativeRequestContext, out currentModuleIndex, out isPostNotification, out currentNotification); // If the HttpContext is null at this point, then we've already transitioned this request to a WebSockets request. // The WebSockets module should already be running, and asynchronous module-level events (like SendResponse) are // ineligible to be hooked by managed code. if (context == null || context.HasWebSocketRequestTransitionStarted) { return (int)RequestNotificationStatus.Continue; } // It is possible for a notification to complete asynchronously while we're in // a call to IndicateCompletion, in which case a new IIS thread might enter before // the call to IndicateCompletion returns. If this happens, block the thread until // IndicateCompletion returns. But never block a SendResponse notification, because // that can cause the request to hang (DevDiv Bugs 187441). if (context.InIndicateCompletion && context.ThreadInsideIndicateCompletion != Thread.CurrentThread && RequestNotification.SendResponse != (RequestNotification)currentNotification) { while (context.InIndicateCompletion) { Thread.Sleep(10); } } // RQ_SEND_RESPONSE fires out of band and completes synchronously only. // The pipeline must be reentrant to support this, so the notification // context for the previous notification must be saved and restored. NotificationContext savedNotificationContext = context.NotificationContext; bool cancellable = context.IsInCancellablePeriod; bool locked = false; try { if (cancellable) { context.EndCancellablePeriod(); } bool isReEntry = (savedNotificationContext != null); if (isReEntry) { context.ApplicationInstance.AcquireNotifcationContextLock(ref locked); } context.NotificationContext = new NotificationContext(flags /*CurrentNotificationFlags*/, isReEntry); Action<RequestNotificationStatus> verifierCheck = null; if (AppVerifier.IsAppVerifierEnabled) { verifierCheck = AppVerifier.GetRequestNotificationStatusCheckDelegate(context, (RequestNotification)currentNotification, isPostNotification); } status = HttpRuntime.ProcessRequestNotification(wr, context); if (verifierCheck != null) { AppVerifier.InvokeVerifierCheck(verifierCheck, status); } } finally { if (status != RequestNotificationStatus.Pending) { // if we completed the notification, pop the notification context stack // if this is an asynchronous unwind, then the completion will clear the context context.NotificationContext = savedNotificationContext; // DevDiv 112755 restore cancellable state if its changed if (cancellable && !context.IsInCancellablePeriod) { context.BeginCancellablePeriod(); } else if (!cancellable && context.IsInCancellablePeriod) { context.EndCancellablePeriod(); } } if (locked) { context.ApplicationInstance.ReleaseNotifcationContextLock(); } } if (status != RequestNotificationStatus.Pending) { // The current notification may have changed due to the HttpApplication progressing the IIS state machine, so retrieve the info again. IIS.MgdGetCurrentNotificationInfo(nativeRequestContext, out currentModuleIndex, out isPostNotification, out currentNotification); // WOS 1785741: (Perf) In profiles, 8% of HelloWorld is transitioning from native to managed. // The fix is to keep managed code on the stack so that the AppDomain context remains on the // thread, and we can re-enter managed code without setting up the AppDomain context. // If this optimization is possible, MgdIndicateCompletion will execute one or more notifications // and return PENDING as the status. ThreadContext threadContext = context.IndicateCompletionContext; // DevDiv 482614: // Don't use local copy to detect if we can call MgdIndicateCompletion because another thread // unwinding from MgdIndicateCompletion may be changing context.IndicateCompletionContext at the same time. if (!context.InIndicateCompletion && context.IndicateCompletionContext != null) { if (status == RequestNotificationStatus.Continue) { try { context.InIndicateCompletion = true; Interlocked.Increment(ref _inIndicateCompletionCount); context.ThreadInsideIndicateCompletion = Thread.CurrentThread; IIS.MgdIndicateCompletion(nativeRequestContext, ref status); } finally { context.ThreadInsideIndicateCompletion = null; Interlocked.Decrement(ref _inIndicateCompletionCount); // Leave will have been called already if the last notification is returning pending // DTS267762: Make sure InIndicateCompletion is released, not based on the thread context state // Otherwise the next request notification may deadlock if (!threadContext.HasBeenDisassociatedFromThread || context.InIndicateCompletion) { lock (threadContext) { if (!threadContext.HasBeenDisassociatedFromThread) { threadContext.DisassociateFromCurrentThread(); } context.IndicateCompletionContext = null; context.InIndicateCompletion = false; } } } } else { if (!threadContext.HasBeenDisassociatedFromThread || context.InIndicateCompletion) { lock (threadContext) { if (!threadContext.HasBeenDisassociatedFromThread) { threadContext.DisassociateFromCurrentThread(); } context.IndicateCompletionContext = null; context.InIndicateCompletion = false; } } } } } if (context.HasWebSocketRequestTransitionStarted && status == RequestNotificationStatus.Pending) { // At this point, the WebSocket module event (PostEndRequest) has executed and set up the appropriate contexts for us. // However, there is a race condition that we need to avoid. It is possible that one thread has kicked off some async // work, e.g. via an IHttpAsyncHandler, and that thread is unwinding and has reached this line of execution. // Meanwhile, the IHttpAsyncHandler completed quickly (but asynchronously) and invoked MgdPostCompletion, which // resulted in a new thread calling ProcessRequestNotification. If this second thread starts the WebSocket transition, // then there's the risk that *both* threads might attempt to call WebSocketPipeline.ProcessRequest, which could AV // the process. // // We protect against this by allowing only the thread which started the transition to complete the transition, so in // the above scenario the original thread (which invoked the IHttpAsyncHandler) no-ops at this point and just returns // Pending to its caller. if (context.DidCurrentThreadStartWebSocketTransition) { // We'll mark the HttpContext as complete, call the continuation to kick off the socket send / receive loop, and return // Pending to IIS so that it doesn't advance the state machine until the WebSocket loop completes. root.ReleaseHttpContext(); root.WebSocketPipeline.ProcessRequest(); } } return (int)status; } } private static void InitializeRequestContext(IntPtr nativeRequestContext, int flags, out IIS7WorkerRequest wr, out HttpContext context) { wr = null; context = null; try { bool etwEnabled = ((flags & HttpContext.FLAG_ETW_PROVIDER_ENABLED) == HttpContext.FLAG_ETW_PROVIDER_ENABLED); // this may throw, e.g. if the request Content-Length header has a value greater than Int32.MaxValue wr = IIS7WorkerRequest.CreateWorkerRequest(nativeRequestContext, etwEnabled); // this may throw, e.g. see WOS 1724573: ASP.Net v2.0: wrong error code returned when ? is used in the URL context = new HttpContext(wr, false); } catch { // treat as "400 Bad Request" since that's the only reason the HttpContext.ctor should throw IIS.MgdSetBadRequestStatus(nativeRequestContext); } }
http://referencesource.microsoft.com/#System.Web/Hosting/IPipelineRuntime.cs
HttpWorkerRequest做爲參數傳入HttpRuntime.ProcessRequestNoDemand的調用。HttpRuntime.ProcessRequestNoDemand最終體如今調用ProcessRequestInternal。下面是真個方法的實現
private void ProcessRequestInternal(HttpWorkerRequest wr) { // Count active requests Interlocked.Increment(ref _activeRequestCount); if (_disposingHttpRuntime) { // Dev11 333176: An appdomain is unloaded before all requests are served, resulting in System.AppDomainUnloadedException during isapi completion callback // // HttpRuntim.Dispose could have already finished on a different thread when we had no active requests // In this case we are about to start or already started unloading the appdomain so we will reject the request the safest way possible try { wr.SendStatus(503, "Server Too Busy"); wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8"); byte[] body = Encoding.ASCII.GetBytes("<html><body>Server Too Busy</body></html>"); wr.SendResponseFromMemory(body, body.Length); // this will flush synchronously because of HttpRuntime.ShutdownInProgress wr.FlushResponse(true); wr.EndOfRequest(); } finally { Interlocked.Decrement(ref _activeRequestCount); } return; } // Construct the Context on HttpWorkerRequest, hook everything together HttpContext context; try { context = new HttpContext(wr, false /* initResponseWriter */); } catch { try { // If we fail to create the context for any reason, send back a 400 to make sure // the request is correctly closed (relates to VSUQFE3962) wr.SendStatus(400, "Bad Request"); wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8"); byte[] body = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>"); wr.SendResponseFromMemory(body, body.Length); wr.FlushResponse(true); wr.EndOfRequest(); return; } finally { Interlocked.Decrement(ref _activeRequestCount); } } wr.SetEndOfSendNotification(_asyncEndOfSendCallback, context); HostingEnvironment.IncrementBusyCount(); try { // First request initialization try { EnsureFirstRequestInit(context); } catch { // If we are handling a DEBUG request, ignore the FirstRequestInit exception. // This allows the HttpDebugHandler to execute, and lets the debugger attach to // the process (VSWhidbey 358135) if (!context.Request.IsDebuggingRequest) { throw; } } // Init response writer (after we have config in first request init) // no need for impersonation as it is handled in config system context.Response.InitResponseWriter(); // Get application instance IHttpHandler app = HttpApplicationFactory.GetApplicationInstance(context); if (app == null) throw new HttpException(SR.GetString(SR.Unable_create_app_object)); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, app.GetType().FullName, "Start"); if (app is IHttpAsyncHandler) { // asynchronous handler IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app; context.AsyncAppHandler = asyncHandler; asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context); } else { // synchronous handler app.ProcessRequest(context); FinishRequest(context.WorkerRequest, context, null); } } catch (Exception e) { context.Response.InitResponseWriter(); FinishRequest(wr, context, e); } }
internal static RequestNotificationStatus ProcessRequestNotification(IIS7WorkerRequest wr, HttpContext context) { return _theRuntime.ProcessRequestNotificationPrivate(wr, context); } private RequestNotificationStatus ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) { RequestNotificationStatus status = RequestNotificationStatus.Pending; try { int currentModuleIndex; bool isPostNotification; int currentNotification; // setup the HttpContext for this event/module combo UnsafeIISMethods.MgdGetCurrentNotificationInfo(wr.RequestContext, out currentModuleIndex, out isPostNotification, out currentNotification); context.CurrentModuleIndex = currentModuleIndex; context.IsPostNotification = isPostNotification; context.CurrentNotification = (RequestNotification) currentNotification; Debug.Trace("PipelineRuntime", "HttpRuntime::ProcessRequestNotificationPrivate: notification=" + context.CurrentNotification.ToString() + ", isPost=" + context.IsPostNotification + ", moduleIndex=" + context.CurrentModuleIndex); IHttpHandler handler = null; if (context.NeedToInitializeApp()) { Debug.Trace("FileChangesMonitorIgnoreSubdirChange", "*** FirstNotification " + DateTime.Now.ToString("hh:mm:ss.fff", CultureInfo.InvariantCulture) + ": _appDomainAppId=" + _appDomainAppId); // First request initialization try { EnsureFirstRequestInit(context); } catch { // If we are handling a DEBUG request, ignore the FirstRequestInit exception. // This allows the HttpDebugHandler to execute, and lets the debugger attach to // the process (VSWhidbey 358135) if (!context.Request.IsDebuggingRequest) { throw; } } context.Response.InitResponseWriter(); handler = HttpApplicationFactory.GetApplicationInstance(context); if (handler == null) throw new HttpException(SR.GetString(SR.Unable_create_app_object)); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, handler.GetType().FullName, "Start"); HttpApplication app = handler as HttpApplication; if (app != null) { // associate the context with an application instance app.AssignContext(context); } } // this may throw, and should be called after app initialization wr.SynchronizeVariables(context); if (context.ApplicationInstance != null) { // process request IAsyncResult ar = context.ApplicationInstance.BeginProcessRequestNotification(context, _requestNotificationCompletionCallback); if (ar.CompletedSynchronously) { status = RequestNotificationStatus.Continue; } } else if (handler != null) { // HttpDebugHandler is processed here handler.ProcessRequest(context); status = RequestNotificationStatus.FinishRequest; } else { status = RequestNotificationStatus.Continue; } } catch (Exception e) { status = RequestNotificationStatus.FinishRequest; context.Response.InitResponseWriter(); // errors are handled in HttpRuntime::FinishRequestNotification context.AddError(e); } if (status != RequestNotificationStatus.Pending) { // we completed synchronously FinishRequestNotification(wr, context, ref status); } #if DBG Debug.Trace("PipelineRuntime", "HttpRuntime::ProcessRequestNotificationPrivate: status=" + status.ToString()); #endif return status; }
首先經過HttpWorkerRequest建立按一個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基本上能夠當作是真個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對象負責處理分發給它的HTTP請求,一個HttpApplication對象在某個時刻只能處理一個請求。Asp.net採用對象池機制來建立和獲取HttpApplication對象。它的工做方式是經過在不一樣階段出發不一樣Event來調用咱們註冊的Event Hander。
一個Application並非只運行在一個AppDomain之中。由於存在一種特殊的場景:在當前Application正在處理Request的時候,咱們把web.config以及其餘一些相關文件修改了,並且這種改變是能夠立刻被ASP.NET檢測到的,爲了使咱們的變更可以及時生效,對於改動後的第一個Request,ASP.NET會爲期建立一個新的AppDomain,而對於原來的AppDomain,也許還在處理修改前的Request,全部原來的Appdomain會持續到將原來的Request處理結束以後,因此對於一個Application,可能出現多個AppDomain並存的現象。
咱們能夠註冊相應的事件,將處理邏輯注入到HttpApplication處理請求的某個階段。
名稱 |
描述 |
BeginRequest |
HTTP管道開始處理請求時,會觸發BeginRequest事件。這個事件標誌着Asp.net服務器處理工做的開始,也是程序員在Asp.net中針對請求所可以處理的第一個事件。 |
AuthenticateRequest,PostAuthenticateRequest |
ASP.NET前後觸發這兩個事件,使安全模塊對請求進行身份驗證(肯定請求用戶的身份以實現安全機制)。前者驗證身份時觸發,後者是已經驗證身份後觸發。 檢查後的用戶能夠經過HttpContext的User屬性獲取到 if (HttpContext.Current.User.Identity.IsAuthenticated) |
AuthorizeRequest,PostAuthorizeRequest |
ASP.NET前後觸發這兩個事件,使安全模塊對請求進程受權(檢查權限)。若是沒有經過安全檢查,通常狀況下,都跳過剩下的事件,直接觸發LogRequest事件。 |
ResolveRequestCache,PostResolveRequestCache |
ASP.NET前後觸發這兩個事件,以使緩存模塊利用緩存的直接對請求直接進程響應(緩存模塊能夠將響應內容進程緩存,對於後續的請求,直接將緩存的內容返回,從而提升響應能力)。 |
MapRequestHandler, PostMapRequestHandler |
對於訪問不一樣的資源類型,ASP.NET具備不一樣的HttpHandler對其進程處理。對於每一個請求,ASP.NET會經過擴展名選擇匹配相應的HttpHandler類型,在匹配先後觸發這兩個事件。 其中MapRequestHandler在Asp.net 4.0後纔可用 |
AcquireRequestState,PostAcquireRequestState |
ASP.NET前後觸發這兩個事件,使狀態管理模塊獲取基於當前請求相應的狀態,好比SessionState |
PreRequestHandlerExecute,PostRequestHandlerExecute |
ASP.NET最終經過一請求資源類型相對應的HttpHandler實現對請求的處理,在實行HttpHandler先後,這兩個實現被前後觸發 |
ReleaseRequestState,PostReleaseRequestState |
ASP.NET前後觸發這兩個事件,使狀態管理模塊釋放基於當前請求相應的狀態 |
UpdateRequestCache,PostUpdateRequestCache |
ASP.NET前後觸發這兩個事件,以使緩存模塊將HttpHandler處理請求獲得的相應保存到輸出緩存中 |
LogRequest,PostLogRequest |
ASP.NET前後觸發這兩個事件爲當前請求進程日誌記錄(Asp.net 4.0) |
EndRequest |
整個請求處理完成後,EndRequest事件被觸發
|
PreSendRequestHeaders |
當準備經過HttpResponse迴應發送HTTP的Header以前觸發。 |
PreSendRequestContent |
當準備經過HttpResponse迴應發送HTTP的Body內容以前觸發。 |
Error |
在出現未處理的錯誤時觸發 |
RequestCompleted |
處理完成後觸發,此時Context已不存在 |
HttpModule是ASP.NET管道提供的擴展機制,經過它將所需操做注入到HttpApplication處理請求的某個階段。
HttpApplication對象初始化過程當中,會根據配置文件加載並初始化註冊的全部HttpModule對象。
public class Module1:IHttpModule { public void Init(HttpApplication context) { context.BeginRequest += BeginRequest; context.AuthenticateRequest += AuthenticateRequest; context.PostAuthenticateRequest += PostAuthenticateRequest; context.AuthorizeRequest += AuthorizeRequest; context.PostAuthorizeRequest += PostAuthorizeRequest; context.ResolveRequestCache += ResolveRequestCache; context.PostResolveRequestCache += PostResolveRequestCache; context.MapRequestHandler += MapRequestHandler; context.PostMapRequestHandler += PostMapRequestHandler; context.AcquireRequestState += AcquireRequestState; context.PostAcquireRequestState += PostAcquireRequestState; context.PreRequestHandlerExecute += PreRequestHandlerExecute; context.PostRequestHandlerExecute += PostRequestHandlerExecute; context.ReleaseRequestState += ReleaseRequestState; context.PostReleaseRequestState += PostReleaseRequestState; context.UpdateRequestCache += UpdateRequestCache; context.PostUpdateRequestCache += PostUpdateRequestCache; context.LogRequest += LogRequest; context.PostLogRequest += PostLogRequest; context.EndRequest += EndRequest; context.PreSendRequestHeaders += PreSendRequestHeaders; context.PreSendRequestContent += PreSendRequestContent; context.Error += Error; context.RequestCompleted += RequestCompleted; context.Disposed += AddOnDisposed; } public void Dispose() { } private void Write(object sender,string msg) { HttpApplication application = (HttpApplication)sender; if (application.Context != null) { application.Context.Response.Write("<br>Module1." + msg); } else { utils.log("Module1." + msg + ",Context不存在!", "log.txt"); } } protected void AddOnDisposed(object sender, EventArgs e) { Write(sender, "Disposed"); } protected void BeginRequest(object sender, EventArgs e) { Write(sender, "BeginRequest"); } protected void AuthenticateRequest(object sender, EventArgs e) { Write(sender, "AuthenticateRequest"); } protected void PostAuthenticateRequest(object sender, EventArgs e) { Write(sender, "PostAuthenticateRequest"); } protected void AuthorizeRequest(object sender, EventArgs e) { Write(sender, "AuthorizeRequest"); } protected void PostAuthorizeRequest(object sender, EventArgs e) { Write(sender, "PostAuthorizeRequest"); } protected void ResolveRequestCache(object sender, EventArgs e) { Write(sender, "ResolveRequestCache"); } protected void PostResolveRequestCache(object sender, EventArgs e) { Write(sender, "PostResolveRequestCache"); } protected void MapRequestHandler(object sender, EventArgs e) { Write(sender, "MapRequestHandler"); } protected void PostMapRequestHandler(object sender, EventArgs e) { Write(sender, "PostMapRequestHandler"); } protected void AcquireRequestState(object sender, EventArgs e) { Write(sender, "AcquireRequestState"); } protected void PostAcquireRequestState(object sender, EventArgs e) { Write(sender, "PostAcquireRequestState"); } protected void PreRequestHandlerExecute(object sender, EventArgs e) { Write(sender, "PreRequestHandlerExecute"); } protected void PostRequestHandlerExecute(object sender, EventArgs e) { Write(sender, "PostRequestHandlerExecute"); } protected void ReleaseRequestState(object sender, EventArgs e) { Write(sender, "ReleaseRequestState"); } protected void PostReleaseRequestState(object sender, EventArgs e) { Write(sender, "PostReleaseRequestState"); } protected void UpdateRequestCache(object sender, EventArgs e) { Write(sender, "UpdateRequestCache"); } protected void PostUpdateRequestCache(object sender, EventArgs e) { Write(sender, "PostUpdateRequestCache"); } protected void LogRequest(object sender, EventArgs e) { Write(sender, "LogRequest"); } protected void PostLogRequest(object sender, EventArgs e) { Write(sender, "PostLogRequest"); } protected void EndRequest(object sender, EventArgs e) { Write(sender, "EndRequest"); } protected void PreSendRequestHeaders(object sender, EventArgs e) { Write(sender, "PreSendRequestHeaders"); } protected void PreSendRequestContent(object sender, EventArgs e) { Write(sender, "PreSendRequestContent"); } protected void Error(object sender, EventArgs e) { Write(sender, "Error"); } protected void RequestCompleted(object sender, EventArgs e) { Write(sender, "RequestCompleted"); } }
子元素add用來增長一個新的HttpModule;clear將清除前面註冊的全部HttpModule;remove移除指定的HttpModule。
name屬性由咱們本身命名,不必定與類名相同。type屬性由分號「,」分爲兩部分,前面是命名空間及類名,也就是類型名;後面是程序集名。若是咱們將代碼建立在App_Code目錄中,則不須要再指定程序集名。
IIS 7經典模式或以前的版本下,在配置元素system.web下注冊HttpModule。
<system.web> <httpModules> <add name="ModuleTest" type="MVC.ModuleTest" /> </httpModules> </system.web>
IIS 7 集成模式下,在配置元素system.webServer下注冊HttpModule。注意此時的配置元素名稱變爲了modules。
<system.webServer> <modules> <add name="Module1" type="MVC.Module1"/> <add name="Module2" type="MVC.Module2"/> </modules> </system.webServer>
兼容:
<system.web> <httpModules> <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web"/> </httpModules> </system.web> <system.webServer> <modules> <remove name="ApplicationInsightsWebTracking"/> <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler"/> </modules> </system.webServer>
Module2與Module1的實現基本一致。
RequestCompleted 沒有在頁面上輸出,由於RequestCompleted事件觸發時,Context已經被不存在了。
在Module1中對AuthenticateRequest事件添加語句
HttpContext.Current.Response.End();
或 HttpContext.Current.ApplicationInstance.CompleteRequest();
主動結束對請求的後續處理。則直接跳過了中間的處理過程。
經過HttpApplication.Modules能夠遍歷當前已註冊的Module
var app = ((HttpApplication)sender); var modules = app.Modules; foreach (var key in modules.AllKeys) { app.Context.Response.Write("<br>"+ utils.KeepLength(key,30) + ":" + modules[key].ToString()); }
一、OutputCacheModule完成Asp.net的輸出緩存管理工做:
OutputCacheModule的配置參數經過system.web配置元素的caching子元素的outputCache元素進行定義。當啓用輸出緩存以後(啓用仍是經過配置文件,下同),OutputCacheModule將註冊HttpApplication的ResolveRequestCache和UpdateRequestCache兩個事件完成輸出緩存的管理。
二、SessionStateModule完成Session的管理工做:
這個Module的配置參數經過配置文件中的system.web配置元素的sessionState子元素進行配置。當啓用Session狀態管理以後,SessionStateModule將註冊HttpApplication的AcquireRequestState、ReleaseRequestState、EndRequest三個事件完成Session狀態的管理工做。
三、ProfileModule提供個性化數據管理:
這是一個自定義的相似於Session的會話狀態管理,可是,個性化數據的讀取和保存能夠由程序員徹底控制,而且提供了強類型的數據訪問方式。這個Module的配置參數在system.web的子元素profile中進行說明。當啓用了個性化數據管理以後,Module將註冊HttpApplication的AcquireRequestState和EndRequest事件處理。
四、AnonymousIdentificationModule提供匿名用戶的標誌:
是否啓用匿名用戶標誌在配置文件的system.web配置元素的子元素anonymousIdentification中定義,還能夠配置匿名標識的管理方式。因爲在AuthenticateRequest事件中將驗證用戶,獲取用戶名,因此這個Module註冊了PostAuthenticateRequest的事件處理,當用戶沒有通過驗證的時候,爲用戶分配一個惟一的匿名標識。
五、WindowsAuthenticationModule、FormsAuthenticationModule和PassportAuthenticationModule用來完成用戶的驗證工做。
它們經過配置文件中system.web的子元素authentication子元素定義,mode屬性用來指定網站當前使用的驗證方式,也就是哪個Module將被用來完成驗證工做。在啓用驗證的狀況下,FormsAuthenticationModule和PassportAuthenticationModule將註冊HttpApplication的AuthenticateRequest和EndRequest事件進行用戶的驗證處理。WindowsAuthenticationModule將註冊AuthenticateRequest的事件處理。
六、RoleManagerModule、UrlAuthorizationModule、FileAuthorizationModule用來完成用戶的受權管理:
受權管理的配置參數來自system.web的authorization子元素。UrlAuthorizationModule和FileAuthorizationModule註冊了HttpApplication的AuthorizeRequest事件處理,用來檢查Url和文件的訪問受權。RoleManagerModule在Url和文件訪問受權檢查經過以後,經過用戶的標識和角色來完成用戶的受權檢查,RoleManagerModule註冊了HttpApplication的PostAuthenticateRequest和EndRequest事件處理。
MVC項目中:
OutputCache :System.Web.Caching.OutputCacheModule
Session :System.Web.SessionState.SessionStateModule
WindowsAuthentication :System.Web.Security.WindowsAuthenticationModule
DefaultAuthentication :System.Web.Security.DefaultAuthenticationModule
RoleManager :System.Web.Security.RoleManagerModule
UrlAuthorization :System.Web.Security.UrlAuthorizationModule
FileAuthorization :System.Web.Security.FileAuthorizationModule
AnonymousIdentification :System.Web.Security.AnonymousIdentificationModule
Profile :System.Web.Profile.ProfileModule
UrlMappingsModule :System.Web.UrlMappingsModule
ServiceModel-4.0 :System.ServiceModel.Activation.ServiceHttpModule
UrlRoutingModule-4.0 :System.Web.Routing.UrlRoutingModule
ScriptModule-4.0 :System.Web.Handlers.ScriptModule
__DynamicModule_Microsoft.Owin.Host.SystemWeb.OwinHttpModule, Microsoft.Owin.Host.SystemWeb, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35_1ac9b451-2909-4d6b-bbc4-86efa473a0cc :Microsoft.Owin.Host.SystemWeb.OwinHttpModule
__DynamicModule_Microsoft.VisualStudio.Web.PageInspector.Runtime.Tracing.PageInspectorHttpModule, Microsoft.VisualStudio.Web.PageInspector.Runtime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a_203183fe-96d3-43c6-a075-0e794a44e2cf :Microsoft.VisualStudio.Web.PageInspector.Runtime.Tracing.PageInspectorHttpModule
__DynamicModule_System.Web.WebPages.WebPageHttpModule, System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35_875410b2-437d-457c-9dd7-7d6e23a53966 :System.Web.WebPages.WebPageHttpModule
__DynamicModule_System.Web.Optimization.BundleModule, System.Web.Optimization, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35_3d095322-e68f-4d9e-812b-f601d60ca1bf :System.Web.Optimization.BundleModule
能夠經過Global.asax文件對HttpApplication的請求處理行爲進行快捷定製,甚至有些事件只能經過global.asax來完成定製。在Global.asax中按照「{ModulesName/Application}_{Event_Name}」的方法命名規則進行事件註冊。
public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e){} protected void Session_Start(object sender, EventArgs e) { } protected void Application_BeginRequest(object sender, EventArgs e) { } protected void Application_AuthenticateRequest(object sender, EventArgs e) { } protected void Application_Error(object sender, EventArgs e) { } protected void Session_End(object sender, EventArgs e) { } protected void Application_End(object sender, EventArgs e) { } }
只會在第一個HttpApplication對象被建立後調用。
當網站啓動後,第一次請求到達網站以後,Asp.net網站將首先觸發一次這個事件,並且在網站的整個生命週期中,也僅僅觸發一次。
在經典模式下,若是在Application_Start中進行某項初始化操做時發生了異常,網站在第一次被訪問時會由於這個異常而報錯,但隨之的後續頁面若是它們的呈現不會受到以前的初始化失敗所影響,則能夠正常打開,直到某個須要用到這個初始化數據時纔會出現報錯,報錯的延遲會致使調試的困難。
集成模式下,則若是在Application_Start時出錯,則後續無論來多少訪客,刷新多少此頁面,這個報錯將始終存在
經典模式下的應對:
private static Exception s_initException; void Application_Start(object sender, EventArgs e) { try { AppInitializer.Init(); } catch( Exception ex ) { // 記下初始化的異常。 s_initException = ex; } } protected void Application_BeginRequest(object sender, EventArgs e) { // 若是存在初始化異常,就拋出來。 // 直到開發人員發現這個異常,並已解決了異常爲止。 if( s_initException != null ) throw s_initException; }
當網站應用程序被關閉的時候,將觸發這個事件。
每一個用戶訪問網站的第一個頁面時觸發
使用了session.abandon(),或session超時用戶退出後都可觸發.
在出現未處理的錯誤時觸發
protected void Application_Error(object sender, EventArgs e) { var error = Server.GetLastError(); var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500; //若是不是HttpException記錄錯誤信息 if (code != 404) { Exception exception = error.InnerException; Response.Write(exception.Message); //此處郵件或日誌記錄錯誤信息 } Response.Write("出錯!"); Server.ClearError(); }
public class TestEventArgs : EventArgs { public string a { get; private set; } public TestEventArgs(string a) { this.a = a; } } public class ModuleTest : IHttpModule { public delegate void TestEventHandler(object sender, TestEventArgs e); public event TestEventHandler Create; public void Dispose() { } public void Init(HttpApplication context) { Create += method; context.BeginRequest += (sender, e)=>{ Create(sender, new TestEventArgs("TestEventArgs.a")); }; } protected void method(object sender, TestEventArgs e) { ((HttpApplication)sender).Context.Response.Write(string.Format("method in ModuleTest,{0}",e.a)); } }
web.config:
<add name="EventModule" type="MVC.ModuleTest"/>
Global:
protected void EventModule_Create(object sender, TestEventArgs e) { Context.Response.Write(string.Format("<br>ModuleTest_Create in Global,{0}", e == null ? "null" : e.a)); }
輸出:
public class ModuleTest2 : IHttpModule { public event EventHandler DoSth; public event EventHandler PostDoSth; public void Dispose() { } public void Init(HttpApplication context) { context.BeginRequest += AddOnBeginRequest; } protected virtual void AddOnBeginRequest(object sender, EventArgs e) { DoSth?.Invoke(sender, e); ((HttpApplication)sender).Context.Response.Write("<br>do sth!"); PostDoSth?.Invoke(sender, e); } }
web.config:
<add name="EventModule2" type="MVC.ModuleTest2"/>
Global:
public class Global : HttpApplication { protected void Application_BeginRequest(object sender, EventArgs e) { Context.Response.Write("<br>Application_BeginRequest in Global"); } protected void EventModule2_DoSth(object sender, EventArgs e) { Context.Response.Write("<br>EventModule2_DoSth in Global"); } protected void EventModule2_PostDoSth(object sender, EventArgs e) { Context.Response.Write("<br>EventModule2_PostDoSth in Global"); } }
輸出:
對HTTP請求的處理實如今HttpHandler中
自定義接口有:IHttpHandlerFactory,IHttpHandler,IHttpAsyncHandler。自定義後,可在Web.config中註冊,創建與請求路徑之間的映射關係。
在HttpApplication的PostMapRequestHandler事件中,執行默認的映射操做,調用配置文件,查找用於處理當前請求的HttpHandler。
在HttpContext中提供有RemapHandler 方法, 能夠切換當前請求的處理程序,跳過默認映射操做。
public class JpgHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { if (context.Request.UrlReferrer == null) { // 若是UrlReferrer爲空,則顯示一張默認的禁止盜鏈的圖片 WriteImg(context, "/image/error.jpg"); } else { // 若是 UrlReferrer中不包含本身站點主機域名,則顯示一張默認的禁止盜鏈的圖片 if (context.Request.UrlReferrer.Host.IndexOf("yourdomain.com") >= 0) { WriteImg(context,context.Request.FilePath); } else { WriteImg(context, "/image/error.jpg"); } } } private void WriteImg(HttpContext context,string path) { string FileName = context.Server.MapPath(path); context.Response.ContentType = "image/JPEG"; context.Response.WriteFile(FileName); } public bool IsReusable { get { return false; } } }
web.config:
<system.web> <httpHandlers> <add name="myJpgHandler" path="*.jpg" verb="*" type="MVC.JpgHandler" /> </httpHandlers> </system.web>
集成管道要配置在 system.webServer/handlers 節下