理解 ASP.NET 应用程序和页面生命周期 - 新手教程






4.70/5 (35投票s)
本文将介绍 ASP.NET 应用程序生命周期和页面生命周期。
引言
本文将介绍 ASP.NET 应用程序生命周期和页面生命周期。我们将尝试了解对 ASP.NET 开发者通常重要的事件以及在这些事件中需要做什么。
背景
作为一名 ASP.NET 开发者,理解 ASP.NET 应用程序生命周期和页面生命周期至关重要。由于 Visual Studio 提供了便捷的开发方式,有时新程序员会在不了解应用程序和页面生命周期的情况下开始编写 ASP.NET 页面。
从最终用户的角度来看,向 Web 服务器发出网页请求,Web 服务器会将页面返回给用户。对于技术稍强的用户,我们也可以说 Web 服务器会接收请求,执行一些服务器端活动,例如从数据库读取数据并定制 HTML 输出,然后将其推回给用户。从 ASP.NET 开发者的角度来看,这只是冰山一角。作为一名 ASP.NET 开发者,需要了解此请求是如何被处理的,即应用程序生命周期,以及网页是如何被处理并提供给用户的,即页面生命周期。
Using the Code
让我们从 ASP.NET 如何处理请求开始讨论。然后,我们将了解应用程序生命周期,以更好地控制请求和响应。最后,我们将了解页面生命周期,以了解何时需要执行操作以正确处理请求。
理解应用程序生命周期
每当用户请求网页时,请求都会发送到 IIS。IIS 然后会检查请求的 ISAPI(Internet 服务器应用程序编程接口)扩展,以确定如何处理该请求。如果请求是针对 .aspx 页面的,则请求将被重定向到 ASP.NET。
当 ASP.NET 引擎收到请求时,它会检查是否存在用于运行此请求的应用程序域。如果存在,它将使用该应用程序域;如果不存在,它将创建一个应用程序域并将请求传递给该应用程序域。
注意:应用程序域提供了实际的隔离级别,因此托管在同一 IIS 服务器上的各种网站不会相互干扰。
应用程序域创建后,将创建处理传入请求和生成正确响应所需的各种对象。以下对象用于实现相同目的:HttpContext
、HttpRequest
和 HttpResponse
。HttpContext
对象包含对 HTTPRequest
和 HTTPResponse
的引用(这些对象包含有关当前请求的信息)。HTTPRequest
对象专门包含有关当前请求的信息,即浏览器相关信息和现有 Cookie。HTTPResponse
对象包含有关将从服务器发送到客户端的响应的信息,即要写入的 Cookie 等。
创建了这些对象后,将创建 HTTPApplication
对象。此对象包含应用程序(当前应用程序域)通用的方法和事件。此对象特别重要的原因是它会引发对开发者有用的事件。如果我们的应用程序中有一个 global.asax 文件,我们可以处理此对象引发的所有事件并执行我们的应用程序特定操作(稍后我们将详细介绍事件)。
一旦 HTTPApplication
对象完成请求初始化、身份验证和授权事件,它就会将请求传递给 Page
。这通常是我们编写大部分代码的点。我们处理所有页面生命周期事件,以根据用户请求定制页面(我们稍后将详细介绍页面事件和生命周期)。
页面处理完成后,HTTPApplication
将执行清理和结束请求事件,然后将响应发送给用户。因此,如果我们尝试以算法的形式可视化上述过程,
1. User initiates the request.
2. Request is received by IIS and checked with ISAPI
2a. If the request is for an .aspx, pass it on to the ASP.NET
3. Check for the AppDmain.
3a. If application domain exists, use it.
3b. If AppDomain does not exist create it.
4. Create the core objects for HTTPContext, HTTPRequest and HTTPResponse.
5. Check if the HTTPApplication object exist.
5a. If it exist pass on the core objects to it.
5b. If it doesn't create it and then pass the core objects.
6.HTTPApplication object will process the request.
6a. HTTPApplication begin, authentication and authorization events will run.
7. The request will then be passed to the Page.
7a. Page events and complete Page life cycle will run.
8. The HTTPApplication object will run the cleanup and end events and
pass on the response to the user.
关于 HTTPHandlers 和 HTTPModules 的说明
HTTPHandlers
用于 ASP.NET 根据扩展来处理特定请求。另一方面,如果我们希望自己的功能与默认的 ASP.NET 功能一起工作,则可以使用 HTTPModules
。对于特定请求,只有一个 Handler,但可以有 N 个 Module。
为什么这很重要?这很重要,因为一旦 HTTPApplication
对象获得核心请求和响应对象,所有 HTTPModules
都将被创建,并且它们的 init()
方法将被调用。因此,如果我们的应用程序中有模块,我们可以在 global.asax 文件以及模块中处理应用程序事件。
对于 HTTPHandlers
,它们将在应用程序事件(包括 global.asax 和模块)执行后调用。如果我们为任何特定扩展注册了 Handler,那么它总是在应用程序开始、身份验证和授权事件之后调用。
因此,我们应用程序中的 HTTPModules
也可用于处理应用程序事件,即我们算法中的 6 和 8。而 HTTPHandlers
将在应用程序开始、身份验证和授权事件执行后介入,即我们算法中的 6 和 7 之间。
深入了解应用程序事件
现在我们已经理解了基本的应用程序生命周期。接下来重要的是了解应用程序事件的顺序,以便我们可以将自定义逻辑写在 global.asax 文件或模块中。理解事件顺序很重要,因为我们应该知道每个事件中可以做什么、应该做什么。
注意:我们将只介绍一些重要且常用的应用程序事件。有关事件的完整列表,MSDN 是一个不错的参考。
事件 | 应该做什么 |
BeginRequest | 此事件将在每个请求中调用。此事件表示新请求的开始。 |
AuthenticateRequest | 当 ASP.NET 准备好进行身份验证时,将触发此事件。所有用于用户身份验证的代码都应放在此处。 |
AuthorizeRequest | 当 ASP.NET 准备好进行授权时,将触发此事件。所有用于用户授权的代码都应放在此处。 |
AcquireRequestState | 此事件很重要,因为此时 ASP.NET 已准备好接受和使用 Session 变量。如果我们需要初始化一些 Session 变量,可以在此处或此后的任何事件中完成。 |
ProcessRequest | 此处将执行所需的 HTTPHandler 。如果它是 aspx 页面,则相应的 Handler 将被执行,并将请求传递给页面。 |
页面事件将在此处发生 | |
ReleaseRequestState | 此事件很重要,因为这是与 Session 变量交互的最后一点。 |
EndRequest | 此事件将在响应发送给用户之前触发。 |
理解页面生命周期
在讨论了应用程序生命周期和应用程序事件之后,现在是时候讨论页面生命周期和页面事件了。页面生命周期基本上包括
Start
初始化
Load (加载)
Validate
事件处理
Render
Unload
Start
阶段不与任何事件相关联。它只是表示请求已传递到页面。同样,Validate
和 Event
处理也不与任何预定义事件相关联。只是在 Load
完成后会发生验证,并且用户定义的控件的事件将在验证之后、渲染之前触发。
对于其余阶段,即 Initialize
、Load
、Render
和 Unload
,有预定义的事件与它们相关联。对于 Unload
,只有一个事件,但对于其余阶段,每个阶段都有 3 个事件与之相关联。第一个在阶段开始前触发,第二个在阶段期间触发,最后一个在工作完成后触发。因此,以下列表显示了 Page
的典型事件顺序。
PreInit
Init
InitComplete
Preload
Load (加载)
LoadComplete
PreRender
PreRenderComplete
Unload
在页面生命周期中还有一个具有重要意义的事件是 SaveStateComplete
。它将在 PreRenderComplete
之后调用,这表示从此时开始,ViewState
中的更改将不会被保留,即 ViewState
已为页面保存。
因此,如果我们现在尝试绘制一个关于事件顺序以及每个事件中应该做什么的矩阵。
事件 | 描述 |
PreInit | 在此事件中应设置所有动态控件、母版页、配置文件和主题。 |
Init | 应使用此事件设置控件属性的初始值。 |
InitComplete | 应使用此事件来处理自定义 ViewState 数据。这是 ViewState 已加载并且可以更改的第一个位置。 |
Preload | 可用于设置控件的属性。 |
Load (加载) | 此处可以执行所有数据库连接和数据绑定。在此事件结束之前,所有验证都将完成。事件完成后,所有控件的事件将在调用此列表中的下一个事件之前执行。 |
LoadComplete | 此事件可用于需要控件完全加载的活动。 |
PreRender | 这是页面上可以更改控件的视觉属性以在页面上显示它们之前的最后一个页面。 |
PreRenderComplete | 当页面准备就绪且无法再更改视觉元素时,将调用此事件。所有数据绑定此时都已完成。 |
SaveStateComplete | View State 已保存,从此时开始,ViewState 中的更改将不会被保留,即 ViewState 已为页面保存。 |
Unload | 页面处理现已完成。这是将要调用的最后一个事件。 |
这里需要考虑的重要一点是,所有这些事件都会调用其子控件的相应事件。Page
还会使其所有子控件的事件与其自身的事件保持同步。这就是为什么最好只在 PreInit
中添加动态控件。如果稍后添加动态控件,例如在 Load
中,那么页面将等待直到所有添加的控件都达到 load
状态并完全赶上页面事件。
现在让我们继续在一个玩具应用程序中查看所有这些事件顺序。示例应用程序包含在一个空的单页网站上处理的所有事件。所有事件都有一个顺序号,并且事件名称消息将打印在调试控制台中,以说明这些事件是如何触发的。以下是代码中的几个示例函数
void Application_BeginRequest(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("MyLog: 1. Application_BeginRequest");
}
void Application_AuthenticateRequest(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("MyLog: 2. Application_AuthenticateRequest");
}
这段代码的输出看起来像
红色高亮区域表示调用相应 Handler 的阶段,在本例中将调用 aspx 页面。
关注点
在本文中,我尝试从新手角度讨论 ASP.NET 应用程序生命周期和页面生命周期。大多数经验丰富的程序员可能已经知道这些内容。我仍然希望这能提供信息。
历史
- 2012年9月12日:第一个版本