ASP.NET 3.5 MVC框架深度解析
一、 简介
MVC(模型—视图—控制器)结构模式把一个应用程序拆分成三个主要组件:模型、视图和控制器。在创建基于MVC框架的Web应用程序时,你可以把ASP.NET MVC框架作为ASP.NET Web表单模式方案的一种重要候选。较之于基于Web表单的应用程序,ASP.NET MVC框架是一个轻量级的、测试性强的描述框架,它能够与现有ASP.NET特征(例如母版页面和基于成员身份的认证)紧密集成。MVC框架定义于System.Web.Mvc命名空间中。
长时间以来,MVC是一种为众多开发人员所熟悉的标准设计模式。一些类型的Web应用程序受益于MVC框架,而另一些将继续使用基于Web表单与回寄技术的传统型ASP.NET应用程序模式,还有一些类型的Web应用程序则有可能把这两种模式结合到一起使用。总之,MVC框架方案与Web表单方案并非相互排斥的。
总体来看,MVC框架包括下列组件:
(一)模型。模型对象是实现应用程序数据域逻辑的部分。经常情况下,模型对象还负责从数据库中检索和存储模型状态。例如,一个产品对象有可能从一个数据库中检索信息,操作其中的数据,然后把更新信息写会SQL Server数据库中的某一个产品表格内。
【提示】在小型应用程序中,模型经常是一个概念上的分离而不是一个物理概念。例如,如果应用程序仅读取一个数据集并且要把它发送到视图中,那么应用程序就不必专门创建一个物理模型层和相关联的类。在这种情况下,数据就直接承担了一个模型对象的角色。
(二)视图。视图是负责显示应用程序的用户接口(UI)的组件。典型情况下,这个UI是基于模型数据创建的。一个典型的示例就是一个产品表格的编辑视图。在此视图中,可以基于一个产品对象的当前状态显示文本框、下拉列表框和复选框等控件。
(三)控制器。控制器组件的作用是负责处理用户交互,操作模型,并且最终选择一个要显示UI的视图并生成之。在一个MVC应用程序中,视图仅仅负责显示信息,而由控制器负责处理和响应用户输入与交互。例如,由控制器来处理查询字符串值,并且把这些值传递到模型,然后在模型中使用该值进行数据库查询。
MVC模式在帮助你把应用程序的不同方面(输入逻辑,业务逻辑和UI逻辑)分离开来的同时,提供了这些元素之间的松耦合。MVC模式要求每一种不同的逻辑应该存在于应用程序中的特定位置。例如,UI逻辑隶属于视图,而输入逻辑隶属于控制器,业务逻辑则隶属于模型。这种分离有助于你在构建一个应用程序管理复杂性,因为它能够使你在某一时刻仅专注于实现的一个方面。例如,你可以专注于视图而不去关心业务逻辑。
除了管理复杂性外,较于测试一个传统型ASP.NET Web应用程序,基于MVC模式进行开发使得测试应用程序更加容易。例如,在一个传统型ASP.NET Web应用程序中,单个的类既用于显示输出也用于响应用户输入。因此,为传统型ASP.NET应用程序编写自动化测试可能非常复杂,因为为了测试单个页面,你必须实例化页面类,它所有的子控件以及应用程序中的其他依赖类。总之,你需要实例化大量的类才能够运行该页面,所以这种情况下要比针对应用程序的单个部件编写测试困难得多。于是,针对传统型ASP.NET应用程序的测试很可能比实现一个使用MVC框架开发的应用程序的测试要复杂和艰难得多。而且,一个传统型ASP.NET应用程序在测试时还需要使用一个Web服务器。相比之下,MVC框架把组件分离开来并且大量地使用接口,这使得有可能独立于框架的其他部分而较容易地单独测试单个组件。
此外,一个MVC应用程序的三个主要组件之间的松耦合特征还推动了平行开发。例如,一名开发人员可以开发视图,而另一名开发人员则在开发控制器逻辑,第三名程序员则可以专注于模型部分的业务逻辑的开发。
二、 基于MVC框架Web应用程序的优点
归纳来看,ASP.NET MVC框架主要提供了下列优点:
- 通过把一个应用程序分离成相互独立的模型、视图和控制器三部分而进一步提高了应用程序复杂性的管理。
- 不再使用视图状态或基于服务器的表单技术。这使得MVC框架特别有利于想全面控制一个应用程序的行为的开发人员。
- 使用一种前端控制器(Front Controller)模式并通过单一的控制器来处理Web应用程序请求。这一特征使你有可能设计出一个支持极其丰富的路由基础结构的应用程序。有关于前端控制器的更多的信息,请参考http://go.microsoft.com/fwlink/?LinkId=106357。
- 提供对于测试驱动开发(TDD)的大力支持。
三、 基于Web表单Web应用程序的优点
传统型基于Web表单的框架能够提供下列优点:
- 支持事件模型。此模型能够保留HTTP传输过程中的状态,而这十分有利于在线业务式Web应用程序的开发。基于Web表单的应用程序提供大量的事件,所有这些事件都得到数以百计的服务器控件的支持。
- 使用一种页面控制器(Page Controller)模式,此模式能够把许多功能添加到单个的页面。有关于页面控制器的更多的信息,请参考http://msdn.microsoft.com/zh-cn/library/ms978764.aspx。
- 使用视图状态或基于服务器的表单,这使得状态信息管理更加容易。
四、 ASP.NET MVC框架特征
概括来看,ASP.NET MVC框架大致提供了下列重要特征:
- 实现应用程序基本任务(输入逻辑,业务逻辑和UI逻辑)的分离,支持测试及测试驱动开发(TDD)。MVC框架中所有的核心契约都是基于接口的,因此十分利于使用mock对象(这是一些模拟应用程序中实际对象的模拟对象)测试工程。你可以对应用程序进行单元测试而不必在真正的ASP.NET进程中运行控制器,这加快了单元测试的速度和灵活性。你可以使用与.NET框架相兼容的任何单元测试框架测试你的工程。
- 提供了一个可扩展的和可插接的框架。ASP.NET MVC框架组件的设计支持用户十分容易地对它们进行替换或定制。你可以设计并加入你自己的视图引擎,URL路由策略,行为方法参数串行化,以及其它组件。ASP.NET MVC框架还支持使用现有依赖性注入(即“Dependency Injection”,简称“DI”)和控制反转(IOC)容器模型。其中,DI允许你把对象注入到一个类中,而不是依赖于类去创建对象本身。而IOC指定,如果一个对象要求使用另一个对象,那么第一个对象应该从一个外部源(例如一个配置文件)得到第二个对象。这一支持极有利于对工程的测试。
- 实现了一个强有力的URL映射组件,支持你在应用程序中构建易于理解的可搜索的URL(而不是像诸多Web应用URL中的那种极其复杂的表达方式)。如今,URL中不必包括文件扩展名,而且其设计还支持URL命名模式。因此,这种模式有利于搜索引擎优化(SEO)和REST(表述性状态转移)寻址。
- 支持在现有ASP.NET页面(.aspx文件)、用户控件(.ascx文件)以及母版页面(.master文件)标记文件中把标记用作视图模板。你可以在使用ASP.NET MVC框架的过程中继续使用现有ASP.NET特征,例如嵌套的母版页面,内联表达式(<%=%>),声明性服务器控件,模板,数据绑定,本地化,等等。
- 支持现有ASP.NET特征。ASP.NET MVC框架支持你使用例如表单和Windows认证,URL授权,会员和角色,输出缓冲和数据缓冲,会话和配置状态管理,健康监视,配置系统,提供者架构,以及其它ASP.NET特征。
五、 URL映射
典型情况下,在一个ASP.NET网站中,URL会被映射到存储在磁盘上的文件(主要是指.aspx文件)。这些.aspx文件中将包括针对客户端发出的请求要处理的代码或标记。
ASP.NET MVC框架以不同于ASP.NET网站应用程序的方式把URL映射到服务器代码。MVC框架不是把URL映射到ASP.NET页面或HTTP处理器程序,而是直接把URL映射到特定的控制器类。然后,由控制器类负责处理发来的请求,例如用户输入与交互,以及基于用户输入执行适当的应用程序及数据逻辑。典型地,一个控制器类调用一个单独的视图组件来生成相应的HTML输出作为对客户端发出请求的响应。
在ASP.NET MVC框架中,模型、视图和控制器组件被分离开来。典型地,模型组件主要负责维护数据库中持久数据的状态,而视图组件则由控制器进行选择并最终在客户端生成相应的UI。默认情况下,ASP.NET MVC框架使用现有ASP.NET页面(.aspx),母版页面(.master)和用户控件(.ascx)类型作为在浏览器端的输出形式。控制器组件将定位控制器中适当的行为方法,取得用作行为方法的参数值,处理执行行为期间所有可能发生的错误,并生成要求的视图。每一组组件各自存在于一个MVC Web应用程序工程的一个单独的文件夹下。
通过提供了一个URL映射引擎,ASP.NET MVC框架在把映射URL到控制器类方面提供了相当的灵活性。你可以使用这种映射引擎来定义路由规则,然后ASP.NET MVC框架使用这些路由规则来计算发来的URL请求并选择要执行的控制器。你还可以让路由引擎自动地分析URL中定义的变量并且使ASP.NET MVC框架把这些内容以参数方式传递给控制器。
六、 MVC框架与页面回寄
ASP.NET MVC框架并不使用传统的ASP.NET回寄模型来实现与服务器端的交互。代之的是,所有的终端用户交互都被路由到一个控制器类。这有利于保持UI逻辑和业务逻辑的分离,并且有助于测试目的。结果是,ASP.NET视图状态和ASP.NET页面生命周期事件并没有与基于MVC的视图集成到一起。
七、 创建一个简单的ASP.NET MVC应用程序
ASP.NET MVC框架提供了支持Visual Studio的工程模板,从而让你创建支持MVC模式的Web应用程序。
这些MVC工程模板包括:
- “ASP.NET MVC Web Application”模板
- “ASP.NET MVC Web Application and Test”模板
这些模板可以用于创建一个新的基于ASP.NET MVC框架的Web应用程序。在这些程序中,你可以使用文件夹,模板以及配置文件等手段配置它们。
默认情况下,当你使用“ASP.NET MVC Web Application and Test”模板创建一个新的Web应用程序时,Visual Studio将创建一个添加有两个工程的方案。第一个工程是一个Web工程,你可以在其中实现你的应用程序。第二个工程是一个测试工程,你可以在此为你的MVC组件编写单元测试。
【注意】“ASP.NET MVC Web Application”模板基于“ASP.NET Web Application”模板。所以,在创建基于ASP.NET MVC框架的网站时,你需要从“File”菜单下选择“New Project”,然后选择一个新的ASP.NET MVC工程,而不是选择“New Website”。
最后,你可以使用任何与.NET框架兼容的单元测试框架来测试ASP.NET MVC应用程序。注意,Visual Studio 2008 Professional(以及Team System)已经提供了对于MSTest测试工程的内置的支持。
八、 Web应用程序的MVC工程架构
当你创建一个ASP.NET MVC Web应用程序工程时,诸MVC组件将基于如图1所示的工程文件夹加以分离:
图1—一个ASP.NET MVC Web应用程序的典型架构
- Views文件夹。该Views文件夹是推荐的放置你的视图的位置。视图组件主要使用.aspx,.ascx和.master文件实现;此外,也有可能使用其他任何与视图有关联的文件。Views文件夹下针对每一个控制器都提供一个独立的文件夹,而且此文件夹以该控制器的名字为前缀命名。例如,如果你有一个名字为HomeController的控制器,那么你的Views文件夹下应该包含一个名字为Home的文件夹。默认情况下,当ASP.NET MVC框架加载一个视图时,它将首先在Views\controllerName文件夹下使用要求的视图名查找一个相应的.aspx文件。此外,还存在一个缺省名字为Common的文件夹,不过它没有对应任何控制器。你可以在这个位置放置母版页面,脚本,CSS文件,以及其它当生成视图时所使用的文件。
- Controllers文件夹。Controllers文件夹是推荐的放置控制器的位置。
- Models文件夹。该Models文件夹是推荐的放置你的MVC Web应用程序的模型的位置。典型情况下,这里会包括定义与数据存储进行交互的逻辑代码,还有对象定义等等。
- App_Data。App_Data文件夹对应存储数据的物理位置。这个文件夹与在ASP.NET Web应用程序中的角色一样。
除了上面列举的文件夹外,一个MVC Web应用程序还使用下列一些重要的应用程序元素:
- Global.asax和Global.asax.cs。在文件Global.asax.cs的Application_Start方法中实现对路由的初始化。下面的代码展示了一个典型的Global.asax文件,其中包括了默认的路由逻辑。
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
//注意:如果把下列表达改写成Url="{controller}.mvc/{action}/{id}"即可自动支持IIS6
RouteTable.Routes.Add(new Route
{
Url = "{controller}/{action}/{id}",
Defaults = new { action = "Index", id = (string)null },
RouteHandler = new MvcRouteHandler()
});
RouteTable.Routes.Add(new Route
{
Url = "Default.aspx",
Defaults = new { controller = "Home", action = "Index", id = (string)null },
RouteHandler = new MvcRouteHandler()
});
}
}
- 配置文件。MVC Web应用程序配置文件Web.config负责注册HTTP模块。在httpModules节中实现注册UrlRoutingModule类,这个类负责分析URL并且把请求路由到适当的处理器。注意,这个入口能够支持应用程序在同一个工程中宿主MVC与非MVC处理器。
下列代码展示了一个ASP.NET MVC应用程序的httpModules节的内容:
<httpModules>
<add name="UrlRoutingModule"
type="System.Web.Mvc.UrlRoutingModule,
System.Web.Extensions, Version=
PublicKeyToken=31BF3856AD364E35" />
</httpModules>
当你在Visual Studio 2008 Professional(或Team System)选择一个ASP.NET MVC Web应用程序和测试工程模板时,方案中将自动包括一个测试工程。你可以使用MVC模板来创建测试以及内在接口的mock实现。
九、 理解MVC工程的执行过程
一个发送到ASP.NET MVC的Web应用程序请求首先要传递到UrlRoutingModule对象(这是一个HTTP模块)。然后,UrlRoutingModule对象分析该请求并执行路由选择。值得注意的是,UrlRoutingModule将选择匹配当前请求的第一个路由(Route)对象。
接下来,UrlRoutingModule对象从选择的路由对象中取得IHttpContext对象—由它进一步处理请求。默认情况下,这个IHttpContext就是MvcHandler对象。然后,MvcHandler对象进一步选择相应的控制器,最终由此控制器来处理该请求。
【注意】当一个ASP.NET MVC Web应用程序运行于IIS7环境下时,并不要求为MVC工程指定文件的扩展名。然而,在IIS6中,处理器要求你把.mvc文件扩展名映射到ASP.NET ISAPI中。
模块和处理器成为指向ASP.NET MVC框架的入口,并执行下列动作:
- 在一个MVC Web应用程序中选择适当的控制器
- 获取一个特定的控制器实例
- 调用该控制器的Execute方法
表格1更细致地描述了一个MVC Web工程的各个执行阶段。
表格1—MVC Web工程的各个执行阶段
阶段 |
详细描述 |
初始请求 |
在Global.asax文件中,把路由添加到RouteTable对象。 |
路由 |
UrlRoutingModule模块根据RouteTable实例中匹配的Route对象创建RouteData对象。这些路由数据用于决定所请求的控制器及要调用的行为。 |
映射到控制器 |
MvcRouteHandler处理器负责根据RouteData实例中的数据创建控制器类型名。 |
调用控制器构建程序(controllerbuilder) |
处理器调用ControllerBuilder类的全局静态CreateController方法从而得到一个IController实例。如果没有返回一个IController实例,那么该处理器将返回一个HTTP 500错误指示出现了服务器错误。 |
创建控制器 |
由ControllerBuilder实例直接创建一个新的控制器,或使用一个IControllerFactory对象来创建该控制器。 |
执行控制器 |
MvcHandler实例被添加到ControllerContext对象中并调用该控制器的Execute方法。 |
十、 小结
在本文中,我们对微软最新推出的MVC(模型—视图—控制器)模式进行了理论的剖析,并把它与传统的ASP.NET Web表单模式和页面回寄方案进行了对比。在了解了MVC框架各组件主要功能之后,最后我们对一个典型的MVC Web工程的各个执行阶段作了粗略的归纳。
最后需要注意的是,ASP.NET MVC模式是微软最新推出的一种开发模式,目前最新测试版本为Preview 2(Preview 3也即将问世)。因此,这一模式是否较之于ASP.NET Web表单模式更有利于提高软件的生产效率还有待于开发实践的检验。