Asp.Net底层解析(四)——应用程序生命周期与HttpModule

本文详细解析了ASP.NET应用程序从客户端请求到服务器响应的整个生命周期,涵盖了从HTTP请求的接收、处理到最终响应的全过程。重点介绍了应用程序生命周期中的关键对象和事件,包括HttpApplication、HttpModule等,并提供了示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    前言:一般ASP.NET开发者对页面生命周期(PageLife Cycle)是比较熟悉的,在开发ASP.NET应用程序中经常需要从页面周期的角度去思考问题。实际上在页面生命周期的背后,还存在着一个不太为人所熟知的更广义的周期——应用程序生命周期(Application Life Cycle)。本篇文章将详细对其进行说明(IIS6.0)。

    一,应用程序生命周期综述

    所谓应用程序生命周期指的是:从客户端向Web服务器发出资源请求开始,到Web服务器反馈结果返回给客户端的整个在服务器端执行的过程。

    ASP.NET是Web 服务器下的 ISAPI 扩展,Web 服务器接收到请求时,会对所请求的文件的扩展名进行检查,确定应由哪个 ISAPI 扩展处理该请求,然后将该请求传递给合适的 ISAPI 扩展。ASP.NET处理已映射到其上的文件扩展名,如.aspx、.ascx、.ashx和.asmx。因此,aspx页面的请求是首先提交给IIS,然后由IIS通过文件扩展名查找ISAPI最终交给ASP.NET(aspnet_isapi.dll进程)进行处理,再经过一系列规范化的步骤最终生成对应的html编码返回给浏览器。综上可知,ASP.NET的页面生命周期是应用程序生命周期的一部分,而且仅仅当请求文件为aspx页面时才能触发该页面的生命周期。

    下面分阶段描述ASP.NET 应用程序生命周期(这里假设请求资源文件类型为aspx页面,下面的内容大部分是从bitfan前辈的博客文章和MSDN中复制而来):

    1,  用户请求aspx页面:
        A, 当此请求到达Web服务器时,由HTTP.SYS(Windows进行http协议信息通讯的核心组件)负责接收,根据ISAPI扩展设置,将其传递给此ASP.NET应用程序所对应的应用程序池,在此应用程序池中运行的工作者进程负责处理请求。
       B, 工作者进程接收到这个请求之后,装载专用于处理ASP.NET页面的一个ISAPI扩展“aspnet_isapi.dll”,并将HTTP请求传给它。
       C, 工作者进程加载完aspnet_isapi.dll后,由aspnet_isapi.dll负责加载ASP.NET应用程序的运行环境――CLR
       D, 工作者进程工作于非托管环境(指Windows操作系统本身)之中,而.NET中的对象则工作于托管环境(指CLR)之中,aspnet_isapi.dll起到了一个沟通两者的桥梁作用,将收到的HTTP请求(由非托管环境传来)转发给相应.NET对象(处于托管环境中)处理。
   2,  ASP.NET 应用程序(通常意义上的网站)接收第一个请求:
       A, 说明:同一个ASP.NET应用程序只在第一次请求时才会执行该阶段。
       B, 加载CLR之后,由System.Web.Hosting.ApplicationManager类负责创建一个应用程序域。
       C, 每个ASP.NET应用程序都运行于自己的应用程序域中,且对应着一个ApplicationManager类的实例对象,该对象负责对该应用程序进行管理,包括:激活和初始化 ASP.NET 应用程序、管理应用程序生存期和在应用程序中注册的对象的生存期、公开宿主环境使用的对象以处理 ASP.NET 应用程序请求、提供任意给定时刻运行于宿主进程中的应用程序的列表。
       D, 接着在应用程序域中创建一个System.Web.Hosting.HostingEnvironment实例对象,该对象提供对有关应用程序的信息(如存储该应用程序在服务器中的目录等等信息)的访问。
       E,关系图表示为:

   3,  为每个请求创建 ASP.NET 核心对象:
        A, 当应用程序域创建完成之后,一个ISAPIRuntime对象被创建,并自动调用它的ProcessRequest()方法。在此方法中,ISAPIRuntime对象根据传入的HTTP请求创建一个HttpWorkerRequest对象,此对象以面向对象的方式包装了HTTP请求的各种信息(这就是说,原始的HTTP请求信息被封装为HttpWorkerRequest对象)
       B, 然后,调用ISAPIRuntime对象的StartProcessing()方法启动整个HTTP请求处理过程(此即“HTTP管线:HTTP Pipeline”),在这个处理过程的开端,一个HttpRuntime类型的对象被创建,前面创建好的HttpWorkerRequest对象作为方法参数被传送给此HttpRuntime对象的ProcessRequest()方法。
       C, HttpRuntime类的ProcessRequest()方法根据HttpWorkerRequest对象中所提供的HTTP请求信息,创建了一个HttpContext对象。HttpContext 类包含特定于当前应用程序请求的对象,如HttpRequest 和 HttpResponse 对象。
       D, HttpRequest 对象包含有关当前请求的信息,包括 Cookie 和浏览器信息。HttpResponse 对象包含发送到客户端的响应,包括所有呈现的输出和 Cookie。
       E,  在整个HTTP处理过程中,HttpContext对象都是可以访问的,也就是说这个阶段之后的应用程序生命周期内所有使用的HttpContext实际上都是这个HttpContext对象的引用,如aspx页面后台常用的Context属性。
   4,  分配一个HttpApplication 对象用于处理请求:
       A, HttpRuntime 类的 ProcessRequest() 方法除了创建 HttpContext 对象之外,还完成了另一个很重要的工作——向 HttpApplicationFactory 类的一个实例申请分配一个 HttpApplication 对象用于管理整个 HTTP 请求处理管线中的各种事件。
       B, HttpApplicationFactory对象负责管理一个HttpApplication对象池,当有HTTP请求到来时,如果池中还有可用的 HttpApplication对象,就直接分配此对象用于处理HTTP请求,否则,创建一个新的HttpApplication对象。
       C, 如果ASP.NET应用程序无 Global.asax 文件,使用原始的System.Web.HttpApplication类,如果ASP.NET应用程序中有使用在Global.asax中定义的继承于System.Web.HttpApplication的类,作为创建HttpApplication对象的类。
    5,  HttpApplication对象启动HTTP管线:
       A, HttpApplication对象负责装配出整个“HTTP请求处理管线(HTTP Pipeline)”,可以将HttpContext看成是订单(HttpRequest)与产品(HttpResponse)的打包,而将HTTP请求处理管线看做是产品生成流程。在这个流程中,各个模块将根据订单(HttpRequest)提供的信息,将特定的内容添加到产品(HttpResponse)中。
       B, HttpContext对象经过“生产流水线”的不同部分时,HttpApplication对象会先后激发出一连串的事件。一些特定的组件——HTTP模块(HTTP Module)可以响应这些事件,在此事件响应代码中可以对HttpContext对象进行“加工和处理”。从这个意义上说,HTTP模块可以看成是“生产流水线”中的工人。
       C, HTTP模块对象是在HttpApplication对象的InitModules()方法中被创建的,一般在HTTP模块对象的Init方法内对HttpApplication对象所激发的特定事件进行绑定,在这些事件中,各个HTTP模块可以对HttpContext对象进行操作,主要是对HttpResponse进行添加内容,也能够直接将本次请求终止(这里需要注意的是,这里的终止并不会完全将HttpResponse返回空,而是跳过HttpApplication中的一些处理步骤,直接引发EndRequest事件)。
       D, ASP.NET提供了一些预定义的HTTP模块响应特定的事件,开发者也可以编写自己的HTTP模块并将其插入到“HTTP请求处理管线”中(可通过Web.Config配置)。
       E,  在HttpApplication对象触发PreRequestHandlerExecute事件之后,会根据请求调用合适的 IHttpHandler(或由IHttpHandlerFactory确定目标IHttpHandler)类的 ProcessRequest 方法,处理过程临时交给已配置的HttpHandler。这里只考虑aspx页面请求的话,那么本阶段将会调用aspx页面对应后台的继承于Page的类的ProcessRequest方法,从而触发页面生命周期开始执行。调用执行完成目标页面的生命周期之后,得到页面周期内呈现(Render)的html编码将加入到HttpContext对象的HttpResponse中。
       F,  调用请求页面的ProcessRequest 方法之后将会触发HttpApplication的PostRequestHandlerExecute事件,处理过程又交还给HttpApplication,直到触发PreSendRequestHenaders及PreSendRequestConternt。这时HttpApplication的任务算是完成了,HttpContext内的HttpResponse产品就是最终版了。

       G, HttpContext对象带着最后的处理结果来到了“HTTP请求处理管线”的未端,其信息被取出来,再次以aspnet_isapi.dll为桥梁传送给工作者进程。工作者进程再将HTTP请求的处理结果转给HTTP.SYS,由它负责将结果返回给浏览器。

    下面用流程图来直观地展示应用程序周期模型:


    以生产个性化面包对上述过程打个简单的比喻(仅参考,部分细节可能不符):

    1,  IIS就是整个面包店,所有参与的成员都是在IIS的环境下进行的。
    2,  HTTP.SYS是前台收费员,负责收集客户对产品的需求信息,包括请求资源的类型、地址等。
    3,  ISAPI负责联系各个面包房,有些负责生产ASP.NET面包,有些负责htm、shtml面包,还有其他生成各种类型面包的面包房。
    4,  工作者进程是大堂小二,负责前台收费员与面包房内沟通,还负责将面包房生成出来的面包交给前台收费员。
    5,  ASP.NET 应用程序是其中一种面包房,它可以生产aspx、asmx等类型的面包,由它根据前台收费员(HTTP.SYS)接到的订单进行生产。ASP.NET 面包房可以有多个,它们之间是独立运营的,如果其中某个ASP.NET面包房从来没有过客户订单,就没有配备相应的面点师傅了;只有当第一个订单来了,面包房才开始招师傅来干活了。
    6,  ApplicationManager对象是ASP.NET面包房的总负责人,他有对这个面包房的总控制权,包括什么时候开工,什么时候干脆关闭得了。
    7,  由ApplicationManager创建的应用程序域就是面包房内部的工作场所,所有的人员及设置都在里面。
    8,  HostingEnvironment对象是对这个面包房的说明书,各个设备在哪,怎么用等等信息,当然包括卫生合格证明啦,要不然就是违法生产了。
    9,  ISAPIRuntime对象及HttpRuntime对象就看做面包房负责人的命令吧,ISAPIRuntime是初步动员令,而HttpRuntime更是正式命令。
    10, HttpWorkerRequest对象是负责人在发布ISAPIRuntime命令的同时根据前台收费员提供的订单信息而组织的使面包房内部更容易理解的简要需求说明书。
    11, HttpContext对象则是负责人在发布HttpRuntime正式命令时,觉得HttpWorkerRequest还不太规范,因此将其修改为HttpContext对象。HttpContext是打包好的,里面有根据HttpWorkerRequest修改后的更详细的面包需求说明书HttpRequest,还有一个包装盒HttpResponse,里面是“基本”是空的,要求在剩下的步骤里将这个包装盒按照说明书的要求制作出准确的面包。
    12, HttpApplicationFactory是所有面包房的人力资源经理,由他分配由哪个师傅来接手面包房负责人的HttpContext打包。如果店里有手艺的师傅不够了,就再招一个;如果有师傅有闲余时间了,那得了,您上吧。
    13, HttpApplication对象就是人力资源经理分配的师傅了,这里称为“主师傅”,他相当于一个小工头。拿到HttpContext打包之后就开始在面包房了吼嗓子了:“我开始工作了!有谁要在HttpResponse中添加内容的赶紧了,过了就不候着了啊!”、“我要加载初始化Session了,之后Session就可以随便用了!”、“雏形师傅,该你大显身手了!”等等。主师傅的每次吼嗓子都代表着HttpApplication的每个事件,所有主师傅是最累最辛苦的角色了,因为他要吼二十几个嗓子,而且嗓门要大,好让HTTP模块对象们都能听见。
    14, HTTP模块对象的是装点师,装点师有多个,各个师傅的作用都不同;每个装点师在HttpApplication主师傅每次吼嗓子的时候都可以往HttpResponse中添加内容,撒点糖、加点装饰品什么的。装点师有一个很大的权利,他可以要求主师傅直接跳过一些步骤,直接吼“我的任务马上就要结束了!”。这里要注意的是,每次主师傅吼嗓子,装点师往HttpResponse加东西的顺序是固定的,谁来店里来早(就是在Web.config中的配置顺序),谁是前辈,谁就先操作;这一嗓子吼完之后,所有想要往HttpResponse加东西的的装点师都操作过之后,主师傅才会吼下一嗓子;各个装点师可以在主师傅吼完一嗓子之后不理他,等到他吼另一个嗓子的时候再行动,也就是说装点师傅的行动完全是自主的。
    15, IHttpHandler对象是特殊的一类师傅,是专业人才,这里称为雏形师傅(而IHttpHandlerFactory则是这些专业人才的介绍人什么的)。每个IHttpHandler对象只负责一种面包,他会按照自己的理解将这个面包做出来,然后放在主师傅的HttpResponse中。
    16, 这里有个流程需要特别注意,在吼完“雏形师傅,该你大显身手了!”之后,主师傅会将HttpContext打包临时交给IHttpHandler对象,等IHttpHandler对象的工作完成之后再交还给主师傅,之后主师傅会吼“雏形师傅的工作完成了!”。
后续流程:等到主师傅吼完“面包已经完成了,我要把面包放到面包房的产品架上了!”,这时主师傅的工作已经结束了,这时大堂小二会到面包房的产品架去面包交给前台收费员,前台拿到面包后交个客户。这样,一次面包的定制过程就完成了。
    17,  后续流程:等到主师傅吼完“面包已经完成了,我要把面包放到面包房的产品架上了!”,这时主师傅的工作已经结束了,这时大堂小二会到面包房的产品架去面包交给前台收费员,前台拿到面包后交个客户。这样,一次面包的定制过程就完成了。

    下面的表格列举了HttpApplication在启动HTTP管线阶段依次触发的事件

BeginRequest

在 ASP.NET 响应请求时作为 HTTP 执行管线链中的第一个事件发生

AuthenticateRequest

当安全模块已建立用户标识时发生。

PostAuthenticateRequest

当安全模块已建立用户标识时发生。

AuthorizeRequest

当安全模块已验证用户授权时发生。

PostAuthorizeRequest

在当前请求的用户已获授权时发生。

ResolveRequestCache

在 ASP.NET 完成授权事件以使缓存模块从缓存中为请求提供服务后发生,从而绕过事件处理程序(例如某个页或 XML Web services)的执行。

PostResolveRequestCache

在 ASP.NET 跳过当前事件处理程序的执行并允许缓存模块满足来自缓存的请求时发生。

PostMapRequestHandler

在 ASP.NET 已将当前请求映射到相应的事件处理程序时发生。

AcquireRequestState

当 ASP.NET 获取与当前请求关联的当前状态(如会话状态)时发生。

PostAcquireRequestState

在已获得与当前请求关联的请求状态(例如会话状态)时发生。

PreRequestHandlerExecute

恰好在 ASP.NET 开始执行事件处理程序(例如,某页或某个 XML Web services)前发生。

PostRequestHandlerExecute

在 ASP.NET 事件处理程序(例如,某页或某个 XML Web service)执行完毕时发生。

ReleaseRequestState

在 ASP.NET 执行完所有请求事件处理程序后发生。 该事件将使状态模块保存当前状态数据。

PostReleaseRequestState

在 ASP.NET 已完成所有请求事件处理程序的执行并且请求状态数据已存储时发生。

UpdateRequestCache

当 ASP.NET 执行完事件处理程序以使缓存模块存储将用于从缓存为后续请求提供服务的响应时发生。

PostUpdateRequestCache

在 ASP.NET 完成缓存模块的更新并存储了用于从缓存中为后续请求提供服务的响应后,发生此事件。

EndRequest

在 ASP.NET 响应请求时作为 HTTP 执行管线链中的最后一个事件发生。

PreSendRequestHeaders

恰好在 ASP.NET 向客户端发送 HTTP 标头之前发生。

PreSendRequestContent

恰好在 ASP.NET 向客户端发送内容之前发生。

二,HttpModule编程

HTTP模块(HttpModule)在应用程序生命周期中的作用在于HttpApplication对象启动HTTP管线阶段,这时所有的HttpModule都可以进行以下操作:

1,  在HttpApplication的各个事件中往HttpResponse添加内容。
2,  当HttpApplication执行出现异常时,执行异常处理机制。
3,  直接终止本次请求,这里需要注意的是,这里的终止并不会完全将HttpResponse返回空,而是跳过HttpApplication中的一些处理步骤,直接引发EndRequest事件,实际上是终结本次HTTP管线。

    利用操作1,我们可以很自由地“影响”最终生成的请求结果,包括实现很猥琐的功能,比如在页面正文之前或之后添加小广告等等。操作2功能很明确,当出现异常时,可以收集异常信息记录错误日志等等。操作3也很好理解,比如今天某个页面我不想对外公开了,直接添加一个HttpModule,如果有访问该页面请求,则直接终止掉就行了;明天又要恢复公开了,那好,把之前那个HttpModule配置给删除就行了。

    下面给出一下实现操作1的简单示例,新建一个Web应用程序,添加一个类库,命名为HttpModules,并且为Web应用程序添加对该类库的引用,解决方案截图如下:


    HttpModules类库添加对System.Web程序集的引用,该类库用于编写各个测试用的HttpModule。这里先编写FullEvents.cs,用于测试Application的所有事件,该HttpModule会在Application的每个事件中都往HttpResponse添加文本(每次添加的文本都会记录本次Application的事件),FullEvents.cs代码如下:

using System;
using System.Web;
namespace HttpModules
{
    public class FullEvents : IHttpModule
    {
        public void Dispose()
        { }  //这里没有什么要释放的资源,所以这里为空
        public void Init(HttpApplication application)
        {
            application.BeginRequest += (sender, e) => { application.Context.Response.Write("Application_BeginRequest<br/>"); };
            application.AuthenticateRequest += (sender, e) => { application.Context.Response.Write("Application_AuthenticateRequest<br/>"); };
            application.PostAuthenticateRequest += (sender, e) => { application.Context.Response.Write("Application_PostAuthenticateRequest<br/>"); };
            application.AuthorizeRequest += (sender, e) => { application.Context.Response.Write("Application_AuthorizeRequest<br/>"); };
            application.PostAuthorizeRequest += (sender, e) => { application.Context.Response.Write("Application_PostAuthorizeRequest<br/>"); };
            application.ResolveRequestCache += (sender, e) => { application.Context.Response.Write("Application_ResolveRequestCache<br/>"); };
            application.PostResolveRequestCache += (sender, e) => { application.Context.Response.Write("Application_PostResolveRequestCache<br/>"); };
            application.PostMapRequestHandler += (sender, e) => { application.Context.Response.Write("Application_PostMapRequestHandler<br/>"); };
            application.AcquireRequestState += (sender, e) => { application.Context.Response.Write("Application_AcquireRequestState<br/>"); };
            application.PostAcquireRequestState += (sender, e) => { application.Context.Response.Write("Application_PostAcquireRequestState<br/>"); };
            application.PreRequestHandlerExecute += (sender, e) => { application.Context.Response.Write("Application_PreRequestHandlerExecute<br/>"); };
            //这里正在执行页面生命周期
            application.PostRequestHandlerExecute += (sender, e) => { application.Context.Response.Write("Application_PostRequestHandlerExecute<br/>"); };
            application.ReleaseRequestState += (sender, e) => { application.Context.Response.Write("Application_ReleaseRequestState<br/>"); };
            application.PostReleaseRequestState += (sender, e) => { application.Context.Response.Write("Application_PostReleaseRequestState<br/>"); };
            application.UpdateRequestCache += (sender, e) => { application.Context.Response.Write("Application_UpdateRequestCache<br/>"); };
            application.PostUpdateRequestCache += (sender, e) => { application.Context.Response.Write("Application_PostUpdateRequestCache <br/>"); };
            application.EndRequest += (sender, e) => { application.Context.Response.Write("Application_EndRequest<br/>"); };
            application.PreSendRequestHeaders += (sender, e) => { application.Context.Response.Write("Application_PreSendRequestHeaders<br/>"); };
            application.PreSendRequestContent += (sender, e) => { application.Context.Response.Write("Application_PreSendRequestContent<br/>"); };
        }
    }
}

    要想在Web应用程序中使用该HttpModule就必须先对其进行注册,可以在Web.config中注册,代码如下:

<system.web>
    <compilation debug="true" targetFramework="4.0" />
  <httpModules>
    <!--只有测试完整Application事件时才使用下面的配置,否则请注释下面的配置-->
    <add name="FullEvents" type="HttpModules.FullEvents,HttpModules"/>
  </httpModules>
</system.web>

    使用页面AspxForFullEvents.aspx用于本次测试,该页面的前台代码为(仅含Form内标签):

<form id="form1" runat="server">
这是页面前台文本<br/><asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
</form>

    后台代码如下:

protected void Page_Load(object sender, EventArgs e)
{
    Label1.Text = "这是后台服务器控件生成文本";
}
    运行该页面,得到结果如下:

   

    下面给出一个实现操作2的简单示例,沿用测试1的解决方案,在类库HttpModules中添加文件ErrorHandler.cs用于在HttpModule中进行错误处理,ErrorHandler.cs代码如下:

using System;
using System.Web;
namespace HttpModules
{
    public class ErrorHandler : IHttpModule
    {
        public void Dispose()
        { }  //这里没有什么要释放的资源,所以这里为空
        public void Init(HttpApplication application)
        {
            application.Error += new EventHandler(application_Error);
            application.BeginRequest += new EventHandler(application_BeginRequest);
        }
        private void application_BeginRequest(object sender, EventArgs e)
        {
            throw new Exception("这里故意抛出一个异常,用于测试!"); //Application在运行时可能会出现异常,但是这是小概率事件,所以这里主动抛出异常以便进行测试
        }
        private void application_Error(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            //这里可以设置断点进行监视
            Exception LastError = application.Context.Server.GetLastError();    //这里获取异常信息
            //这里利用LastError,添加任意代码进行错误处理,一般操作是记录错误日志,以便后续查错修正
        }
    }
}

    在Web应用程序中添加AspxForErrorHandler.aspx空页面,且将Web.config中的配置内容修改为:

<httpModules>
  <!--只有测试完整Application事件时才使用下面的配置,否则请注释下面的配置-->
  <!--<add name="FullEvents" type="HttpModules.FullEvents,HttpModules"/>-->
  <!--只有测试错误处理时才使用下面的配置,否则请注释下面的配置-->
  <add name="ErrorHandler" type="HttpModules.ErrorHandler,HttpModules"/>
</httpModules>

    运行AspxForErrorHandler.aspx页面(实际上运行AspxForFullEvents.aspx也是一样的,因为HttpModule是针对每次请求都执行一遍的,不管这次请求的具体页面是什么),因为在Application的BeginRequest中主动抛出一个异常,所以会触发Application的Error事件,执行application_Error方法;后通过ExceptionLastError = application.Context.Server.GetLastError()可获取出错异常的Exception的实例,接下来就可以利用这个LastError进行错误日志记录了,当然你也可以做其他任何你需要的操作。


    下面给出一个实现操作3的简单示例,同样沿用测试1的解决方案,在类库HttpModules中添加文件ErrorHandler.cs用于在HttpModule中进行错误处理,ErrorHandler.cs代码如下:

using System;
using System.Web;
namespace HttpModules
{
    public class CompleteRequest : IHttpModule
    {
        public void Dispose() { }
        public void Init(HttpApplication application)
        {
            application.BeginRequest += new EventHandler(Application_BeginRequest);
            //下面两个是从剩下的近20个事件中随机选择的两个,可以发现这两个事件都不会触发,因为在BeginRequest中调用了CompleteRequest方法,导致直接跳到EndRequest事件。
            application.PostResolveRequestCache += (sender, e) => { application.Context.Response.Write("Application_PostResolveRequestCache<br/>"); };
            application.PostMapRequestHandler += (sender, e) => { application.Context.Response.Write("Application_PostMapRequestHandler<br/>"); };
            //EndRequest事件会被执行,PreSendRequestHeaders与PreSendRequestContent因为在EndRequest之后,因此也会被执行
            application.EndRequest += (sender, e) => { application.Context.Response.Write("Application_EndRequest<br/>"); };
            application.PreSendRequestHeaders += (sender, e) => { application.Context.Response.Write("Application_PreSendRequestHeaders<br/>"); };
            application.PreSendRequestContent += (sender, e) => { application.Context.Response.Write("Application_PreSendRequestContent<br/>"); };
        }
        public void Application_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication application = sender as HttpApplication;
            application.CompleteRequest();
            application.Context.Response.Write("请求被终止<br/>");
        }
    }
}

    在Web应用程序中添加AspxForCompleteRequest.aspx空页面(就算不是空页面也不会显示页面内容,因为已经HttpModule中终止请求,不会再去执行页面生命周期),且将Web.config中的配置内容修改为:

<httpModules>
  <!--只有测试“完整Application事件”时才使用下面的配置,否则请注释下面的配置-->
  <!--<add name="FullEvents" type="HttpModules.FullEvents,HttpModules"/>-->
  <!--只有测试“错误处理”时才使用下面的配置,否则请注释下面的配置-->
  <!--<add name="ErrorHandler" type="HttpModules.ErrorHandler,HttpModules"/>-->
  <!--只有测试“直接终止请求”时才使用下面的配置,否则请注释下面的配置-->
  <add name="CompleteRequest" type="HttpModules.CompleteRequest,HttpModules"/>
</httpModules>

    运行AspxForCompleteRequest.aspx得到结果如下:


    可以看到,Application在BeginRequest中调用了CompleteRequest方法,终止了本次请求,跳过PostResolveRequestCache等等中间事件(包括执行目标页面的生命周期)而直接跳到EndRequest事件。

    可以选择在任意HttpModule的EndRequest之前的任意事件中调用CompleteRequest方法,这里要注意的是:如果HttpModuleA在BeginRequest中调用了CompleteRequest方法,那么HttpModuleB、HttpModuleC的BeginRequest事件都不会执行,而直接转到依次执行三个HttpModule(A、B、C)的EndRequest事件。


    本篇博客的测试代码下载地址: 点击打开链接







第4章 ASP.NET的网页代码模型及生命周期 从本章开始,就进入了ASP.NET应用程序开发的世界。在了解了C#的结构,以及面向对象的概念后,就可以从面向对象的思想开发ASP.NET应用程序。在ASP.NET中,能够使用面向对象的思想和软件开发中的一些思想,例如封装、派生、继承以及高级的设计模式等。本章首先介绍ASP.NET中最重要的概念---网页代码模型。 4.1 ASP.NET的网页代码模型 在ASP.NET应用程序开发中,微软提供了大量的控件,这些控件能够方便用户的开发以及维护。这些控件具有很强的扩展能力,在开发过程中无需自己手动编写。不仅如此,用户还能够创建自定义控件进行应用程序开发以扩展现有的服务器控件的功能。 4.1.1 创建ASP.NET网站 在ASP.NET中,可以创建ASP.NET网站和ASP.NET应用程序ASP.NET网站的网页元素包含可视元素和页面逻辑元素,并不包含designer.cs文件。而ASP.NET应用程序包含designer.cs文件。创建ASP.NET网站,首先需要创建网站,单击【文件】按钮,在下拉菜单中选择【新建网站】选项,单击后会弹出对话框用于ASP.NET网站的创建,如图4-1所示。 图4-1 新建ASP.NET网站 在【位置】选项中,旁边的【下拉菜单】可以按照开发的需求来写,一般选择文件系统,地址为本机的本地地址。语言为.NET网站中使用的语言,如果选择Visual C#,则默认的开发语言为C#,否则为Visual Basic。创建了ASP.NET网站后,系统会自动创建一个代码隐藏页模型页面Default.aspx。ASP.NET网页一般由三部分组成,这三个部分如下所示。 q 可视元素:包括HTML,标记,服务器空间。 q 页面逻辑元素:包括事件处理程序和代码。 q designer.cs页文件:用来为页面的控件做初始化工作,一般只有ASP.NET应用程序(Web Application)才有。 ASP.NET页面中包含两种代码模型,一种是单文件页模型,另一种是代码隐藏页模型。这两个模型的功能完全一样,都支持控件的拖拽,以及智能的代码生成。 4.1.2 单文件页模型 单文件页模型中的所有代码,包括控件代码、事物处理代码以及HTML代码全都包含在.aspx文件中。编程代码在script标签,并使用runat=“server”属性标记。创建一个单文件页模型,在【文件】按钮中选择【新建文件】选项,在弹出对话框中选择【Web窗体】或在右击当前项目,在下拉菜单中选择【添加新建项】选项即可创建一个.aspx页面,如图4-2所示。 图4-2 创建单文件页模型 在创建时,去掉【将代码放在单独的文件中】复选框的选择即可创建单文件页模型的ASP.NET文件。创建后文件会自动创建相应的HTML代码以便页面的初始化,示例代码如下所示。 <%@ Page Language=“C#” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=“server”> </script> <html xmlns=“http://www.w3.org/1999/xhtml”> <head runat=“server”> <title>无标题页</title> </head> <body> <form id=“form1” runat=“server”> <div> </div> </form> </body> </html> 编译并运行,即可看到一个空白的页面被运行了。ASP.NET单文件页模型在创建并生成时,开发人员编写的类将编译成程序集,并将该程序集加载到应用程序域,并对该页的类进行实例化后输出到浏览器。可以说,.aspx页面的代码也即将会生成一个类,并包含内部逻辑。在浏览器浏览该页面时,.aspx页面的类实例化并输出到浏览器,反馈给浏览者。ASP.NET单文件页模型运行示例图如图4-3所示。 图4-3 单文件页模型 4.1.3 代码隐藏页模型 代码隐藏页模型单文件页模型不同的是,代码隐藏页模型将事物处理代码都存放在cs文件中,当ASP.NET网页运行的时候,ASP.NET类生成时会先处理cs文件中的代码,再处理.aspx页面中的代码。这种过程被成为代码分离。 代码分离有一种好处,就是在.aspx页面中,开发人员可以将页面直接作为样式来设计,即美工人员也可以设计.aspx页面,而.cs文件由程序员来完成事务处理。同时,将ASP.NET中的页面样式代码和逻辑处理代码分离能够让维护变得简单,同时代码看上去也非常的优雅。在.aspx页面中,代码隐藏页模型的.aspx页面代码基本上和单文件页模型的代码相同,不同的是在script标记中的单文件页模型的代码默认被放在了同名的.cs文件中,.aspx文件示例代码如下所示。 <%@ Page Language=“C#” AutoEventWireup=“true” CodeFile=“Default.aspx.cs” Inherits=“_Default” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=“http://www.w3.org/1999/xhtml”> <head runat=“server”> <title>无标题页</title> </head> <body> <form id=“form1” runat=“server”> <div> </div> </form> </body> </html> 从上述代码中可以看出,在头部声明的时候,单文件页模型只包含Language=“C#”,而代码隐藏页模型包含了CodeFile=“Default.aspx.cs”,说明被分离出去处理事物的代码被定义在Default.aspx.cs中,示例代码如下所示。 using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; //使用HtmlControls using System.Web.UI.WebControls; //使用WebControls using System.Web.UI.WebControls.WebParts; //使用WebParts public partial class _Default : System.Web.UI.Page //继承自System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } } 上述代码为Default.apx.cs页面代码。从上述代码可以看出,其格式类库、编写类的格式相同,这也说明了.aspx页面允许使用面向对象的特性,如多态、继承等。但是ASP.NET代码隐藏页模型的运行过程比单文件页模型要复杂,运行示例图如图4-4所示。 图4-4 代码隐藏页模型 上述描述了代码隐藏类模型的页面生成模型。当页面被呈现之前,ASP.NET应用程序会解释并编译相应的cs文件中的代码,此同时,ASP.NET应用程序还会将.aspx页面进行编译并生成.aspx页面对应的类。生成.aspx页面对应的类后会将该类cs文件中的类进行协调生成新的类,该类会通过IIS在用户浏览页面时呈现在用户的浏览器中。 4.1.4 创建ASP.NET Web Application ASP.NET网站有一种好处,就是在编译后,编译器将整个网站编译成一个DLL(动态链接库),在更新的时候,只需要更新编译后的DLL(动态链接库)文件即可。但是ASP.NET网站却有一个缺点,编译速度慢,并且类的检查不彻底。 相比之下,ASP.NET Web Application不仅加快了速度,只生成一个程序集,而且可以拆分成多个项目进行管理。创建Application,首先需要新建项目用于开发Web Application,单击菜单栏上的【文件】按钮,在下拉菜单中选择【新建项目】选项,在弹出窗口中选择【ASP.NET应用程序】选项,如图4-5所示。 图4-5 创建ASP.NET应用程序 在创建了ASP.NET应用程序后,系统同样会默认创建一个Default.aspx页面,不同的是,多出了一个Default.aspx.designer.cs,用来初始化页面控件,一般不需要修改。 4.1.5 ASP.NET网站和ASP.NET应用程序的区别 在ASP.NET中,可以创建ASP.NET网站和ASP.NET应用程序,但是ASP.NET网站和ASP.NET应用程序开发过程和编译过程是有区别的。ASP.NET应用程序主要有以下特点: q 可以将ASP.NET应用程序拆分成多个项目以方便开发,管理和维护。 q 可以从项目中和源代码管理中排除一个文件或项目。 q 支持VSTS的Team Build方便每日构建。 q 可以对编译前后的名称,程序集等进行自定义。 q 对App_GlobalResources 的Resource强类支持。 ASP.NET WebSite编程模型具有以下特点: q 动态编译该页面,而不用编译整个站点。 q 当一部分页面出现错误不会影响到其他的页面或功能。 q 不需要项目文件,可以把一个目录当作一个Web应用来处理。 总体来说,ASP.NET网站适用于较小的网站开发,因为其动态编译的特点,无需整站编译。而ASP.NET应用程序适应大型的网站开发、维护等。 4.2 代码隐藏页模型的解释过程 在ASP.NET的代码隐藏页模型中,一个完整的.aspx页面包含两个页面,分别是以.aspx和.cs文件为后缀的文件,这两个文件在形成了整个Web窗体。在编译的过程中都被编译成由项目生成的动态链接库(.DLL),同时,.aspx页面同样也会编译。但是.cs页面编译过程不同的是,当浏览者第一次浏览到.aspx页面时,ASP.NET自动生成该页的.NET类文件,并将其编译成另一个.DLL文件。 当浏览者再一次浏览该页面的时候,生成的.DLL就会在服务器上运行,并响应用户在该页面上的请求或响应,ASP.NET应用程序的解释过程图如4-6所示。 图4-6 代码隐藏页模型页面的执行过程 在客户端浏览器访问该页面时,浏览器会给IIS发送请求消息,IIS则会开始执行ASP.NET编译过程,如果不存在编译过后的DLL文件,则加载编译的类并创建对象。当创建对象完成,生成创建对象后的代码并生成一个ASPX页面代码,该页面代码反馈给IIS,IIS再反馈成HTML页面的形式给客户端。 4.3 代码隐藏页模型的事件驱动处理 在传统的ASP开发中,ASP的事件都是按照网页的顺序来处理的,一般情况下,ASP页面的事件都是从上到下处理事件,可以说ASP的开发是一个线性的处理模型。在用户的浏览操作中,每一次用户的操作都会导致页面重新被发送到服务器。因此,重复的操作必然导致客户端和服务器的往返过程,服务器必须重新创建页面,当创建页面后,服务器再按照原来的从上到下的顺序进行事件处理。 在ASP.NET中,通过使用模拟事件驱动模型的行为代替了ASP的线性处理模型。ASP.NET页框架模型隐式的为用户建立了事件和事件处理程序的关联。ASP.NET让用户可以为从浏览器传递的事件在服务器代码中设置相应的处理程序。假设某个用户正在浏览网站并页面之间产生了某种交互,用户的操作就会引发事件,事件通过HTTP被传输到服务器。在服务器中,ASP.NET框架解释信息,并触发事件之对应的处理程序。该程序可以是.aspx页面中的处理程序,也可以是开发者自定义的类库,或者COM组件等。事件驱动处理如图4-7所示。 图4-7 页面框架的事件驱动处理模型 上图则说明了当一个浏览者通过浏览器触发ASPX页面时,浏览器、服务器和服务器返回页的过程。 4.4 ASP.NET客户端状态 Web开发不像软件开发,Web应用实际上是没有状态的,这就说明Web应用程序不自动指示序列中的请求是否来自相同的浏览器或客户端,也无法判断浏览器是否一直在浏览一个页面或者一个站点,也无法判断用户执行了哪个操作并统计用户的喜好。 4.4.1 视图状态 从上面的章节中可以知道,当服务器每次的往返过程,都将销毁页面并重新创建新的页面。如果一个页面中的信息超出了页面的生命周期,那么这个页面中的相关信息就不存在了。如果注销了页面的信息,那么用户的一些信息可能就不存在了。 在ASP.NET中,网页包含视图状态来保存用户的信息,视图状态在页面发回到自身时,跨页过程存储和用户自己的页面的特定值,视图状态的优点如下所示。 q 不需要任何服务器资源。 q 在默认情况下,对控件启用状态的数据进行维护,不会被破坏。 q 视图状态的值经过哈希运算和压缩保护,安全性更高。 视图状态同样有一些缺点,缺点如下所示。 q 视图状态会影响性能,如果页面存储较大较多的值,则性能会有较大的影响。 q 在手机,移动终端上,可能无法保存视图状态中使用的值。 q 视图状态虽然安全性较高,但是还是有风险,如果直接查看页面代码,可以看到相应代码。 4.4.2 控件状态 ASP.NET中还提供了控件状态属性作为在服务器往返过程中存储自定义控件中的数据的方法。在页面控件中,如果有多个自定义控件使用多个不同的控件来显示不同的数据结构,为了让这些页面控件能够在在页面上协调的工作,则需要使用控件状态来保护控件,同时,控件状态是不能被关闭的。同样,控件状态也有它的优点,优点如下所示。 q 视图状态相同的是,不需要任何服务器资源。 q 控件状态是不能被关闭的,提供了控件管理的更加可靠的方法。 q 控件状态具有通用性。 4.4.3 隐藏域 在ASP中,通常使用隐藏域保存页面的信息。在ASP.NET中,同样具有隐藏域来保存页面的信息,作为维护页面状态的一种形式,但是隐藏域的安全性并不高,最好不要在隐藏域保存过多的信息。隐藏域具有以下优点。 q 不需要任何服务器资源。 q 支持广泛,任何客户端都支持隐藏域。 q 实现简单,隐藏域属于HTML控件,无需像服务器控件那样有需要编程知识。 而隐藏域具有一些不足,如下所示。 q 具有较高的安全隐患。 q 存储结构简单。 q 同样,如果存储了较多的较大的值,则会导致性能问题。 q 如果隐藏域过多,则在某些客户端中被禁止。 q 隐藏域将数据存储在服务器上,而不存储在客户端。 注意:如果开发中,页面的隐藏域过多,这些隐藏域被存储在服务器。当客户端浏览页面的时候,会有一些防火墙扫描页面,以保证操作系统的安全,如果页面的隐藏域过多,那么这些防火墙可能会禁止页面的某些功能。 4.4.4 Cookie Cookie在客户端用户保存网站的少量的用户信息,服务器可以通过编程的方法获取用户信息,Cookie信息和页面请求通常一起发送到服务器,服务器对客户端传递过来的Cookie信息做处理。通常Cookie保存用户的登录状态、用户名等基本信息等等,在后面的章节会详细介绍使用ASP.NET操作Cookies。 4.4.5 客户端状态维护 虽然使用某些客户端状态并不使用服务器资源,但是这些状态都具有潜在的安全隐患,如Cookie。非法用户可以使用Cookie欺骗来攻击网站进行用户信息的获取,不过使用客户端状态能够使用客户端的资源从而提高服务器性能。使用客户端状态,虽然有安全隐患,但是具有良好的编程能力,以及基本的安全知识,能够较好的解决安全问题,同时也能够提高服务器性能。下面小结了一些客户端状态的优缺点。 q 视图状态:推荐当存储少量挥发到自身的页面的信息时使用。 q 控件状态:不需要任何服务器资源,控件状态是不能被关闭的,提供了控件管理的更加可靠和更通用的方法。 q 隐藏域:实现简单,但是在应用程序中会造成一些安全隐患。 q Cookie:实现简单,同样也能够简单的获取用户的信息,但是Cookie有大小的限制,不适宜存储大量的代码。 4.5 ASP.NET页面生命周期 ASP.NET页面运行时,也同类的对象一样,有自己的生命周期ASP.NET页面运行时,ASP.NET页面将经历一个生命周期,在生命周期内,该页面将执行一系列的步骤,包括控件的初始化,控件的实例化,还原状态和维护状态等,以及通过IIS反馈给用户呈现成HTML。 ASP.NET页面生命周期ASP.NET中非常重要的概念,了解ASP.NET页面的生命周期,就能够在合适的生命周期内编写代码,执行事务。同样,熟练掌握ASP.NET页面的生命周期,可以开发高效的自定义控件。ASP.NET生命周期通常情况下需要经历几个阶段,这几个阶段如下所示。 q 页请求:页请求发生在页生命周期开始之前。当用户请求一个页面,ASP.NET将确定是否需要分析或者编译该页面,或者是否可以在不运行页的情况下直接请求缓存响应客户端。 q 开始:发生了请求后,页面就进入了开始阶段。在该阶段,页面将确定请求是发回请求还是新的客户端请求,并设置IsPostBack属性。 q 初始化:在页面开始后,进入了初始化阶段。初始化期间,页面可以使用服务器控件,并为每个服务器控件进行初始化。 q 加载:页面加载控件。 q 验证:调用所有的验证程序控件的Vailidate方法,来设置各个验证程序控件和页的属性。 q 回发事件:如果是回发请求,则调用所有事件处理的程序。 q 呈现:在呈现期间,视图状态被保存并呈现到页。 q 卸载:完全呈现页面后,将页面发送到客户端并准备丢弃时,将调用卸载。 4.6 ASP.NET生命周期中的事件 在页面周期的每个阶段,页面将引发可运行用户代码进行处理事件。对于控件产生的事件,通过声明的方式执行代码,并将事件处理程序绑定到事件。不仅如此,事件还支持自动事件连接,最常用的就是Page_Load事件了,除了Page_Load事件以外,还有Page_Init等其他事件,本节将会介绍此类事件。 4.6.1 页面加载事件(Page_PreInit) 每当页面被发送到服务器时,页面就会重新被加载,启动Page_PreInit事件,执行Page_PreInit事件代码块。当需要对页面中的控件进行初始化时,则需要使用此类事件,示例代码如下所示。 protected void Page_PreInit(object sender, EventArgs e) //Page_PreInit事件 { Label1.Text = “OK”; //标签赋值 } 在上述代码中,当触发了Page_PreInit事件时,就会执行该事件的代码,上述代码将Lable1的初始文本值设置为“OK”。Page_PreInit事件能够让用户在页面处理中,能够让服务器加载时只执行一次而当网页被返回给客户端时不被执行。在Page_PreInit中可以使用IsPostBack来实现,当网页第一次加载时IsPostBack属性为false,当页面再次被加载时,IsPostBack属性将会被设置为true。IsPostBack属性的使用能够影响到应用程序的性能。 4.6.2 页面加载事件(Page_Init) Page_Init事件Page_PreInit事件基本相同,区别在于Page_Init并不能保证完全加载各个控件。虽然在Page_Init事件中,依旧可以访问页面中的各个空间,但是当页面回送时,Page_Init依然执行所有的代码并且不能通过IsPostBack来执行某些代码,示例代码如下所示。 protected void Page_Init(object sender, EventArgs e) //Page_Init事件 { if (!IsPostBack) //判断是否第一次加载 { Label1.Text = “OK”; //将成功信息赋值给标签 } else { Label1.Text = “IsPostBack”; //将回传的值赋值给标签 } } 4.6.3 页面载入事件(Page_Load) 大多数初学者会认为Page_Load事件是当页面第一次访问触发的事件,其实不然,在ASP.NET生命周期内,Page_Load远远不是第一次触发的事件,通常情况下,ASP.NET事件顺序如下所示。 q 1. Page_Init()。 q 2. Load ViewState。 q 3. Load Postback data。 q 4. Page_Load()。 q 5. Handle control events。 q 6. Page_PreRender()。 q 7. Page_Render()。 q 8. Unload event。 q 9. Dispose method called。 Page_Load事件是在网页加载的时候一定会被执行的事件。在Page_Load事件中,一般都需要使用IsPostBack来判断用户是否进行了操作,因为IsPostBack指示该页是否正为响应客户端回发而加载,或者它是否正被首次加载和访问,示例代码如下所示。 protected void Page_Load(object sender, EventArgs e) //Page_Load事件 { if (!IsPostBack) { Label1.Text = “OK”; //第一次执行的代码块 } else { Label1.Text = “IsPostBack”; //如果用户提交表单等 } } 上述代码使用了Page_Load事件,在页面被创建时,系统会自动在代码隐藏页模型的页面中增加此方法。当用户执行了操作,页面响应了客户端回发,则IsPostBack为true,于是执行else中的操作。 4.6.4 页面卸载事件(Page_Unload) 在页面被执行完毕后,可以通过Page_Unload事件用来执行页面卸载时的清除工作,当页面被卸载时,执行此事件。以下情况会触发Page_Unload事件。 q 页面被关闭。 q 数据库连接被关闭。 q 对象被关闭。 q 完成日志记录或者其他的程序请求。 4.6.5 页面指令 页面指令用来通知编译器在编译页面时做出的特殊处理。当编译器处理ASP.NET应用程序时,可以通过这些特殊指令要求编译器做特殊处理,例如缓存、使用命名空间等。当需要执行页面指令时,通常的做法是将页面指令包括在文件的头部,示例代码如下所示。 <%@ Page Language=“C#” AutoEventWireup=“true” CodeBehind=“Default.aspx.cs” Inherits=“MyWeb._Default” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> 上述代码中,就使用了@Page页面指令来定义ASP.NET页面分析器和编译器使用的特定页的属性。当代码隐藏页模型的页面被创建时,系统会自动增加@Page页面指令。 ASP.NET页面支持多个页面指令,常用的页面指令如下所示。 q @ Page:定义ASP.NET页分析器和编译器使用的页特定(.aspx文件)属性,可以编写为<%@ Page attribute=“value” [attribute=“value”…]%>。 q @ Control:定义ASP.NET页分析器和编译器使用的用户控件(.ascx文件)特定的属性。该指令只能为用户控件配置。可以编写为<%@ Control attribute=“value” [attribute=“value”…]%>。 q @ Import:将命名空间显示导入到页中,使所导入的命名空间的所有类和接口可用户该页。导入的命名空间可以是.NET Framework类库或用户定义的命名空间的一部分。可以编写为<%@ Import namespace=“value” %>。 q @ Implements:提示当前页或用户控件实现制定的.NET Framework接口。可以编写为<%@ Implements interface=“ValidInterfaceName” %>。 q @ Reference:以声明的方式指示,应该根据在其中声明此指令的页对另一个用户控件或页源文件进行动态编译和链接。可以编写为<%@ Reference page | control=“pathtofile” %>。 q @ Output Cache:以声明的方式空间ASP.NET页或页中包含的用户控件的输出缓存策略。可以编写为<%@ Output Cache Duration=“#ofseconds” Location=“Any | Client | Downstream | Server | None” Shared=“True |False” VaryByControl=“controlname” VaryByCustom=“browser | customstring” VaryByHeader=“headers” VaryByParam=“parametername” %> q @ Assembly:在编译过程中将程序集链接到当前页,以使程序集的所有类和接口都可用在该页上。可以编写为<%@ Assembly Name=“assemblyname” %>或<%@ Assembly Src=“pathname” %>的方式。 q @ Register:将别名命名空间以及类名关联起来,以便在自定义服务器控件语法中使用简明的表示法。可以编写为<%@ Register tagprefix=“ tagprefix” Namespace=“namepace” Assembly=“assembly” %>或<%@ Register tagprefix=“ tagprefix” Tagname=“tagname” Src=“pathname” %>的方式。 4.7 ASP.NET网站文件类型 在ASP.NET中包含诸多的文件类型,这些类型的文件由ASP.NET支持和管理,而除了这些文件以外,其他的文件都由IIS托管。使用VS2008能够创建大部分可以使用ASP.NET托管运行的程序。同时,使用应用程序映射可以将文件类型映射到应用程序。当需要伪静态时,很可能需要将.html后缀托管到IIS中的应用扩展,因为默认情况下ASP.NET不会处理HTML的操作。 技巧:现在的网站构架中,生成静态是一种降低网站压力的一种很好的解决方案。在某些情况下,服务器可能需要伪静态支持,就是将.aspx页面后缀显式成.html后缀,让搜索引擎能够更好的搜录。 1.ASP.NET管理的文件类型 ASP.NET管理的文件类型能够在ASP.NET应用程序中被ASP.NET应用程序的不同模块进行访问和调用,这些文件可能是用户能够直接访问的,也有可能是用户无法直接访问的。ASP.NET管理的文件类型如表4-1所示。 表4-1 ASP.NET管理的文件类型 文件类型 保存位置 描述 .asax 根目录。 Global.asax 文件。包含 HttpApplication 对象的派生代码,用于重新展示 Application 对象。 .ascx 根目录或子目录。 可重用的自定义 Web 控件。 .ashx 根目录或子目录。 处理器文件。包含实现 IHttpHandler 接口的代码,用于处理输入请求。 .asmx 根目录或子目录。 XML Web Services 文件。包含由 SOAP 提供给其他 Web 应用的类对象和功能。 .aspx 根目录或子目录。 ASP.NET Web 窗体。包含 Web 控件和其他业务逻辑。 .axd 根目录。 跟踪视图文件。通常是 Trace.axd。 .browser App_Browsers 目录。 浏览器定义文件。用于识别客户端浏览器的可用特征。 .cd 根目录或子目录。 类图文件。 .compile Bin 目录。 定位于适当汇编集中的预编译文件。可执行文件(.aspx,.ascx,.master,theme)预编译后放在 Bin 目录。 .config 根目录或子目录。 Web.config 配置文件。包含用于配置 ASP.NET 若干特征的 XML 元素集。 .cs,.jsl,vb App_Code 目录。有些是 ASP.NET 的代码分离文件,位于 Web 页面相同的目录。 运行时被编译的类对象源代码。类对象可以是 HTTP 模块,HTTP 处理器,或 ASP.NET 页面的代码分离文件。 .csproj,vbproj,vjsproj Visual Studio 工程目录。 Visual Studio 客户工程文件。 .disco,.vsdisco App_WebReferences 目录。 XML Web Services Discovery 文件。用于定位可用 Web Services。 .dsdgm,dsprototype 根目录或子目录。 分布式服务图表(DSD)文件。可添加到 Visual Studio 方案中,为反向引擎提供消耗 Web Services 时的交互性图表。 .dll Bin 目录。 已编译类库文件。作为替代,可将类对象源代码保存到 App_Code 目录。 .licx,.webinfo 根目录或子目录。 许可协议文件。许可协议有助于保护控件开发者的知识产权,并对控件用户的使用权进行验证。 .master 根目录或子目录。 模板文件定义 Web 页面的统一布局,并在其他页面中得到引用。 .mdb,.ldb App_Data 目录。 Access 数据库文件。 .mdf App_Data 目录。 SQLServer 数据库文件。 .msgx,.svc 根目录或子目录。 Indigo Messaging Framework(MFx)服务文件。 .rem 根目录或子目录。 远程处理器文件。 .resources App_GlobalResources 或 App_LocalResources 目录。 资源文件。包含图像,本地化文本,或其他数据的资源引用串。 .resx App_GlobalResources 或 App_LocalResources 目录。 资源文件。包含图像,本地化文本,或其他数据的资源引用串。 .sdm,.sdmDocument 根目录或子目录。 系统定义模型(SDM)文件。 .sitemap 根目录。 网站地图文件。包含网站的结构。ASP.NET 通过默认的网站地图提供者,简化导航控件对网站地图文件的使用。 .skin App_Themes 目录。 皮肤定义文件。用于确定显示格式。 .sln Visual Web Developer 工程目录。 Visual Web Developer 工程的项目文件。 .soap 根目录或子目录。 SOAP 扩展文件。 注意:ASP.NET 管理的文件类型映射到 IIS 的 Aspnet_isapi.dll。 2.IIS 管理的文件类型 在ASP.NET应用程序中,有些动态的文件如asp文件就不被ASP.NET应用程序框架管理,这些文件由IIS进行管理,由IIS管理的文件类型如表4-2所示。 表4-2 IIS管理的文件类型 文件类型 保存位置 描述 .asa 根目录。 Global.asa 文件。包含 ASP 会话对象或应用程序对象生命周期中的各种事件处理。 .asp 根目录或子目录. ASP Web 页面。包含 @ 指令和使用 ASP 内建对象的脚本代码。 .cdx App_Data 目录. Visual FoxPro 的混合索引文件。 .cer 根目录或子目录。 证明文件。用于对网站的授权。 .idc 根目录或子目录。 Internet Database Connector(IDC)文件。被映射到 httpodbc.dll。 注意:由于无法为数据库连接提供足够的安全性,IDC 将不再被继续使用。IIS 6.0 是最后一个支持 IDC 的版本。 .shtm,.shtml,.stm 根目录或子目录。 包含文件。被映射到 ssinc.dll。 注意:IIS管理的文件类型被映射到IIS的asp.dll 3.静态文件类型 IIS仅提供已注册MIME类型的静态文件服务,注册信息保存在Mime Map IIS元数据库中。如果某种文件类型已经映射到指定应用程序,在不需要作为静态文件的情况之下,无需再在MIME类型列表中进行包含。默认的静态文件类型如表4-3所示。 表4-3 静态文件类型 文件类型 保存位置 描述 .css 根目录或子目录,以及 App_Themes 目录。 样式表文件。用于确定 HTML 元素的显示格式。 .htm,.html 根目录或子目录。 静态网页文件。由 HTML 代码编写。 注意:虽然ASP.NET的代码页面也能够手动添加到MIME类型列表中,但是这样操作浏览者就能够看到页面源代码,从而暴露ASP.NET页面源代码,相对于服务器而言是非常不安全的。 4.8 小结 本章介绍了ASP.NET页面生命周期,以及ASP.NET页面的几种模型。ASP.NET页面生命周期ASP.NET中非常重要的概念,熟练掌握ASP.NET生命周期能对ASP.NET开发,自定义控件开发起到促进作用。本章还介绍了: q 代码隐藏页模型的解释过程。 q 代码隐藏页模型的事件驱动处理。 q ASP.NET网页的客户端状态。 q ASP.NET页面生命周期。 q ASP.NET生命周期中的事件。 q ASP.NET 网站文件类型。 上面的章节都分开的讲解了ASP.NET运行中的一些基本机制,在了解了这些基本运行机制后,就能够在.NET框架下做ASP.NET开发了。虽然这些都是基本概念,但是在今后的开发中,会起到非常重要的作用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值