.NET MVC页面生命周期及传统ASP.NET页面周期

目前我主要使用.Net MVC框架进行网页创建,数据库是MSSQL Server。所以,我就用.NET MVC框架的web页面周期来说明页面的生命周期,但是我觉着其他MVC框架也是大同小异的。

本文主要分两个部分

一、.NET MVC的网页生命周期

二、普通ASP.NET的网页生命周期

一、.NET MVC的网页生命周期

ASP.NET MVC请求从开始到结束的每一个过程,在浏览器输入URL并敲击回车来请求一个ASP.Net MVC网站的页面之后发生的任何事情,都是页面的生命周期的一部分。

为什么需要关心这些?有两个原因。首先是因为ASP.NET MVC是一个扩展性非常强的框架。例如,我们可以插入不同的ViewEngine来控制网站内容呈现的方式。我们还可以定义控制器生成和分配到某个请求的方式。因为我想发掘任何ASP.NET MVC页面请求的扩展点,所以我要来探究请求过程中的一些步骤。

其次,如果你对测试驱动开发佷感兴趣,当为控制器写单元测试时,我们就必须理解控制器的依赖项。在写测试的时候,我们需要使用诸如Typemock Isolator或Rhino Mocks的Mock框架来模拟某些对象。如果不了解页面请求生命周期就不能进行有效的模拟。

生命周期步骤概览

当我们对ASP.NET MVC网站发出一个请求的时候,会发生5个主要步骤:

步骤1:创建RouteTable

当ASP.NET应用程序第一次启动的时候才会发生第一步。RouteTable把URL映射到Handler。

步骤2:UrlRoutingModule拦截请求

第二步在我们发起请求的时候发生。UrlRoutingModule拦截了每一个请求并且创建和执行合适的Handler。

步骤3:执行MvcHandler

MvcHandler创建了控制器,并且把控制器传入ControllerContext,然后执行控制器。

步骤4:执行控制器

控制器检测要执行的控制器方法,构建参数列表并且执行方法。

步骤5:调用RenderView方法

大多数情况下,控制器方法调用RenderView()来把内容呈现回浏览器。Controller.RenderView()方法把这个工作委托给某个ViewEngine来做。

现在让我们来详细研究每一个步骤:

步骤1:创建RouteTable

  当我们请求普通ASP.NET应用程序页面的时候,对于每一个页面请求都会在磁盘上有这样一个页面。例如,如果我们请求一个叫做SomePage.aspx的页面,在WEB服务器上就会有一个叫做SomePage.aspx的页面。如果没有的话,会得到一个错误。

  从技术角度说,ASP.NET页面代表一个类,并且不是普通类。ASP.NET页面是一个Handler。换句话说,ASP.NET页面实现了IhttpHandler接口并且有一个ProcessRequest()方法用于在请求页面的时候接受请求。ProcessRequest()方法负责生成内容并把它发回浏览器。

  因此,普通ASP.NET应用程序的工作方式佷简单明了。我们请求页面,页面请求对应磁盘上的某个页面,这个页面执行ProcessRequest()方法并把内容发回浏览器。

  ASP.NET MVC应用程序不是以这种方式工作的。当我们请求一个ASP.NET MVC应用程序的页面时,在磁盘上不存在对应请求的页面。而是,请求被路由转到一个叫做控制器的类上。控制器负责生成内容并把它发回浏览器。

  当我们写普通ASP.NET应用程序的时候,会创建很多页面。在URL和页面之间总是一一对应进行映射。每一个页面请求对应相应的页面。

  相反,当我们创建ASP.NET MVC应用程序的时候,创建的是一批控制器。使用控制器的优势是可以在URL和页面之间可以有多对一的映射。例如,所有如下的URL都可以映射到相同的控制器上。

http://MySite/Products/1
http://MySite/Products/2
http://MySite/Products/3

  这些URL映射到一个控制器上,通过从URL中提取产品ID来显示正确的产品。这种控制器方式比传统的ASP.NET方式更灵活。控制器方式可以产品更显而易见的URL。

  那么,某个页面请求是怎么路由到某个控制器上的呢?ASP.NET MVC应用程序有一个叫做路由表(Route Table)的东西。路由表映射某个URL到某个控制器上。一个应用程序有一个并且只会有一个路由表。路由表在Global.asax文件中创建。

  应用程序的路由表由RouteTable.Routes的静态属性表示。这个属性表示了路由对象的集合。我们在应用程序首次启动时为路由表增加两个路由对象(Application_Start()方法在第一次请求网站页面的时候被调用一次)。

  路由对象负责把URL映射到Handler。我们可以创建了两个路由对象。这2个对象都把URL映射到MvcRouteHandler。第一个路由映射任何符合{controller}/{action}/{id}模式的URL到MvcRouteHandler。第二个路由映射某个URL Default.aspx到MvcRouteHandler。

  顺便说一下,这种新的路由构架可以脱离ASP.NET  MVC独立使用。Global.asax文件映射URL到MvcRouteHandler。然而,我们可以选择把URL路由到不同类型的Handler上。这里说的路由构架包含在一个叫做System.Web.Routing.dll的独立程序集中。我们可以脱离MVC使用路由。

步骤2:UrlRoutingModule拦截请求

  当我们对ASP.NET MVC应用程序发起请求的时候,请求会被UrlRoutingModule HTTP Module拦截。HTTP Module是特殊类型的类,它参与每一次页面请求。例如,传统ASP.NET包含了FormsAuthenticationModule HTTP Module用来使用表单验证实现页面访问安全性。

  UrlRoutingModule拦截请求后做的第一件事情就是包装当前的HttpContext为HttpContextWrapper2对象。HttpContextWrapper2类和派生自HttpContextBase的普通HttpContext类不同。创建的HttpContext的包装可以使使用诸如Typemock Isolator或Rhino Mocks的Mock对象框进行模拟变得更简单。

  接着,Module把包装后的HttpContext传给在之前步骤中创建的RouteTable。HttpContext包含了URL、表单参数、查询字符串参数以及和当前请求关联的cookie。如果在当前请求和路由表中的路由对象之间能找到匹配,就会返回路由对象。

  如果UrlRoutingModule成功获取了RouteData对象,Module然后就会创建表示当前HttpContext和RouteData的RouteContext对象。Module然后实例化基于RouteTable的新HttpHandler,并且把RouteContext传给Handler的构造函数。

  对于ASP.NET MVC应用程序,从RouteTable返回的Handler总是MvcHandler(MvcRouteHandler返回MvcHandler)。只要UrlRoutingModule匹配当前请求到路由表中的路由,就会实例化带有当前RouteContext的MvcHandler。

  Module进行的最后一步就是把MvcHandler设置为当前的HTPP Handler。ASP.NET应用程序自动调用当前HTTP Handler的ProcessRequest()方法然后转入下一步。

步骤3:执行MvcHandler

  在之前的步骤中,表示某个RouteContext的MvcHandler被设置作为当前的HTTP Handler。ASP.NET应用程总是会发起一系列的事件,包括Star、BeginRequest、PostResolveRequestCache、 PostMapRequestHandler、PreRequestHandlerExecute和EndRequest事件(非常多的应用程序事件——对于完整列表,请查阅Visual Studio 文档中的HttpApplication类)。

之前内容中描述的所有东西都在PostResolveRequestCache和PostMapRequestHandler中发生。当前HTTP Handler的ProcessRequest()方法在PreRequestHandlerExecute事件之后被调用。

当之前内容中创建的MvcHandler对象的ProcessRequest()被调用的时候,会创建一个新的控制器。控制器由ControllerFactory创建。由于我们可以创建自己的ControllerFactory,所以这又是一个可扩展点。默认的ControllerFactory名字相当合适,叫做DefaultControllerFactory。

  RequestContext以及控制器的名字被传入ControllerFactory.CreateController()方法来获得一个控制器。然后,从RequestContext和控制器构造ControllerContext对象。最后,调用控制器类的Execute()方法。在调用Execute()方法的时候会给方法传入ControllerContext。

步骤4:执行控制器

  Execute()方法首先创建TempData对象(在Ruby On Rails中叫做Flash对象)。TempData可以用于保存下次请求必须的临时数据(TempData和会话状态差不多,不长期占用内存)。

接着,Execute()方法构建请求的参数列表。这些参数从请求参数中提取,将会被作为方法的参数。参数会被传入执行的控制器方法。

Execute()通过对控制器类进行反射来找到控制器的方法。控制器类是我们写的。Execute()方法找到了我们控制器类中的方法后就执行它。Execute()方法不会执行被装饰NonAction特性的方法。

至此,就进入了自己应用程序的代码。

步骤5:调用RenderView方法

  通常,我们的控制器方法最后会调用RenderView()或RedirectToAction()方法。RenderView()方法负责把视图(页面)呈现给浏览器。

  当我们调用控制器RenderView()方法的时候,调用会委托给当前ViewEngine的RenderView()方法。ViewEngine是另外一个扩展点。默认的ViewEngine是WebFormViewEngine。然而,我们可以使用诸如Nhaml的其它ViewEngine。

  WebForm的ViewEngine.RenderView()方法创建了一个叫做ViewLocator的类来寻找视图。然后,它使用BuildManager来创建ViewPage类的实例。然后,如果页面有ViewData就会设置ViewData。最后,ViewPage 的RenderView()方法被调用。

  ViewPage类从System.Web.UI.Page基类(和用于传统ASP.NET的页面一样)派生。RenderView()方法做的最后一个工作就是调用页面类的ProcessRequest()。调用视图的ProcessRequest()生成内容的方式和普通ASP.NET页面生成内容的方式一致。

  可扩展点

  ASP.NET MVC生命周期在设计的时候包含了很多可扩展点。我们可以自定义通过插入自定义类或覆盖既有类来自定义框架的行为。下面是这些扩展点的概要:

  路由对象:当我们创建路由表的时候,调用RouteCollection.Add()方法来增加新的路由对象。Add()方法接受了RouteBase对象。我们可以通过派生RouteBase基类来实现自己的路由对象。

  MvcRouteHandler :当创建MVC应用程序的时候,我们把URL映射到MvcRouteHandler对象上。然而,我们可以把URL映射到实现IRouteHandler接口的任何类上。路由类的构造函数接受任何实现IRouteHandler接口的对象。

  MvcRouteHandler.GetHttpHandler():MvcRouteHandler 类的GetHttpHandler()方法是virtual方法。默认情况下,MvcRouteHandler返回MvcHandler。如果愿意的话,我们可以覆盖GetHttpHandler()方法来返回不同的Handler。

  ControllerFactory:我们可以通过System.Web.MVC.ControllerBuilder.Current.SetControllerFactory()方法指定一个自定义类来创建自定义的控制器工厂。控制器工厂负责为某个控制器名和RequestContext返回控制器。

  控制器:我们可以通过实现Icontroller接口来实现自定义控制器。这个接口只有一个Execute(ControllerContext controllerContext)方法。

  ViewEngine:我们可以为控制器指定自定义的ViewEngine。通过为公共的Controller.ViewEngine属性指定ViewEngine来把ViewEngine指定给控制器。ViewEngine必须实现IviewEngine接口,接口只有一个方法:RenderView(ViewContext viewContext)。

  ViewLocator :ViewLocator把视图名映射到实际视图文件上。我们可以通过WebFormViewEngine.ViewLocator的属性来执行自定义的ViewLocator。

二、普通ASP.NET的网页生命周期

  大体上,ASP.NET请求的处理流程分为如下图所示的两步。用户发送一个请求到IIS服务器时:

  (1)ASP.NET会创建一个能够处理请求的环境。换句话说,它会创建一个包含请求、响应以及上下文对象的应用程序对象来处理这个请求。

  (2)一旦ASP.NET环境被创建,用户请求就会通过由modules(管道)、handlers(处理程序)和page objects(页面对象)触发的一系列事件进行处理。简而言之,我们暂且将此步凑称为MHPM(Module、Handler、Page和Module Event)。

ASP.NET环境的创建

  第一步:用户请求到达IIS后,IIS首先会检查哪一个ISAPI扩展能够处理这个请求,这会取决于文件的后缀名。例如:如果请求的是一个‘.aspx‘的页面,那么就会被传递到‘aspnet_isapi.dll‘来进行处理。

  第二步:如果这是该网站的首次请求,那么一个称为‘ApplicationManager‘的类会首先创建一个该网站可以运行的应用程序域(App Domain)。正如我们所知,应用程序域隔离部署在同一台IIS服务器上的两个不同的Web应用程序。因此,即使其中一个应用程序域出现了错误,也不会影响其他应用程序域的正常运作。

  (.NET平台下,程序集并没有直接加载进进程中。.NET可执行程序承载在进程的一个逻辑分区中,术语称应用程序域(简称AppDomain)。应用程序域是.NET引入的一个新概念,它比进程所占用的资源要少,可以被看作是一个轻量级的进程。在一个进程中可以包含多个应用程序域,一个应用程序域可以装载一个可执行程序(*.exe)或者多个程序集(*.dll)。这样可以使应用程序域之间实现深度隔离,所以:即使进程中的某个应用程序域出现错误,也不会影响其他应用程序域的正常运作。)

  第三步:在新创建的应用程序域中,会创建ASP.NET的宿主环境,也就是HttpRuntime对象。一旦宿主环境被创建完成,ASP.NET最核心的对象如HttpContext、HttpRequest和HttpResponse对象都会被创建好。

  第四步:一旦所有核心的ASP.NET对象被创建好,HttpApplication对象就会随之被创建来服务这个请求。如果你的系统中存在一个global.asax文件,那么这个global.asax文件的对象也会被创建。但是,需要注意的是你的global.asax需要继承自HttpApplication类。

  注意:在一个ASP.NET页面第一次附加到网站,一个HttpApplication实例便随之产生。为了最大化得提高处理性能,HttpApplication的实例将会被复用以处理多个请求。

  Global.asax 文件(也称作 ASP.NET 应用程序文件)是可选文件,包含用于响应 ASP.NET 或 HttpModule 引发的应用程序级别事件的代码。(换句话说,我们可以自定义后面我们所要介绍的一些事件,因为请求处理流程会经历后面的10多个事件,我们可以写代码来自定义其中的一些事件,加一些我们想做的业务逻辑操作,比如:URL重写、身份验证、图片水印等等。)如果不定义该文件,ASP.NET 页框架假设您未定义任何应用程序或会话事件处理程序。

  第五步:此时HttpApplication对象将会被分配给一系列的ASP.NET核心对象来处理请求的页面。

  第六步:这时,HttpApplication开始通过HTTP管道事件、处理程序(Handlers)和页面事件来处理请求了。也就是说:它会触发 MHPM 中的事件来处理请求。

时间: 2024-10-08 09:15:32

.NET MVC页面生命周期及传统ASP.NET页面周期的相关文章

asp.net页面的请求处理响应的过程描述

概述 本篇博客从IIS到asp.net页面后台运行完,整个过程做一个简单的描述,如果有不对的地方,望指出. IIS处理请求的过程 我们通过浏览器(Socket客户端)访问一个IIS服务器上的网页时,该请求到达IIS服务器上后,IIS的http.sys(分发器)组件就会根据相应的判断,将其交给对应的应用程序池(IIS上都有相应的注册信息),对应的应用程序池接收到请求后,会将其交给相应的工作进程进行处理,工作进程接到请求后,根据请求文件的后缀名,进行判断,如果此文件IIS可以处理,则直接处理,如果处

C# 问题解决思路--《数组bytes未定义》,ASP.NET页面加载顺序

好久没写博客了,废话不多说,直接说问题. 问题发生情况,首先这个是老项目,然后我是第一次修改.当我解决了各种引用,数据库配置之后等类似的问题,我启动的项目的时候,无任何问题,但是当我点击页面的按钮的时候,就报类型的错误. 因此,我按照我个人的经验,做出排查. 1.首先是代码问题,我根据按钮找到对应的后台事件,然后加上断点,发现根本就不进对应的断点.(说明不是按钮的后台代码的错误) 2.有可能是整个页面的后台代码的其他的错误,因此我搜索了该后台代码  看是否哪里出现了 bytes这个数组,结果没有

ASP.NET 页面间传递参数的方法

这个新特性意味着ASP.NET2.0开发人员目前有三种可供选择的技术来将数据从一个web页面传送到另外一个页面.这三种方法是:响应重定向,服务端传输和新的跨网页提交特性.我们可以已经熟悉前两种技术了,因此,我们只是简要地复习一下它们,然后我们会将主要精力放到学习如何使用跨网页提交特性,以及阐述一下这种方法和响应重定向以及服务传输方式有什么不同. 一.响应重定向方法 响应重定向方法是目前为止将一个网页重定向到另一个网页的最简单的方法的最简单的方法.当Web服务器接到一个重定向请求后,它会将一个响应

ASP.NET MVC深度接触:ASP.NET MVC请求生命周期

这篇博文的目的旨在详细描述ASP.NET MVC 请求从开始到结束的每一个过程.我希望能理解在浏览器输入URL 并敲击回车来请求一个ASP.NET MVC 网站的页面之后发生的任何事情. 为什么需要关心这些?有两个原因.首先是因为ASP.NET MVC 是一个扩展性非常强的框架.例如,我们可以插入不同的ViewEngine 来控制网站内容呈现的方式.我们还可以定义控制器生成和分配到某个请求的方式.因为我想发掘任何ASP.NET MVC 页面请求的扩展点,所以我要来探究请求过程中的一些步骤. 其次

【译】ASP.NET应用程序和页面生命周期

为何翻译此文 一.此文是Code Project社区2010年4月ASP.NET板块的最佳文章,说明了此文的份量: 二.锻炼自己的英文技术文章翻译能力,提高英文技术文档阅读能力: 三.了解掌握ASP.NET页面生命周期是非常必要的,这有助于我们更加灵活的控制页面,以我们需要的方式编程开发: 关于原文作者 原文作者:Shivprasad koirala 原文地址:http://www.codeproject.com/Articles/73728/ASP-NET-Application-and-Pa

【深入ASP.NET原理系列】--ASP.NET页面生命周期

前言 ASP.NET页面运行时候,页面将经历一个生命周期,在生命周期中将执行一系列的处理步骤.包括初始化.实例化控件.还原和维护状态.运行时间处理程序代码以及进行呈现.熟悉页面生命周期非常重要,这样我们才能在生命周期的合适阶段编写代码.如果我们能在写代码的时候想着我们现在是在做生命周期的哪一步那将是非常好的. 你可能会说我不清楚还不是一样写代码,反正每次都在Page_load里面写代码 然后页面就出来了我管那么多干什么.所谓知其然如果能知其所以然岂不是更吊?我个人认为做ASP.NET B/S开发

ASP.NET页面生命周期与控件生命周期

ASP.NET页面生命周期 (1)PreInit 预初始化(2)Init 初始化(3)InitComplete 初始化完成(4)PreLoad 预加载(5)Load 加载(6)LoadComplete 加载完成(7)PreRender 预输出(8)PreRenderComplete 预输出完成(9)Unload 卸载 ASP.NET控件生命周期 -- 实例化(Instantiate) 控件被页面或另一个控件通过调用它的构造器所实例化.这个步骤之后所列出的阶段,仅当控件加入控件树中才会发生. --

ASP.NET应用程序与页面生命周期

在本文中,我们将了解不同的事件,ASP.NET 应用程序的生命周期以浏览器向 Web 服务器(对于 ASP.NET 应用程序,通常为 IIS)发送请求为起点,直至将请求结果返回至浏览器结束.在这个过程中,首先我们需要了解ASP.NET请求的2个大致的步骤.其次我们将详细了解 'httphandler ',' httpmodule和 asp.net 页面对象(Page)中不同的事件的执行顺序,逻辑. 二个步骤的过程:asp.net请求处理,2步的过程如下所示,用户发送一个请求到IIS 服务器: 1

ASP.NET MVC 5 生命周期

原文地址: Lifecycle of an ASP.NET MVC 5 Application 具体内容请点击原文查看. 由于最近在做微信公众号开发,对于微信使用XML数据格式,已经表示有些无言以对. 查看ASP.NEt MVC的生命周期是为了查找是否有提供XML路由参数绑定到Action的解决方案. 在这里我只给出自己阅读后的一些重点. 可能文字比较少,但我觉得有图基本就够了.