.NET (C#) Internals: ASP.NET 应用程序与页面生命周期(意译)

简介:

2010-04-29 16:05 by 吴秦, 15435 阅读, 18 评论, 收藏, 编辑

前几天逛codeproject时,遇到一篇比较好文章,不敢独享,故译之于君共享。Outline如下:

  • 1、引言
  • 2、两个处理步骤
    • 2.1、创建ASP.NET环境
    • 2.2、用触发的MHPM事件处理请求
  • 3、什么事件中应该做什么
  • 4、示例代码
  • 5、深入ASP.NET页面事件

1、引言

这篇文章我们将试图理解,从用户发送一个请求直到请求呈现到浏览器发生的事件的差异。因此,我们首先将介绍解ASP.NET请求的两个概括的步骤,接下来我们将介绍‘HttpHandler’,‘HttpModule’和ASP.NET页面对象发出的事件的差异。随着我们的事件旅程,我们将理解这些事件的逻辑。

2、两个处理步骤

ASP.NET请求处理可以总结为如下所示的两个处理步骤。用户发送一个请求到IIS:

  • ASP.NET创建处理请求的环境。换句话说,创建应用程序对象、request、response和context对象去处理请求。
  • 一旦环境已经创建,请求通过使用modules、handlers和page对象的一系列事件处理。为了简化可以称为MHPM(module、handler、page、module event),我们将在后面详细讨论。

1

图1、ASP.NET请求处理的两个步骤

在接下来的各节,我们将知道更多关于这两个步骤的细节。

2.1、创建ASP.NET环境

step 1:用户发送一个请求到IIS。IIS首先检查哪个ISAPI扩展可以处理这个请求,这取决于请求的文件扩展名。举例来说,如果请求页面是‘.ASPX’,它将被传送到‘aspnet_isapi.dll’来处理。

step 2:如果这是www站点的第一个请求,ApplicationManager类将创建一个应用程序域,www站点运行于其中。我们都知道在同一个IIS上,两个web应用程序的应用程序域是独立的(隔离的)。因此一个应用程序域中问题不会的影响到其它应用程序域。

step 3:新建的应用程序域创建宿主环境,如HttpRuntime对象。一旦宿主环境被创建,必要的ASP.NET核心对象如HttpContextHttpRequestHttpRespone对象也被创建。

step 4:一旦所有的ASP.NET核心对象被创建,HttpApplication对象将被创建去处理请求。如果系统中有global.asax文件,global.asax文件对象将被创建。请注意:global.asax文件继承自HttpApplication类。

注意:第一次ASP.NET页面连接到应用程序,一个HttpApplication新实例将被创建。为了最大化性能,HttpApplication实例可能被多个请求重用。

step 5:接下来HttpApplication对象分配给核心ASP.NET对象来处理页面。

step 6:然后HttpApplication通过HttpContextHttpRequestHttpRespone事件开始处理请求。它触发MHPM事件处理请求。更多细节

2

图2、创建ASP.NET环境

下图解释了ASP.NET请求的内部对象模型。最高层是ASP.NET运行时,它已经创建一个应用程序域(AppDomain),相应地有HttpRuntime包括request、respone、context对象。

3 图3、ASP.NET请求的内部对象模型

2.2、用触发的MHPM事件处理请求

一旦创建了HttpApplication,它开始处理请求,它经历3个不同的部分HttpModule、Page、HttpHandler。随着它移动到这些部分,将调用不同的事件,开发人员可以扩展和定制同一逻辑。在我们前进之前让我们了解什么是HttpModuleHttpHandlers。他们帮组我们在ASP.NET页处理的前后注入自定义逻辑。他们之间的主要差别是:

  • 如果你想要注入的逻辑是基于像‘.ASPX’、‘.HTML’这样的文件扩展名,使用HttpHandler。换句话说HttpHandler是基于处理器的扩展。

4

  • 如果你想在ASP.NET管道事件中注入逻辑,使用HttpModule。换言之是基于处理器的事件。

5

你可以点这了解他们之间更多的差异。下面是怎样处理请求的逻辑流。有四个重要的步骤MHPM,解释如下:

Step 1(M HttpModule):客户端请求处理开始。ASP.NET引擎开始和创建HttpModule发出事件(你可以注入定制逻辑)之前,有6个重要事件你可以使用:BeginRequestAuthenticateRequestAuthorizeRequestResolveRequestCacheAcquireRequestStatePreRequestHandlerExecute

Step 2(H HttpHandler):一旦上面6个事件触发,ASP.NET引擎将调用ProcessRequest事件,即使你已经在项目中执行了HttpHandler。

Step 3(P ASP.NET page):一旦HttpHandler逻辑执行,ASP.NET page对象被创建。ASP.NET page对象被创建,许多事件被触发,你可以在这些页面事件中写我们自定义的逻辑。有6个重要事件给我们提供占位,在ASP.NET页中写逻辑:InitLoadValidateEventRenderUnload。你可以记住单词SILVER来记这些事件,S-Start(没有任何意义,仅仅是为了形成一个单词),I(Init)、L(Load)、V(Validate)、E(Event)、R(Render)。

Step 4(M HttpModule):一旦页面对象执行了且从内存中卸载,HttpModule提供发送页面执行事件,它们可用于注入自定义post-处理逻辑。有4个重要的post-处理事件,PostRequestHandlerExecutePostRequestStateUpdateRequestCacheEndRequest

下图展示了上面的过程。

6

3、什么事件中应该做什么

下面的表格展示了什么事件中做什么逻辑或代码。

Section Event Description
HttpModule BeginRequest 此事件标志着一个新的请求,他保证在每个请求中都有。
HttpModule AuthenticateRequest 此事件标志ASP.NET运行时准备验证用户。任何身份验证代码都可以在此注入。
HttpModule AuthorizeRequest 此事件标志ASP.NET运行时准备授权用户。任何授权代码都可以在此注入。
HttpModule ResolveRequest 在ASP.NET中我们通常使用OutputCache指令做缓存。在这个事件中,ASP.NET运行时确定是否能够从缓存中加载页面,而不是从头开始生成。任何缓存的具体活动可以被注入这里。
HttpModule AcquireRequestState 此事件标志着ASP.NET运行时准备获得会话变量。可以对会话变量做任何你想要的处理。
HttpModule PreRequestHandlerExecute 恰好在ASP.NET 开始执行事件处理程序前发生。可以预处理你想做的事。
HttpHandler ProcessRequest HttpHandler逻辑被执行。在这个部分我们将为每个页面扩展名写需要的逻辑。
Page Init 此事件发生在ASP.NET页面且可以用来:
1、动态地创建控件,如果你一定要在运行时创建控件;
2、任何初始化设置
3、母版页及其设置
在这部分中我们没有获得viewstate、postedvalues及已经初始化的控件。
Page Load 在这部分ASP.NET控件完全被加载且在这里你可以写UI操作逻辑或任何其他逻辑。
Page Validate 如果在页面上你有验证器,你同样想在这里检查。
Page Render 是时候将输入发送到浏览器。如果你想对最终的HTML做些修改,你可以在这里输入你的HTML逻辑。
Page Unload 页面对象从内存中卸载。
HttpModule PostRequestHandlerExecute 可以注入任何你想要的逻辑,在处理程序执行之后。
HttpModule ReleaseRequestState 想保存更新某些状态变量,如会话变量。
HttpModule UpdateRequestCache 在结束之前是否更新你的缓存。
HttpModule EndRequest 这是将输出发送到客户端浏览器之前的最后一个阶段。

4、示例代码

点击下载代码,示例代码展示了事件是怎样触发的。代码中我们创建了一个HttpModule和HttpHandler,且我们显示一个简单的响应在所有的事件中。下面是HttpModule类,跟踪所有的事件且添加到全局集合。

HttpModule类public class clsHttpModule : IHttpModule
{
...... 
void OnUpdateRequestCache(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnUpdateRequestCache");
}
void OnReleaseRequestState(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnReleaseRequestState");
}
void OnPostRequestHandlerExecute(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnPostRequestHandlerExecute");
}
void OnPreRequestHandlerExecute(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnPreRequestHandlerExecute");
}
void OnAcquireRequestState(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnAcquireRequestState");
}
void OnResolveRequestCache(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnResolveRequestCache");
}
void OnAuthorization(object sender, EventArgs a)
{
objArrayList.Add("httpModule:OnAuthorization");
}
void OnAuthentication(object sender, EventArgs a)
{

objArrayList.Add("httpModule:AuthenticateRequest");
}
void OnBeginrequest(object sender, EventArgs a)
{

objArrayList.Add("httpModule:BeginRequest");
}
void OnEndRequest(object sender, EventArgs a)
{
objArrayList.Add("httpModule:EndRequest");
objArrayList.Add("<hr>");
foreach (string str in objArrayList)
{
httpApp.Context.Response.Write(str + "<br>") ;
}
} 
}

下面是HttpHandler的代码片段,它跟踪ProcessRequest事件。

HttpHandler代码片段public class clsHttpHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
clsHttpModule.objArrayList.Add("HttpHandler:ProcessRequest");
context.Response.Redirect("Default.aspx");
}
}

我们也追踪ASP.NET页面的所有事件。

asp.net页面事件public partial class _Default : System.Web.UI.Page 
{
protected void Page_init(object sender, EventArgs e)
{

clsHttpModule.objArrayList.Add("Page:Init");
}
protected void Page_Load(object sender, EventArgs e)
{
clsHttpModule.objArrayList.Add("Page:Load");
}
public override void Validate() 
{
clsHttpModule.objArrayList.Add("Page:Validate");
}
protected void Button1_Click(object sender, EventArgs e)
{
clsHttpModule.objArrayList.Add("Page:Event");
}
protected override void Render(HtmlTextWriter output) 
{
clsHttpModule.objArrayList.Add("Page:Render");
base.Render(output);
}
protected void Page_Unload(object sender, EventArgs e)
{
clsHttpModule.objArrayList.Add("Page:UnLoad");
}}

下面显示上面讨论的所有事件的执行顺序:

7

5、深入ASP.NET页面事件

在前面部分我们已经知道ASP.NET页面请求的整体事件流,但是我们没有详细讨论,因此本节我们将深入了解。任何ASP.NET页面有2个部分,一个是显示在浏览器上的页面,它有HTML标记、viewstate形式的隐藏值、HTML inputs上的数据。当页面被发送时,在服务器上这些HTML标记被创建到ASP.NET控件且viewstate和表单数据捆绑在一起。一旦你得到这些服务器控件的后台代码,你可以执行和写你自己的逻辑和呈现返回给浏览器。

8 现在这些HTML控件在服务器上作为ASP.NET控件,ASP.NET页面发出一些事件,我们可以注入自己的逻辑。根据任务/你要执行的逻辑,我们需要把这些逻辑放入适当的事件中。

注意:大部分开发者直接使用Page_Load方法执行一切,这不是一个好的方法。因此,不是填充控件、设置viewstate、应用主题等一切都发生在页面加载上。因此,如果我们能在适当的事件中放入逻辑,将真正使你的代码干净。

Seq Events 控件初始化 Viewstate可用 表单数据可用 什么逻辑可以写在这里?
1 Init No No No 注意:你可以通过使用ASP.NET请求对象访问表单数据等,但是不是通过服务器控件。
动态地创建控件,如果你一定要在运行时创建;任何初始化设置;母版页及其设置。在这部分中我们没有获得viewstate、postedvalues及已经初始化的控件。
2 Load View State Not guaranteed Yes Not guaranteed 你可以访问View State及任何同步逻辑,你希望viewstate被推倒后台代码变量可以在这里完成。
3 PostBackdata Not guaranteed Yes Yes 捏可以访问表单数据。任何逻辑,你希望表单数据被推倒后台代码变量可以在这里完成。
4 Load Yes Yes Yes 在这里你可以放入任何你想操作控件的逻辑,如从数据库填充combox、对grid中的数据排序等。这个事件,我们可以访问所有控件、viewstate、发送的值。
5 Validate Yes Yes Yes 如果你的页面有验证器或者你想为你的页面执行验证,那就在这里做吧。
6 Event Yes Yes Yes 如果这是通过点击按钮或下拉列表的改变的一个回发,相关的事件将被触发。与事件相关的任何逻辑都可以在这里执行。
7 Pre-render Yes Yes Yes 如果你想对UI对象做最终的修改,如改变属性结构或属性值,在这些控件保存到ViewState之前。
8 Save ViewState Yes Yes Yes 一旦对服务器控件的所有修改完成,可以保存控件数据到View State。
9 Render Yes Yes Yes 如果你想添加一些自定义HTML到输出,可以在这里完成。
10 Unload Yes Yes Yes 做任何你想做的清理工作。

9

原文链接:ASP.NET application and page life cycle

另附几篇相关的文章:

DotText源码学习——ASP.NET的工作机制

A low-level Look at the ASP.NET Architecture,对于的译文从底层了解ASP.NET体系结构




本文转自吴秦博客园博客,原文链接:http://www.cnblogs.com/skynet/archive/2010/04/29/1724020.html,如需转载请自行联系原作者

相关文章
|
10天前
|
数据可视化 网络协议 C#
C#/.NET/.NET Core优秀项目和框架2024年3月简报
公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:追逐时光者,第一时间获取每周精选分享资讯🔔)。
|
1月前
|
SQL 数据库 C#
C# .NET面试系列十一:数据库SQL查询(附建表语句)
#### 第1题 用一条 SQL 语句 查询出每门课都大于80 分的学生姓名 建表语句: ```sql create table tableA ( name varchar(10), kecheng varchar(10), fenshu int(11) ) DEFAULT CHARSET = 'utf8'; ``` 插入数据 ```sql insert into tableA values ('张三', '语文', 81); insert into tableA values ('张三', '数学', 75); insert into tableA values ('李四',
62 2
C# .NET面试系列十一:数据库SQL查询(附建表语句)
|
1月前
|
开发框架 算法 搜索推荐
C# .NET面试系列九:常见的算法
#### 1. 求质数 ```c# // 判断一个数是否为质数的方法 public static bool IsPrime(int number) { if (number < 2) { return false; } for (int i = 2; i <= Math.Sqrt(number); i++) { if (number % i == 0) { return false; } } return true; } class Progr
58 1
|
1月前
|
并行计算 安全 Java
C# .NET面试系列四:多线程
<h2>多线程 #### 1. 根据线程安全的相关知识,分析以下代码,当调用 test 方法时 i > 10 时是否会引起死锁? 并简要说明理由。 ```c# public void test(int i) { lock(this) { if (i > 10) { i--; test(i); } } } ``` 在给定的代码中,不会发生死锁。死锁通常是由于两个或多个线程互相等待对方释放锁而无法继续执行的情况。在这个代码中,只有一个线程持有锁,且没有其他线程参与,因此不
102 3
|
4天前
|
开发框架 前端开发 JavaScript
采用C#.Net +JavaScript 开发的云LIS系统源码 二级医院应用案例有演示
技术架构:Asp.NET CORE 3.1 MVC + SQLserver + Redis等 开发语言:C# 6.0、JavaScript 前端框架:JQuery、EasyUI、Bootstrap 后端框架:MVC、SQLSugar等 数 据 库:SQLserver 2012
|
1月前
|
开发框架 人工智能 .NET
C#/.NET/.NET Core拾遗补漏合集(持续更新)
C#/.NET/.NET Core拾遗补漏合集(持续更新)
|
1月前
|
SQL 存储 关系型数据库
C# .NET面试系列十:数据库概念知识
#### 1. 为什么要一定要设置主键? 设置主键是数据库设计中的一个重要概念,有几个主要原因: 1、唯一性 ```c# 主键必须保证表中的每一行都有唯一的标识。这样可以避免数据冗余和不一致性。如果没有主键或者主键不唯一,就可能出现数据混乱或错误。 ``` 2、查询性能 ```c# 数据库系统通常会使用主键来加速数据检索。主键通常会被索引,这样可以更快速地找到特定行的数据,提高查询效率。 ``` 3、关联性 ```c# 主键常常用于建立表与表之间的关系。在关系数据库中,一个表的主键通常与其他表中的外键建立关联,这种关系对于数据的一致性和完整性非常重要。 ``` 4、数据完
131 1
C# .NET面试系列十:数据库概念知识
|
1月前
|
XML 开发框架 .NET
C# .NET面试系列八:ADO.NET、XML、HTTP、AJAX、WebService
## 第二部分:ADO.NET、XML、HTTP、AJAX、WebService #### 1. .NET 和 C# 有什么区别? .NET(通用语言运行时): ```c# 定义:.NET 是一个软件开发框架,提供了一个通用的运行时环境,用于在不同的编程语言中执行代码。 作用:它为多语言支持提供了一个统一的平台,允许不同的语言共享类库和其他资源。.NET 包括 Common Language Runtime (CLR)、基础类库(BCL)和其他工具。 ``` C#(C Sharp): ```c# 定义: C# 是一种由微软设计的面向对象的编程语言,专门为.NET 平台开发而创建。 作
174 2
|
1月前
|
开发框架 中间件 .NET
C# .NET面试系列七:ASP.NET Core
## 第一部分:ASP.NET Core #### 1. 如何在 controller 中注入 service? 在.NET中,在ASP.NET Core应用程序中的Controller中注入服务通常使用<u>依赖注入(Dependency Injection)</u>来实现。以下是一些步骤,说明如何在Controller中注入服务: 1、创建服务 首先,确保你已经在应用程序中注册了服务。这通常在Startup.cs文件的ConfigureServices方法中完成。例如: ```c# services.AddScoped<IMyService, MyService>(); //
60 0
|
1月前
|
开发框架 前端开发 .NET
C# .NET面试系列六:ASP.NET MVC
<h2>ASP.NET MVC #### 1. MVC 中的 TempData\ViewBag\ViewData 区别? 在ASP.NET MVC中,TempData、ViewBag 和 ViewData 都是用于在控制器和视图之间传递数据的机制,但它们有一些区别。 <b>TempData:</b> 1、生命周期 ```c# TempData 的生命周期是短暂的,数据只在当前请求和下一次请求之间有效。一旦数据被读取,它就会被标记为已读,下一次请求时就会被清除。 ``` 2、用途 ```c# 主要用于在两个动作之间传递数据,例如在一个动作中设置 TempData,然后在重定向到另
95 5