Http请求刚刚到达服务器的时候
当服务器接收到一个 Http请求的时候,IIS (Internet Information Services,互联网信息服务)首先需要决定如何去处理这个请求。
什么是IIS?
IIS是一种Web(网页)服务组件,(其中包括Web服务器、FTP服务器、NNTP服务器和SMTP服务器,分别用于网页浏览、文件传输、新闻服务和邮件发送等方面,它使得在网络(包括互联网和局域网)上发布信息成了一件很容易的事。对于IIS来说,它依赖一个叫做 HTTP.SYS 的内置驱动程序来监听来自外部的 HTTP请求。IIS支持的东西也比较多,最常见的就是处理多媒体的功能(例如:为什么请求的图片会被显示在客户端?就是IIS进行处理的。这也是为什么要防图片盗链:若图片被盗链,在其它网站打开本站图片同样占一个iis,而你的IIS资源是有限的),当然如果你进行了设置,图片的处理也能交由应用程序进行处理。
在操作系统启动的时候,IIS首先在HTTP.SYS中注册自己的虚拟路径(实际上相当于告诉HTTP.SYS哪些URL是可以访问的,这也是为什么你访问不存在的文件会出现 404 错误),如果请求的是一个可访问的URL,HTTP.SYS会将这个请求交给 IIS 工作者进程。
注:一会要说的aspnet_isapi.exe,在IIS6.0中叫做 w3wp.exe,IIS5.0中叫做 aspnet_wp.exe。
那IIS是怎么识别这些Asp、Java、VBscript、Asp.Net等程序发布的页面,依据的是什么呢?
答:根据文件的后缀名。
服务器获取所请求的页面(也可以是多媒体、文件(也有后缀名)等)的后缀名以后,接下来会在服务器端寻找可以处理这类后缀名的应用程序。IIS不可能处理各种后缀名的应用程序,要处理这些,就要借助 ISAPI 应用程序(NOTE:Internet Server Application Programe Interface,互联网服务器应用程序接口)。ISAPI实际上只是一个接口,起到一个代理的作用,它的主要工作是映射所请求的页面(文件) 和与此后缀名相对应的实际的处理程序。
除了映射文件与其对应的处理程序以外,ISAPI 还需要做一些其他的工作:
- 从HTTP.SYS中获取当前的Httq请求信息,并且将这些信息保存到 HttpWorkerRequest 类中。
- 在相互隔离的应用程序域AppDomain中加载HttpRuntime。
- 调用 HttpRuntime的ProcessRequest方法。
让我们更进一步地看一下 ISAPI ,看看它到底是什么样子,请按下面的步骤进行:
- 打开IIS。
- 选择随意一个站点,鼠标右键,“属性”。
- 选择“主目录”选项卡。
- 选择“配置”。
你应该会看到如下的画面:
图1. 应用程序配置
很清楚地就可以看到,所有IIS所能处理,或者叫ISAPI 所提供代理服务的 文件类型 及其相对应的实际的后台处理程序都在这里清楚地列出来了。
我们找到 .aspx 的应用处理程序,然后点“编辑”,会出现下面的画面:
图2. 编辑.aspx文件的处理程序
可以看出,所有的.aspx文件实际上都是由 aspnet_isapi.dll 。这个程序来处理的,当IIS把对于.aspx页面的请求提通过ISAPI交给了aspnet_isapi.dll以后,接着就是只是等待结果,返回给用户(实际是IIS 接收返回的数据流,并重新返还给 HTTP.SYS,最后,HTTP.SYS 再将这些数据返回给客户端浏览器。)。现在我们应该知道:Asp.Net 只是服务器(IIS)的一个组成部分而已,它是一个 ISAPI扩展。
Ps:到这里我们就应该大致知道,一个请求到服务器后,这个请求是怎么样被处理的(至于怎么到的,那是网络传输中的事)。我们还要知道一个内容(可能有点多余),我们开发的网站是部署在服务端的,这个网页是输入网址就能直接看的,这些程序是经过发布的。这多少和你本地运行程序有点类似)。
应用程序生命周期前:编译
在第一次对应用程序发出请求时,ASP.NET 按特定顺序编译应用程序项。要编译的第一批项称为顶级项。在第一次请求之后,仅当依赖项更改时才会重新编译顶级项。下表描述编译 ASP.NET 顶级项的顺序。
项 | 说明 |
App_GlobalResources | 编译应用程序的全局资源并生成资源程序集。应用程序的 Bin 文件夹中的任何程序集都链接到资源程序集。 |
App_WebResources | 创建并编译 Web 服务的代理类型。所生成的 Web 引用程序集将链接到资源程序集(如存在)。 |
Web.config 文件中定义的配置文件属性 | 如果应用程序的 Web.config 文件中定义了配置文件属性,则生成一个包含配置文件对象的程序集。 |
App_Code | 生成源代码文件并创建一个或更多个程序集。所有代码程序集和配置文件程序集都链接到资源和 Web 引用程序集(如果有)。 |
Global.asax | 编译应用程序对象并将其链接到所有先前产生的程序集。 |
在编译应用程序的顶级项之后,ASP.NET 将根据需要编译文件夹、页和其他项。下表描述编译 ASP.NET 文件夹和项的顺序。
项 | 说明 |
App_LocalResources | 如果包含被请求项的文件夹包含 App_LocalResources 文件夹,则编译本地资源文件夹的内容并将其链接到全局资源程序集。 |
各个网页(.aspx 文件)、用户控件(.ascx 文件)、HTTP 处理程序(.ashx 文件)和 HTTP 模块(.asmx 文件) | 根据需要编译并链接到本地资源程序集和顶级程序集。 |
主题、主控页、其他源文件 | 在编译引用页时编译那些页所引用的各个主题、主控页和其他源代码文件的外观文件。 |
由于应用程序在第一次请求时进行编译,所以对应用程序的初始请求所花的时间会明显长于后续请求。可以预编译应用程序以减少第一次请求所需的时间。编译后的程序集缓存在服务器上并在后续请求时被重用,并且只要源代码未更改,就会在应用程序重新启动之间得到保留。
上面的是MSDN中的,咱说点别的:在编译时,.aspx其实是属于前台页面,当然也可以在前台写C#代码,这只和选的编程模式有关。执行的程序都是被编译了的,简单的说,就是页面是没法子运行的,只有编译后才能执行。.aspx编译后是前台继承与后台。后台的页码编译成程序集,而前台代码是根据的选择模式有关。网站将前台页面 直接以前台页面类的方式 编译到程序集里;网站应用程序,只能编译 后台页面类到程序集,前台页面必须在被访问的时候 才编译到程序集中。当然.aspx页面有三种控件,分别是Html标签、Html服务器控件、服务器控件三种,这些控件会被编译成不同的类,他们的简洁性是由低到复杂的(此处有空再整下三种控件的区别)。
应用程序生命周期概述
说这么多也没说到应用程序的生命生命周期。程序的生命周期也是有几种的,有应用程序生命周期、Asp.Net页生命周期、控件生命周期等。
参考MSDN地址:
http://msdn.microsoft.com/zh-cn/library/ms178473(v=vs.100).aspx 这是IIS5和IIS6的
http://msdn.microsoft.com/zh-cn/library/bb470252(v=vs.100).aspx 这是IIS7的
我们注意:
在 IIS 6.0 中,有两个请求处理管道。 一个管道用于本机代码 ISAPI 筛选器和扩展组件。 另一个管道用于托管代码应用程序组件,如 ASP.NET。
在 IIS 7.0 中,ASP.NET 运行时与 Web 服务器集成,这样就有了一个针对所有请求的统一的请求处理管道。
我的梳理是以IIS5和IIS6为基本,毕竟程序是向下兼容的。以下文字大部分是MSDN的东西,有些图是自己找的。
1.用户从Web服务器请求应用程序
ASP.NET 应用程序的生命周期以浏览器向 Web 服务器(对于 ASP.NET 应用程序,通常为 IIS)发送请求为起点。ASP.NET 是 Web 服务器下的 ISAPI 扩展。Web 服务器接收到请求时,会对所请求的文件的文件扩展名进行检查,确定应由哪个 ISAPI 扩展处理该请求,然后将该请求传递给合适的 ISAPI 扩展。ASP.NET 处理已映射到其上的文件扩展名,如 .aspx、.ascx、.ashx 和 .asmx。
如果文件扩展名尚未映射到 ASP.NET,则 ASP.NET 将不会接收该请求。例如,由于 .htm 文件通常没有映射到 ASP.NET,因此 ASP.NET 将不会对 .htm 文件请求执行身份验证或授权检查。因此,即使文件仅包含静态内容(包括Javascript文件、图片、CSS文件等等),如果希望 ASP.NET 检查身份验证,也应使用映射到 ASP.NET 的文件扩展名创建该文件。如果要创建服务于特定文件扩展名的自定义处理程序,必须在 IIS 中将该扩展名映射到 ASP.NET,还必须在应用程序的 Web.config 文件中注册该处理程序(常见的是IhttpModule进行扩展,可参考:http://msdn.microsoft.com/zh-cn/library/bb398986%28v=vs.100%29.aspx)。不建议这些交由Asp.Net处理,毕竟资源有限。
先看一张MSDN提供的用户请求.aspx、.ascx、.ashx 和 .asmx等文件的处理过程吧。
2.ASP.NET 接收对应用程序的第一个请求
当用户请求的文件的后缀名为:.aspx、.ascx、.ashx 和 .asmx时,IIS会把这个请求交由ISAPI ,ISAP找到aspnet_ISAPI,然后让aspnet_ISAPI进行处理。
当 ASP.NET接收到对应用程序中任何资源的第一个请求时,名为 ApplicationManager的类会创建一个应用程序域(AppDomanin)。应用程序域为全局变量提供应用程序隔离,并允许单独卸载每个应用程序。在应用程序域中,将为名为 HostingEnvironment 的类创建一个实例,该实例提供对有关应用程序的信息(如存储该应用程序的文件夹的名称)的访问。
看不懂没关系想来看下他们分别是干什么的:
ApplicationManager对象在 ASP.NET 应用程序的宿主环境中提供对象生存期管理。也就是为ASP.NET 宿主应用程序管理ASP.NET 应用程序域。该对象负责:
- 激活和初始化 ASP.NET 应用程序;
- 管理应用程序生存期和在应用程序中注册的对象的生存期;
- 公开宿主环境使用的对象以处理 ASP.NET 应用程序请求;
- 提供任意给定时刻运行于宿主进程中的应用程序的列表;
再看下应用程序域是干啥的:为全局变量提供应用程序隔离,并允许单独卸载每个应用程序。这时再看看上面的那张图(PS:想成此时只有一次请求),不难理解:要进行管理还得需要一个第三方来对宿主对象和ApplicationManager之间进行信息交流,且这个第三方还得在在应用程序域中,而这第三方就是HostingEnvironment。
HostingEnvironment的作用是什么呢?在托管应用程序的应用程序域内向托管应用程序提供应用程序管理功能和应用程序服务。备注:每个托管应用程序域都包含HostingEnvironment类的一个静态实例,该实例提供对应用程序管理功能和应用程序服务的访问。
这只是接到请求的第一步处理,也就是为后续处理提供必要的环境。不得不说,在IIS 6引入了Application Pool的概念,其实就是一个application的容器,但这个容器我们可以创建若干个。一个请求对应一个Application,而一个Application对应一个Worker Process:w3wp.exe,在Worker Process中才开始真正的处理,处理的过程就是在Application的管线中。对运行Asp.Net的进程来说,进程内的资源毕竟有限,用户第一次在应用程序中请求 ASP.NET 页或进程时,将创建HttpApplication的一个新实例,再次请求时为了尽可能提高性能,进程就要对多个请求进行重复使用这个HttpApplication实例。
只要知道HostingEnviroment即可,下面先简单说下大致的流程:在初始化的时候,加载ASP.NETISAPI,ASP.NETISAPI进而加载CLR。ASP.NET ISAPI会创建一个叫做aspnet_wp.exe的Worker Process(如果该进程不存在的话),在aspnet_wp.exe初始化的时候会加载CLR,从而为ASP.NET Application创建一个托管的运行环境, 在CLR初始化的使用会加载两个重要的dll:AppManagerAppDomainFactory和ISAPIRuntime。通过 AppManagerAppDomainFactory的Create方法为Application创建一个Application Domain;通过ISAPIRuntime的ProcessRequest处理Request,进而将流程拖入到ASP.NET Http Runtime Pipeline。
如下图:
图注:为了避免用户应用程序访问或者修改关键的操作系统数据,windows提供了两种处理器访问模式:用户模式(User Mode)和内核模式(Kernel Mode)。一般地,用户程序运行在User mode下,而操作系统代码运行在Kernel Mode下。Kernel Mode的代码允许访问所有系统内存和所有CPU指令。在User Mode下,http.sys接收到一个基于aspx的http request,然后它会根据IIS中的Metabase查看该基于该Request的Application属于哪个Application Pool,如果该Application Pool不存在,则创建之。否则直接将request发到对应Application Pool的Queue中。摘自:http://www.cnblogs.com/artech/archive/2007/09/09/887528.html
那允许单独卸载每个应用程序是怎么回事呢?
原因就是因为有隔离。Http请求被分放在相互隔离的应用程序域中,当 Web.config文件的内容发生改变 或者 .aspx文件发生变动的时候,能够卸载运行在同一个进程中的应用程序(卸载也是为了重新加载)。注:这也是为什么配置文件发生改变时,程序能立即更改,当然这个还和接口有关系
3.为每个请求创建 ASP.NET 核心对象
创建了应用程序域并对HostingEnvironment对象进行了实例化之后,ASP.NET 将创建并初始化核心对象,如HttpContext、HttpRequest和HttpResponse。
MSDN备注:HostingEnvironment构造函数初始化HostingEnvironment对象。HostingEnvironment构造函数仅由应用程序的ApplicationManager对象调用一次
- HttpContext类包含特定于当前应用程序请求的对象,如HttpRequest和HttpResponse对象。
- HttpRequest对象包含有关当前请求的信息,包括 Cookie 和浏览器信息。
- HttpResponse对象包含发送到客户端的响应,包括所有呈现的输出和 Cookie。
上面是MSDN的描述。下面是了解到初始化Asp.Net核心对象的过程,主要是通过反编译,我觉得合理。
IIS接到一个针对动态页面(.aspx)的请求,自己处理不了,就调用aspnet_isapi.dll这个服务器扩展程序,再有aspnet_isapi.dll将请求交给ASP.NET,也就是一个名为aspnet_wp.exe的工作进程。aspnet_wp.exe就调用FrameWork里的类 ------- ISAPIRuntime。
我们来看看ISAPIRuntime的类:public sealed class ISAPIRuntime : MarshalByRefObject,IISAPIRuntime,IRegisteredObject
分析:
a.继承MarshalByRefObject:从而以此获得跨应用程序访问对象的能力。
b.实现 IISAPIRuntime接口,来看下这个接口里一个最重要的方法:
1 2 | [ return : MarshalAs(UnmanagedType.I4)] int ProcessRequest([In] IntPtr ecb, [In, MarshalAs(UnmanagedType.I4)] int useProcessModel); |
注意看第一个参数:IntPtr ecb,是一个指针对象,其实就是保存的aspnet_isapi.dll的引用,为什么要位ProcessRequest方法传入这个指针?因为ASP.NET需要通过这个指针对应的aspnet_isapi.dll对象 从IIS处获得浏览器发来的请求报文,并且当ASP.NET最后处理完页面生成响应报文后,也要通过它将响应报文发回给IIS再有IIS发回到浏览器。
c.继承MarshalByRefObject的原因也就是为了能访问到托管代码外的这个aspnet_isapi.dll。
在ISAPIRuntime中调用了实现了接口IISAPIRuntime 的方法ProcessRequest。此方法中做了两重大的事情:
a.创建WorkerRequest对象:通过调用CreateWorkerRequest (IntPtr ecb, bool useOOP)方法来创建一个ISAPIWorkerRequest对象,此对象中包含了请求报文,并拥有和aspnet_isapi.dll通讯的能力。
b. 调用HttpRuntime.ProcessRequestNoDemand(HttpWorkerRequest wr)方法,然后在此方法里通过调用HttpRuntime实例方法ProcessRequestInternal(HttpWorkerRequest wr),开始处理请求。
HttpRuntime中调用的ProcessRequestInternal(HttpWorkerRequest wr)做了3个重要的事情:
a.生成上下文对象(HttpContext),为上下文对象context内部的两个重要的成员赋值:Reqeust和Response,前者负责向ASP.NET和程序员提供请求报文的数据,后者向ASP.NET和程序员提供储存要输出到浏览器的数据的地方。
(I).内部的HttpRequest对象:根据wr里封装的报文数据进一步封装出了HttpRequest对象。
(II).内部的HttpResponse对象:在内部初始化了一个HtmlWriter对象,用来保存服务端要输出给浏览器的页面代码。
(III).注意,如果在创建context时出现错误,比如浏览器发来的请求报文格式错误,那么就会直接在此时向浏览器输出400响应报文。
b.创建HttpApplication handler,此对象中执行请求的动态页面对象创建和执行的整个过程。
(I).通过HttpApplicationFactory创建——HttpApplicationFactory.GetApplicationInstance(context) ;(具体方法)。注意:此方法中为创建的HttpApplication对象传入了上下文对象context。
(II).HttpApplication具体执行过程。
c.当HttpApplication执行完后,调用FinishRequest方法,生成并输出响应报文到浏览器。
创建WorkerRequest对象,并HttpRuntime中调用的ProcessRequestInternal(HttpWorkerRequest wr)之后的大致流程:
MSDN对HttpRuntime的解释:为当前应用程序提供一组 ASP.NET 运行时服务。HttpRuntime对象在处理 HTTP 请求的ASP.NET 管线模型的开头使用。ProcessRequest 方法驱动所有后续的 ASP.NET Web 处理(注:ProcessRequest是HttpRuntime的方法,和IHttpHandler类似)。网页开发人员可以使用HttpRuntime类属性来查找关于当前应用程序域的信息,例如用于诊断目的。创建自定义进程管线或自定义宿主环境的开发人员应该在从 HttpWorkerRequest 或 SimpleWorkerRequest 类派生的类中调用 ProcessRequest 方法。
我们按编号再来看一下这幅图中的数据是如何流动的。具体的请看下文。
- HttpRuntime将请求转交给 HttpApplicationFactory。HttpApplicationFactory(HttpApplicantFactory工厂)创建HttpApplication(为什么要用工厂?请参考:http://www.cnblogs.com/whtydn/archive/2009/10/16/1584584.html)。
- 接下来请求通过一系列Module。
- 请求经过所有的Module之后,流转到Handler进一步处理。
- Handler处理完以后,请求再一次回到Module,此时Module可以做一些某个工作已经完成了之后的事情。
4.将HttpApplication对象分配给请求
初始化所有核心应用程序对象之后,将通过创建HttpApplication类的实例启动应用程序。如果应用程序具有 Global.asax 文件(ps:此时文件已经加载了,config中的配置可能也读取了),则 ASP.NET 会创建 Global.asax 类(从HttpApplication类派生)的一个实例,并使用该派生类表示应用程序。创建HttpApplication的实例时,将同时创建所有已配置的模块。例如,如果将应用程序这样配置,ASP.NET 就会创建一个SessionStateModule模块(ps:此处是创建了包含自定义和原有的Module系列,并没有执行。也就是说:遍历就是遍历过滤器,为HttpApplicant里的事件注册用户方法)。创建了所有已配置的模块之后,将调用HttpApplication类的Init方法(ps:Init方法并不是开启管道的方法,Msdn:在添加所有事件处理程序模块之后执行自定义初始化代码。)。
下面的关系图说明了这种关系:
那SessionStateModule是什么呢?怎么样配置自定义的IhttpModule呢?
ASP.NET 应用程序生命周期可通过IHttpModule类进行扩展。ASP.NET 包含若干实现IHttpModule的类,如SessionStateModule类。您还可以自行创建实现IHttpModule的类。这时也就知道SessionStateModule是实现IHttpModule接口的类而已。(关于IHttpModule只是点到而已,具体请参考:http://www.cnblogs.com/JimmyZhang/archive/2007/11/25/971878.html)
关于Module请参考:http://www.cnblogs.com/anlen/articles/3613186.html
MSDN:http://msdn.microsoft.com/zh-cn/library/system.web.ihttpmodule(v=vs.100).aspx
关于Handler请参考:http://www.cnblogs.com/anlen/articles/3613204.html
MSDN:http://msdn.microsoft.com/zh-cn/library/system.web.ihttphandler(v=vs.110).aspx
通过HttpApplicationFactory创建了一个HttpApplicant对象,负责处理整个请求,是通过创建HttpApplication类的实例启动应用程序。
5.由HttpApplication管线处理请求
在处理该请求时将由HttpApplication类执行以下事件。希望扩展HttpApplication类的开发人员需要注意这些事件。
1.对请求进行验证,将检查浏览器发送的信息,并确定其是否包含潜在恶意标记。有关更多信息,请参见ValidateRequest和脚本侵入概述。
2.如果已在 Web.config 文件的UrlMappingsSection节中配置了任何 URL,则执行 URL 映射。
3.引发BeginRequest事件。
在 ASP.NET 响应请求时作为 HTTP 执行管线链中的第一个事件发生。BeginRequest事件发出信号表示创建任何给定的新请求。此事件始终被引发,并且始终是请求处理期间发生的第一个事件。
4.引发AuthenticateRequest事件。
当安全模块已建立用户标识时发生。AuthenticateRequest事件发出信号表示配置的身份验证机制已对当前请求进行了身份验证。订阅AuthenticateRequest事件可确保在处理附加模块或事件处理程序之前对请求进行身份验证。
5.引发PostAuthenticateRequest事件。
当安全模块已建立用户标识时发生。PostAuthenticateRequest事件在AuthenticateRequest事件发生之后引发。预订PostAuthenticateRequest事件的功能可以访问由PostAuthenticateRequest处理的任何数据。
6.引发AuthorizeRequest事件。
当安全模块已验证用户授权时发生。AuthorizeRequest事件发出信号表示 ASP.NET 已对当前请求进行了授权。订阅AuthorizeRequest事件可确保在处理附加的模块或事件处理程序之前对请求进行身份验证和授权。
7.引发PostAuthorizeRequest事件。
在当前请求的用户已获授权时发生。PostAuthorizeRequest事件发出信号表示 ASP.NET 已对当前请求进行了授权。订阅PostAuthorizeRequest事件可确保在处理附加的模块或处理程序之前对请求进行身份验证和授权。
8.引发ResolveRequestCache事件。
在 ASP.NET 完成授权事件以使缓存模块从缓存中为请求提供服务后发生,从而绕过事件处理程序(例如某个页或 XML Web services)的执行。
9.引发PostResolveRequestCache事件。
在 ASP.NET 跳过当前事件处理程序的执行并允许缓存模块满足来自缓存的请求时发生。
10.根据所请求资源的文件扩展名(在应用程序的配置文件中映射),选择实现IHttpHandler的类,对请求进行处理。如果该请求针对从Page类派生的对象(页),并且需要对该页进行编译,则 ASP.NET 会在创建该页的实例之前对其进行编译。
11.引发PostMapRequestHandler事件。
在 ASP.NET 已将当前请求映射到相应的事件处理程序时发生。
12.引发AcquireRequestState事件。
当 ASP.NET 获取与当前请求关联的当前状态(如会话状态)时发生。AcquireRequestState事件在创建了事件处理程序之后引发。
13.引发PostAcquireRequestState事件。
在已获得与当前请求关联的请求状态(例如会话状态)时发生。PostAcquireRequestState事件在AcquireRequestState事件发生之后引发。预订AcquireRequestState事件的功能可以访问由PostAcquireRequestState处理的任何数据。
14.引发PreRequestHandlerExecute事件。
恰好在 ASP.NET 开始执行事件处理程序(例如,某页或某个 XML Web services)前发生。
15.为该请求调用合适的IHttpHandler类的ProcessRequest方法(或异步版IHttpAsyncHandler.BeginProcessRequest)。例如,如果该请求针对某页,则当前的页实例将处理该请求。
16.引发PostRequestHandlerExecute事件。
在 ASP.NET 已将当前请求映射到相应的事件处理程序时发生。
17.引发ReleaseRequestState事件。
在 ASP.NET 已将当前请求映射到相应的事件处理程序时发生。
18.引发PostReleaseRequestState事件。
在 ASP.NET 已完成所有请求事件处理程序的执行并且请求状态数据已存储时发生。
19.如果定义了Filter属性,则执行响应筛选。
20.引发UpdateRequestCache事件。
当 ASP.NET 执行完事件处理程序以使缓存模块存储将用于从缓存为后续请求提供服务的响应时发生。
21.引发PostUpdateRequestCache事件。
在 ASP.NET 完成缓存模块的更新并存储了用于从缓存中为后续请求提供服务的响应后,发生此事件。
22.引发EndRequest事件。
在 ASP.NET 响应请求时作为 HTTP 执行管线链中的最后一个事件发生。
23.引发PreSendRequestHeaders事件。
恰好在 ASP.NET 向客户端发送 HTTP 标头之前发生。
24.引发PreSendRequestContent事件。
恰好在 ASP.NET 向客户端发送内容之前发生。PreSendRequestContent事件可能发生多次。
IIS6中共有24个事件,其中有19个事件是我们可以进行控制的。因为这些都是事件,所以可以往里面添加多个自定义方法。通常说的事件是从BeginRequest开始的。先通过一张图来了解下这些事件是在什么地方执行的。
M:客户端请求处理开始,前六个事件是在HttpModule中处理的,这六个事件是我们可以调用的,分别是:BeginRequest、AuthenticateRequest、AuthorizeRequest、ResolveRequestCache、AcquireRequestState和PreRequestHandlerExecute。(ps:HttpModule不只是一个,这六个可在Application中控制)。
H:执行Handler中的PracessRequest()。(ps:Handler也不止一个)。
P:一旦HttpHandler逻辑执行,ASP.NET的page对象被创建(ps:Page继承的接口有IHttpHandle)。ASP.NET page对象被创建,许多事件被触发,你可以在这些页面事件中写我们自定义的逻辑。有6个重要事件给我们提供占位,在ASP.NET页中写逻辑:Init、Load、Validate、Event、Render、Unload。你可以记住单词SILVER来记这些事件,S-Start(没有任何意义,仅仅是为了形成一个单词),I(Init)、L(Load)、V(Validate)、E(Event)、R(Render)。其实还是HttpHandler中的事件。
M:一旦页面对象执行了且从内存中卸载,HttpModule提供发送页面执行事件,它们可用于注入自定义post-处理逻辑。有4个重要的post-处理事件,PostRequestHandlerExecute、PostRequestState、UpdateRequestCache、EndRequest。
添加方法如下:
应用程序的生命周期中的添加方法:通过使用命名约定Application_event(如Application_BeginRequest),ASP.NET 可在 Global.asax 文件中将应用程序事件自动绑定到处理程序。(为什么可以在Global.asax中添加方法?因为Global.asax与HttpApplication是继承关系。)。此处能添加的事件有19个,如下图所示:
IHttpModule中方法的调用:可以自定义post-处理逻辑。果向应用程序添加模块,模块本身会引发事件。通过使用modulename_eventname约定,应用程序可以在 Global.asax 文件中预订这些事件。例如,若要处理FormsAuthenticationModule对象引发的Authenticate事件,可以创建一个名为FormsAuthentication_Authenticate的处理程序。默认情况下,ASP.NET 中会启用SessionStateModule类。所有会话事件自动命名为Session_event,如Session_Start。每次创建新会话时都会引发Start事件。
还有特殊如:Application_Start、Application_event、Init、Dispose、Application_End等。
页还支持自动事件连接,即,ASP.NET 将查找具有特定名称的方法,并在引发了特定事件时自动运行这些方法。如果@Page指令的AutoEventWireup特性设置为true,页事件将自动绑定至使用命名约定Page_事件(如Page_Load和Page_Init)的方法。可参考。还有:当您创建从Page类继承的类时,除了可以处理由页引发的事件以外,还可以重写页的基类中的方法。
下面再整体看下流程图,具体都做了那些事情:声明:这两幅图不是本人所画,是由邹老师所画。
注意:每一次请求都执行这个过程(前提是没有缓存),HttpContext保持单个用户、单个请求的数据,并且数据只在该请求期间保存。被提供用于保持需要在不同的HttpModules和HttpHandlers之间传递的值。也可以用于保持某个完整请求的相应信息。 在请求管道中,第八个事件PostMapRequestHandler中创建页面对象,并转换成HttpHandler接口;第九个事件加载Session事件,先判断当前页面是否实现了IRequiresSessionState接口,如果实现了,则从浏览器发来的请求报文中获得Cookie里的SessionID,然后到服务器Session池中获得对应的Session对象,并将其引用赋值给页面对象的Session属性(Page.HttpContext.HttpSession)。接下来在第十一个和第十二个事件调用页面对象的ProcessPequest(HttpContext context),页面也是继承IHttpHandler的对象。进入是一个和第二个事件之后就是页面的生命周期了,具体请看下回分解。
如果我们将注意力只集中在请求、HttpHandler和HttpModule上,不去考虑HttpContext和HttpApplication,可以将请求简化成下面这样:
参考:http://www.cnblogs.com/skynet/archive/2010/04/29/1724020.html介绍Module中的事件。
http://www.cnblogs.com/artech/archive/2007/09/09/887528.html IIS 解释
http://www.cnblogs.com/artech/archive/2009/06/20/1507165.html管道事件
Application Restarts(应用程序重新启动的次数)
修改 Web 应用程序的源代码将导致 ASP.NET 把源文件重新编译为程序集。当修改应用程序中的顶级项时,应用程序中引用顶级程序集的其他所有程序集也会被重新编译。
此外,修改、添加或删除应用程序的已知文件夹中的某些类型的文件将导致应用程序重新启动。下列操作将导致应用程序重新启动:
-
添加、修改或删除应用程序的 Bin 文件夹中的程序集。
-
添加、修改或删除 App_GlobalResources 或 App_LocalResources 文件夹中的本地化资源。
-
添加、修改或删除应用程序的 Global.asax 文件。
-
添加、修改或删除 App_Code 目录中的源代码文件。
-
添加、修改或删除配置文件配置。
-
添加、修改或删除 App_WebReferences 目录中的 Web 服务引用。
-
添加、修改或删除应用程序的 Web.config 文件。
当应用程序需要重新启动时,ASP.NET 将在重新启动应用程序域和加载新的程序集之前,从现有应用程序域和旧的程序集中为所有挂起的请求提供服务。
页生命周期、控件生命周期,请看下回分解。
图解请看:
请参考:http://www.cnblogs.com/zhaoyang/archive/2011/11/16/2251200.html 图解生命周期