有一只企鹅首先发现地球暖化、冰山在融化,回来告诉其它企鹅,却没有任一只企鹅愿意相信;因为企鹅们觉得现在生活过得很好,都不想费心思考如何改变、改变何时会来临,因此仍每天做例行性的工作,不愿去研究别人的观点。「有时人们会抗拒改变,其实只是不想改变;但是愿意接收新观念的技术人员,通常会活得比较久」。本帖只提供 MVC Pattern 的观念和架构介绍,仅供未接触过 MVC Framework 的 .NET 技术人员作为参考之用,以评估是否要深入学习或在将来的项目导入。
但二者本质上是完全不同的概念,如下: 3-Tier 或 N-Tier 的架构,中间层可能包含 EJB, Enterprise JavaBeans (J2EE),或 .NET Enterprise Services、Remoting、WCF: MVC 则为:
ASP.NET MVC provides a framework that enables you to easily implement the model-view-controller (MVC) pattern for Web applications. This pattern lets you separate applications into loosely coupled, pluggable components for application design, processing logic, and display. ASP.NET MVC also greatly facilitates test -driven development (TDD).
● View (JSP) 只纯粹用来生成输出,不涉及数据来源的访问、事件处理、各种逻辑处理和运算工作。因此更适合大型项目的分工,把这层完全交由美工人员制作 (而非程序员兼美工和排版)。此层如同 ASP.NET 中的 .aspx 前台页面,亦即 UI (User Interface)。 ● Model (JavaBean、EJB 组件) 用来存放独立且可重复使用的组件,包括:数据来源 (数据库) 的访问、商业逻辑的代码,并应与 View 作完整地切割,以便保留系统日后扩充或改写时的弹性。此层如同 ASP.NET 中的 App_Code 文件夹中的自定义类、DataSet (.xsd)、TableAdapter、…等等。 ● Controller (Java Servlet) 用来控制整个网站处理的「流程」,负责协调 View 和 Model 之间的流程传递和转向,也要管理和指派由哪个文件去接收用户所提出的「请求 (request)」,亦即由它决定要展现哪一个 aspx 文件给用户。当用户从浏览器送出 request 时 (例若用鼠标单击页面中的控件,或输入数据后单击 Button 的提交、输入 URL 网址、点选 hyperlink…等等),Controller 里定义的某些 method 会判断要交由 Model 中的哪些运算逻辑去处理,然后再判断要将处理结果传回哪一个 View 去显示出来。此外,Controller 还可包含错误处理、授权、输入验证…等功能的代码,集中统一处理,以避免传统 WebForm 里大量重复的代码。但说穿了,Controller 事实上只是一个自定义类,搭配一些 Attribute (特性)。而 Controller 也是目前 ASP.NET 所欠缺的部分。
● 第一种是将 HTML 和 .NET (Java) code 混在一起,俗称意大利面的写法,如:古早的 ASP。这种 Inline code 最为人垢病的问题,是代码的可读性低、难以维护。 ● 第二种是由与 .aspx 一对一搭配的 Code-Behind code (.aspx.cs) 直接访问数据库,亦即二层式的架构。但这样的缺点是代码难以重复使用,而且因为逻辑已写死在固定的页面里,会造成系统日后扩充、维护的困难。 ● 第三种是经由自定义类库、App_Code 文件夹中的自定义类或组件,去访问数据库,或做商业逻辑的运算 (JSP + JavaBean)。但此种做法仍缺乏「流程 (flow)」的统一控管,导致每一支 ASPX (JSP) 都要验证用户身份、验证 request 的参数、处理 Session、做例外处理,甚至包括 View 里的写码原则、语系设定,都得在每一支 ASPX 对应的 Code-Behind 去处理,也因此不适合大型系统的开发、扩充和维护。这种架构虽然也能做到虚拟式的三层式或多层式架构,但也是目前 ASP.NET 的极限。
但 MVC 架构也有其缺点,例如开发人员需要另外花时间转换观念及学习某一个 Framework,尤其是 .NET 的开发人员,因为过去较没有 Controller 统一协调流程的观念,势必得重新习惯,将很多原本写死在各个页面中的代码,统一改写在 Controller 里面。而且系统在设计阶段时,即要先协调好各个类及对象,彼此间数据交换的格式及做法,因此势必得拉长系统事先的分析、规划时程。但若能有像 Java Struts 或 ASP.NET MVC Framework 这样现成的框架可套用,则日后开发大型系统时,即可望达到事半功倍的效果。
事实上版工我在一年前试玩 ASP.NET MVC Beta 版时,当时的 View 默认仍配有 Code-Behind 文件,但里面空无一物,只有一行注释: 在前阵子发布的正式版里,View 默认已不配置 Code-Behind 文件。此外,ASP.NET MVC 里的 View,为了让项目开发更明确地切割,已不再有页面初始化和加载方法,也没有事件处理程序,除了基类声明以外没有任何内容,基类声明为 System.Web.Mvc.ViewPage,而不再是过去 WebForm 的 System.Web.UI.Page。
http://localhost:端口号/Home/index 事实上该页的内容,是去捉 Views/Home 文件夹底下,的 Index.aspx 的内容来呈现。其由 Global.asax.cs 配置,并用了「URL 重写 (Url Routing)」,让用户可以按你自己定义的的规则来访问网站 [14]。 在图 3 首页的右侧,会有两个 hyperlink,可分别导向「LogOn.aspx」、「About.aspx」页面;但 hyperlink 要导向哪一个页面,不是写死在 View 的页面中 (已无 Code-Behind),而是统一由 Global.asax.cs 去定义 Routing rules,去解析当浏览器收到 URL、表单或任何 request 时,应该要扔给哪一个 Controller 自定义类去处理;再由该个被指定的 Controller (如上图 2 里默认自动产生的 HomeController.cs),里面所定义的一或多个 System.Web.Mvc.ActionResult 类,以及 View 方法 (术语叫 action method),去设定要把 UI render 回哪一个 View (页面) 去,也就是本文一开始所提到的 (参考本帖图 1),Model 2 架构是由 Controller 文件,去统一控管整个 ASP.NET 系统的流程、页面导向,而不是写死在各个 aspx.cs 文件里。
using System.Web.Mvc; using System.Web.Routing; public class MvcApplication : System.Web.HttpApplication { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute( " {resource}.axd/{*pathInfo} "); // C# 3.0 的「匿名类型 (Anonymous Type)」语法 routes.MapRoute( " Default ", // Route name " {controller}/{action}/{id} ", // URL with parameters (控制器名 / 操作名称 / ID 参数) new { controller = " Home ", action = " Index ", id = "" } // Parameter defaults ); } }
MapRoute 方法里的三个参数,分别代表:routes、route name、URL。其中第一个参数 "Default",代表了项目中的 Default.aspx (不是位于 View 文件夹里)。这个 URL Routing 是为了解决用户直接访问域名时,会出现找不到文件的情形,所以要采用这个方法,将主页改成 Routing 到 Home/Index 上。因此当您在浏览器的地址栏输入: http://localhost:端口号/Default.aspx 亦会导至图 2 中的 Views/Home/Index.aspx。
RouteCollectionExtensions.MapRoute Method: ASP.NET MVC Framework (Part 1) (ScottGu 于 2007 年 11 月发表的 blog,图中为 ASP.NET MVC 在 Beta 版时的画面):
以下为 Site.master 的一部分代码: <% @ Master Language = " C# " Inherits = " System.Web.Mvc.ViewMasterPage " %> < ul id ="menu" > < li > <% = Html.ActionLink( " Home " , " Index " , " Home " ) %> </ li > < li > <% = Html.ActionLink( " About " , " About " , " Home " ) %> </ li > </ ul >
Controller 除了用来控管整个网站的流程转向外,事实上还能避免大量的代码重复,亦即统一处理一些系统可「共享」的功能,例如:验证用户身份、输入验证 (validate)、Session 管理,或是像购物网站中,当购物车里物品的添加、修改、移除时,任何用户皆相同的「共通逻辑」处理;以及要结帐时,去做购物数量、单价相乘的计算动作,或将某个存储购物明细的 Collection 数据结构对象加入 Session,最后再转向某一个页面 (View)。
总结: 本帖仅是给 .NET 技术人员,作为初步了解 MVC Pattern,以及 ASP.NET MVC Framework 的入门文章。若有兴趣深入研究的网友,可参考本帖最下方的「相关文件」,另博客园及网络上已有许多相关的帖子。至于通用的 MVC 观念,亦可参考坊间 JSP / Struts / J2EE 的网络论坛、相关书籍,或 Design Patterns (设计模式) 中的「复合模式 (Compound Pattern)」[15]。 如同 MSDN Magazine 中提到的 [7],ASP.NET MVC 不是用来取代传统 ASP.NET WebForm 的,两者有各自的优点和缺点,而将来的 ASP.NET MVC 还可能在 UI、控件拖曳、Routing 上持续改进。不过身为一个技术人员,最好能早点厘清两者的特性与优缺点 [4] [6],以评估是否要深入学习,或在将来的项目导入;并思考为什么微软要推出这个框架,且还花了至少两年的时间很慎重地去研发。
-------------------------------------------------------- 相关文件: [1] MVC : The Official Microsoft ASP.NET Site: [2] ASP.NET - Home: [3] ScottGu's Blog (美国微软开发部门总经理): [4] Model-View-Controller (MSDN): [5] Page Controller (MSDN): [6] Implementing Model-View-Controller in ASP.NET (MSDN): [7] Building Web Apps without Web Forms (MSDN Magazine): [8] 从零开始学习 ASP.NET MVC 1.0 (一) 开天辟地入门篇 (子秋的博客): [9] ASP.NET MVC Framework 与 WCSF 中 MVP 模式之小小比较 (Terry Lee): [10] 博客园 ASP.NET MVC 技术专题: [11] 一步一步学习 ASP.NET MVC 1.0 创建 NerdDinner 范例程序: [12] ASP.NET MVC 开发心得分享 (1):开发流程篇 (繁体中文): [13] .NET Framework Class Library: [14] URL Routing: -------------------------------------------------------- 相关书籍: [15] Head First 设计模式, Chapter 12: [16] Free ASP.NET MVC eBook Tutorial: [17] ASP.NET MVC in Action: [18] Pro ASP.NET MVC Framework: [19] Professional ASP.NET MVC 1.0: |