本文的目的旨在详细描述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文件中创建。清单1包含了在使用visual studio新建asp.net mvc web应用程序时默认的global.asax文件。

清单 1 – global.asax

using system;

using system.collections.generic;

using system.linq;

using system.web;

using system.web.mvc;

应用程序的路由表由routetable.routes的静态属性表示。这个属性表示了路由对象的集合。在清单1列出的global.asax文件 中,我们在应用程序首次启动时为路由表增加两个路由对象(application_start()方法在第一次请求网站页面的时候被调用一次)。 路由对象负责把url映射到handler。在清单1中,我们创建了两个路由对象。这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使用路由. 当我们对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 2008文档中的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。

下表列出了 MVC Web 项目的执行阶段。

阶段

详细信息

接收对应用程序的第一个请求

在 Global.asax 文件中,Route 对象将添加到 RouteTable 对象中。

执行路由

UrlRoutingModule 模块使用 RouteTable 集合中第一个匹配的 Route 对象来创建 RouteData 对象,然后使用所创建的对象创建 RequestContext 对象。

创建 MVC 请求处理程序

MvcRouteHandler 对象将创建 MvcHandler 类的实例,并将 RequestContext 实例传递给处理程序。

创建控制器

MvcHandler 对象使用 RequestContext 实例标识用于创建控制器实例的 IControllerFactory 对象(通常是 DefaultControllerFactory 类的实例)。

执行控制器

MvcHandler 实例调用控制器的 Execute 方法。

调用操作

对于从 ControllerBase 类继承的控制器,与该控制器关联的 ControllerActionInvoker 对象将决定要调用的 controller 类的操作方法,然后调用该方法。

执行结果

操作方法将接收用户输入,准备合适的响应数据,然后通过返回结果类型来执行结果。 可执行的内置结果类型包括:ViewResult(呈现视图并且是最常用的结果类型)、RedirectToRouteResultRedirectResultContentResultJsonResultFileResult 和 EmptyResult

详解ASP.NET MVC的请求生命周期的更多相关文章

  1. Asp.net MVC 之请求生命周期

    今天主要试着描述一下ASP.NET MVC 请求从开始到结束的整个生命周期,了解这些后,对MVC会有一个整体的认识. 这里主要研究了MVC请求的五个过程. 1.创建RouteTable 当ASP.NE ...

  2. ASP.NET MVC的请求生命周期

    我希望能理解在浏览器输入URL并敲击回车来请求一个ASP.NET MVC网站的页面之后发生的任何事情. 为什么需要关心这些?有两个原因.首先是因为ASP.NET MVC是一个扩展性非常强的框架.例如, ...

  3. 详解ASP.NET MVC 控制器

    1   概述 在阅读本篇博文时,建议结合上篇博文:详解ASP.NET MVC 路由  一起阅读,效果可能会更好些. Controller(控制器)在ASP.NET MVC中负责控制所有客户端与服务端的 ...

  4. ASP.NET MVC请求处理管道生命周期的19个关键环节(1-6)

    ASP.NET和ASP.NET MVC的HttpApplication请求处理管道有共同的部分和不同之处,本系列将体验ASP.NET MVC请求处理管道生命周期的19个关键环节. ①以IIS6.0为例 ...

  5. ASP.NET MVC请求处理管道生命周期的19个关键环节(7-12)

    在上一篇"ASP.NET MVC请求处理管道生命周期的19个关键环节(1-6) ",体验了1-6关键环节,本篇继续. ⑦根据IsapiWorkerRequest对象,HttpRun ...

  6. ASP.NET MVC请求处理管道生命周期的19个关键环节(13-19)

    在上一篇"ASP.NET MVC请求处理管道生命周期的19个关键环节(7-12) ",体验了7-12关键环节,本篇继续. ⒀当请求到达UrlRoutingModule的时候,Url ...

  7. ASP.NET MVC 小牛之旅4:ASP.NET MVC的运行生命周期

    ASP.NET MVC的运行生命周期大致分成三大过程:(1)网址路由对比. (2)运行Controller与Action. (3)运行View并回传结果. 4.1网址路由对比 当iis收到http请求 ...

  8. vue 源码详解(二): 组件生命周期初始化、事件系统初始化

    vue 源码详解(二): 组件生命周期初始化.事件系统初始化 上一篇文章 生成 Vue 实例前的准备工作 讲解了实例化前的准备工作, 接下来我们继续看, 我们调用 new Vue() 的时候, 其内部 ...

  9. 详解ASP.NET MVC应用程序请求生命周期

    ------转载当一个ASP.NET MVC应用程序提出请求,为了响应请求,包含一些请求执行流程步骤! 在ASP.NET MVC应用程序Http request 和Http response 过程中, ...

随机推荐

  1. [HAOI2009]求回文串

    神奇到爆炸的贪心,策略很简单.但是实现上好像比较恶心.换了一种思路.先保存所有点应该转移到的位置,BIT搞个逆序对就好了. 如何找到每个点应该转移到的位置?这个处理方式也是比较玄学.看代码吧. //O ...

  2. <<< java环境搭建

    先百度搜索"jdk下载"            安装完成之后,到系统环境变量设置(电脑右键,属性,高级系统设置) 然后点击下面path系统变量,把C:\Program Files ...

  3. AXIS 调用 webservice服务时传递 服务器验证需要的用户名密码

    System.setProperty("javax.net.ssl.trustStore", T.class.getResource(".").getPath( ...

  4. 面试题目——《CC150》中等难题

    面试题17.1:编写一个函数,不用临时变量,直接交换两个数. 思路:使用差值或者异或 package cc150.middle; public class Exchange { public stat ...

  5. ASP.NET WEB API路由机制

    (一)路由原理 (二)路由设计架构分析 RouteBase

  6. Hadoop概括——学习笔记<一>

    之前有幸在MOOC学院抽中小象学院hadoop体验课. 这是小象学院hadoop2.X概述第一章的笔记 第一章主要讲的是hadoop基础知识.老师讲的还是比较全面简单的,起码作为一个非专业码农以及数据 ...

  7. jquery numberbox赋值

    numberbox不能使用$('#id').val( '');只能使用$('#id').numberbox('setValue','');

  8. [Kerberos] Kerberos 认证过程整理

    Kerberos是一种安全认证协议,意在提供 more secure authentication simplified management of password convenience of s ...

  9. 【Go入门教程4】struct类型(struct的匿名字段)

    struct Go语言中,也和C或者其他语言一样,我们可以声明新的类型,作为其它类型的属性或字段的容器.例如,我们可以创建一个自定义类型person代表一个人的实体.这个实体拥有属性:姓名和年龄.这样 ...

  10. C和指针 第五章 逻辑位移与算术位移

    对于操作数的左位移都是相同的,右边空出来的位置用0补齐. 但是对于右位移,对于有符号和无符号数是不一样的,最高位的1有两种处理方式.逻辑位移和算术位移. 逻辑位移:右移入位用0补齐 算术位移:右移入位 ...