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 节下