快速搞懂 ASP.NET MVC
-Tier 或 N-Tier 的架构中间层可能包含 EJB, Enterprise JavaBeans (J2EE)或 .NET Enterprise Services、Remoting、WCFClient (WebPage) -- [ BLL (业务逻辑层) -- DAL (数据访问层) ] (Web Server) -- 数据来源或数据库Client (WebPage) -- [ BLL (业务逻辑层) -- DAL (数据访问层) ] (Web Server Application Server) -- 数据来源或数据库MVC 则为● Model (模型) - 包含 BLL、DAL。View 和 Controller 都依赖于 Model但是 Model 既不依赖于 View也不依赖于 Controller这是分离的主要优点之一● View (视图) - 仅负责生成输出 (UI)。以 ASP.NET MVC 来说不再有 Code-behind 这种 .aspx.cs 文件亦无 runatserver 标记、form 标记、控件声明、事件处理● Controller (控制器) - 控制整个系统的 workflow、运作逻辑、错误处理、身分验证和授权、用户输入数据验证、…等等总的来讲微软的 ASP.NET MVC Framework 是为了让 ASP.NET 更适合中、大型的项目能统一规划、控管整个网站系统的流程 (用 Controller 处理)并更有效地分工开发、日后维护 (避免许多 WebForm 里重复出现相同功能的自定义函数)、管控某个功能在修改后对系统整体的影响并将不同的功能作更明确地切割让不同专长的技术人员各司其职也顺便提高了代码的可读性便于测试 (TDD, test -driven development) (减少直接绑到用户界面中的代码量) [4]并达到「松散耦合 (loosely coupled)」让组件易于更换和重复使用。但也因此ASP.NET 程序员必须先改变过去把很多业务逻辑、输入验证、页面切换…等杂七杂八的功能全部写在 Code-Behind (aspx.cs) 里的旧习惯。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).事实上MVC Pattern 的发明已有二十多年其与再衍生的观念和框架 (MVP)在 Java / J2EE / Struts 的一些 Framework 早已行之多年。MVC 模式的结构如下 [4]● 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 所欠缺的部分。图 1 MVC (Model 2) 架构的运作方式可与下图 2 的 ASP.NET MVC 项目相互对应事实上在 Java / JSP / JavaEE 常引用的网站开发架构中还可分为 Model 1 与 Model 2。而 Model 1 还可分为二至三种如下● 第一种是将 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 的极限。至于 Model 2即俗称的 MVC Pattern则加入了 Controller 的部分将流程及事件交由中心控管除了可让整个系统的运作流程更为明确有效切开各层的工作亦可避免让 View 里的 Code-Behind 去处理 Model 中的数据库访问、商业逻辑运算也不必再每个页面重复撰写「流程传递和转向」的代码而改由中央的 Controller 程序代码 (action method) 来统一控管。但 MVC 架构也有其缺点例如开发人员需要另外花时间转换观念及学习某一个 Framework尤其是 .NET 的开发人员因为过去较没有 Controller 统一协调流程的观念势必得重新习惯将很多原本写死在各个页面中的代码统一改写在 Controller 里面。而且系统在设计阶段时即要先协调好各个类及对象彼此间数据交换的格式及做法因此势必得拉长系统事先的分析、规划时程。但若能有像 Java Struts 或 ASP.NET MVC Framework 这样现成的框架可套用则日后开发大型系统时即可望达到事半功倍的效果。要开发 ASP.NET MVC必须要有 Visual Studio 2008 SP1并下载 MVC 套件 (经过至少两年的研发微软终于在 2009 年 4 月发布了 1.0 正式版)下载网址如下ASP.NET MVC 1.0 download (2009/04/09):http://www.microsoft.com/downloads/details.aspx?FamilyID53289097-73ce-43bf-b6a6-35e00103cb4bdisplaylangen安装完成后在新建项目时会多出一个「ASP.NET MVC Web Application」的选项。创建一个默认的 MVC 项目后其在 VS 解决方案的结构如下图所示图 2 View 中的 ASPX、ASCX、MasterPage 纯粹负责显示用默认没有配置 Code-Behind 文件 (aspx.cs)如上图 2ASP.NET MVC 项目会自动产生存储 MVC 文件的三个文件夹、控制流程的两个 Controller 类、多个没有 Code-Behind 的 View 页面和定义 Routing rules 的 Global.asax.cs。事实上版工我在一年前试玩 ASP.NET MVC Beta 版时当时的 View 默认仍配有 Code-Behind 文件但里面空无一物只有一行注释!-- Please do not delete this file. It is used to ensure that ASP.NET MVC is activated by IIS when a user makes a / request to the server. --在前阵子发布的正式版里View 默认已不配置 Code-Behind 文件。此外ASP.NET MVC 里的 View为了让项目开发更明确地切割已不再有页面初始化和加载方法也没有事件处理程序除了基类声明以外没有任何内容基类声明为 System.Web.Mvc.ViewPage而不再是过去 WebForm 的 System.Web.UI.Page。新创的 ASP.NET MVC 项目直接按 F5 即可执行如下图 3 所示。但由于 MVC 的 View 会在显示前先要求 Controller 运行 (参考图 1)因此若您尝试将 View 里的某个 .aspx 设为「起始页」后按 F5 执行时反而会发生「HTTP 404. The resource cannot be found.」的错误亦即若想直接导航至该页面将不起作用。图 3 ASP.NET MVC 项目的首页您会发现浏览器里的 URL 会像下面这样网址不是具体的 .aspx 扩展名http://localhost:端口号/Home/indexhttp://localhost:端口号事实上该页的内容是去捉 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 文件里。以下为 Global.asax.cs 的一部分代码网友们可搭配参考上图 2 里 Controller、View 的结构去阅读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或http://localhost:端口号亦会导至图 2 中的 Views/Home/Index.aspx。System.Web.Mvc Namespace (MSDN Library, 暂无中文版):http://msdn.microsoft.com/en-us/library/system.web.mvc.aspxRouteCollectionExtensions.MapRoute Method:http://msdn.microsoft.com/en-us/library/dd470521.aspxASP.NET MVC Framework (Part 1) (ScottGu 于 2007 年 11 月发表的 blog图中为 ASP.NET MVC 在 Beta 版时的画面):http://weblogs.asp.net/scottgu/archive/2007/11/13/asp-net-mvc-framework-part-1.aspx在图 3 的首页里当鼠标移到右侧的 hyperlink 时实际连结的页面名称 (某个 .aspx) 并不会直接显示在浏览器的地址栏、浏览器下方的信息栏里因为 request「转向」的运作皆统一由 Controller 处理亦即由图 2 中 HomeController.cs 里配置的 System.Web.Mvc.ActionResult 类的 actionName 去指定因此浏览器的网址仍会维持 Site.Master (MasterPage) 里的 Html.ActionLink 绑定所配置的 actionName (即下方两行代码的第二个参数 Index、About)而不会如过去 ASP.NET 1.x / 2.0 般直接在地址栏里显示某一个 .aspx 的页面名称。以下为 Site.master 的一部分代码% Master LanguageC# InheritsSystem.Web.Mvc.ViewMasterPage %ul idmenuli% Html.ActionLink(Home, Index, Home)%/lili% Html.ActionLink(About, About, Home)%/li/ulController 除了用来控管整个网站的流程转向外事实上还能避免大量的代码重复亦即统一处理一些系统可「共享」的功能例如验证用户身份、输入验证 (validate)、Session 管理或是像购物网站中当购物车里物品的添加、修改、移除时任何用户皆相同的「共通逻辑」处理以及要结帐时去做购物数量、单价相乘的计算动作或将某个存储购物明细的 Collection 数据结构对象加入 Session最后再转向某一个页面 (View)。