讨论一个问题最好的方式是从其定义开始,然后再深入讨论其细节。在阅读本章时,牢记控制器的定义,这将为理解控制器含义及其应用打下坚实基础。
MVC模式中的控制器(Controller)主要负责响应用户的输入,并且在响应时修改模型(Model)。通过这种方式,MVC模式中的控制器主要关注的是应用程序流、输入数据的处理,以及对相关视图(View)输出数据的提供。
过去的Web 服务器支持访问以静态文件存储在磁盘上的HTML页面。随着动态网页的盛行,Web服务器也支持由存储在服务器上的动态脚本生成的HTML页面。MVC则略有不同。URL首先告知路由机制(下面几章会有介绍,在第9章会进行详细介绍)去实例化哪个控制器,调用哪个操作方法,并为该方法提供需要的参数。然后控制器的方法决定使用哪个视图,并对该视图进行渲染。
URL并不与存储在Web服务器磁盘上的文件有直接对应关系,而是与控制器类的方法有关。ASP.NET MVC对MVC模式中的前端控制器进行了改进,正如后面第9章介绍的,路由子系统在前面,之后才是控制器。
理解MVC模式在Web场景中工作原理的简便方法就是记住:MVC提供的是方法调用结果,而不是动态生成的(又名脚本)页面。
控制器简史
MVC已经出现了很长一段时间——可以追溯到现代Web应用程序时代来临前的几十年。当MVC第一次开发出来的时候,图形用户界面(GUI)才刚刚起步,且在不断演化发展。当时,当用户按下一个按键或单击屏幕时,某个进程将会“监听到”他们的动作,这个进程就是控制器。控制器主要负责接收和解释输入,并更新任何需要的数据类(模型),然后通知用户进行的修改或程序更新(视图,第3章会详细介绍)。
20世纪70年代末和80年代初,Xerox PARC(刚好也是MVC模式诞生的地方)的研究员开始研究GUI的概念,在GUI中用户“工作”在一个虚拟的“桌面”环境中,在这种环境下,用户可以单击和来回拖曳条目。从这里产生了事件驱动编程的思想——根据用户触发的事件(如单击鼠标或是敲击键盘上的按键)来执行程序操作。
后来,随着GUI成为规范,MVC模式不完全适合这些新系统,这一点变得更加清晰。在此类系统中,由GUI组件负责处理用户输入,比如当按下一个按钮时,是该按钮本身响应鼠标单击,而不是控制器。按钮转而将依次通知所有单击的观察者或侦听者它被单击了。相对于MVC模式而言,另一些模式,如模型-视图-表示器(Model-View-Presenter,MVP)则表现的与这些现代系统更相关。
ASP.NET Web Forms是一个基于事件的系统,这在Web应用程序平台中是独一无二的。它拥有一个强大的基于控件和事件驱动的编程模型,从而为开发人员进行Web开发提供了一个良好的组件化GUI。当单击一个按钮时,Button控件将会做出响应,并在服务器端引发一个事件以告知它被单击。这种方法的妙处在于它可以让开发人员在更高的抽象级别下编写代码。
然而,进行更深入的分析会发现,开展的很多工作都是在模拟这种组件化的事件驱动。然而本质上,当单击一个按钮时,浏览器将向包含了页面上控件状态的服务器提交一个请求,控件所在的页面会被封装在一个编码的隐藏输入中。在服务器端,为了响应该请求,ASP.NET必须重建整个控件层次结构,然后解释请求,并利用请求的内容来恢复应用程序中用户的当前状态。究其本质,所有这些都是因为Web是无状态的。因此,当使用富客户端的Windows GUI应用程序时,没必要每当用户单击一个UI小部件时就重建整个屏幕和控件层次结构,因为应用程序保持了原状态,不曾改变。
对于Web程序而言,用户的应用程序状态实质上是消失的,只不过是后来用户每次单击后都会恢复。虽然这会极大地简化程序,但是以HTML形式出现的用户界面需要从服务器发送到客户端浏览器。这就引发一个问题:“应用程序在哪里?”,对于大多数Web 页面而言,应用程序就在客户端和服务器之间“舞蹈”,每次都维持一个小状态,可能是客户端的一个cookie或是服务器上的一块内存,一切都被小心地设计来掩盖一个小小的“谎言”,这个“谎言”就是Internet和HTTP可以进行有状态的编程。
当进行Web开发时,事件驱动编程方法(即“状态”概念)的支撑作用将不复存在,并且许多人不愿接受这个虚拟有状态平台的谎言。鉴于此,业界已经见证了MVC模式的复兴(尽管对其做了一点轻微的改动)。
下面给出一个改动的示例。在传统的MVC模式中,模型可以通过与视图的间接联系来“观察”视图,这就允许模型根据视图的事件来进行自我调整。对于在Web开发中应用MVC模式而言,当视图被发送到客户端浏览器时,模型通常已经不在内存当中,所以就不再能观察视图上的事件(注意,当第8章中讨论将Ajax运用到MVC中时,将看到这一改动的例外情况)。
在Web开发中采用MVC模式,控制器再次走在了前列。应用MVC模式要求Web应用程序中的每一个用户输入只采用请求的方式。例如,在ASP.NET MVC中,每个请求都被路由(路由使用将在第9章中介绍)到控制器的一个方法(又称操作),该控制器全权负责解释这些请求,如有必要,还要操纵模型,然后选择一个视图反馈给用户。
上面学习了一部分理论知识,接下来深入讲解ASP.NET MVC控制器的具体实现。我们将继续使用第1章创建的项目。如果跳过了第1章中新项目的创建,请参照上一章中的步骤,使用Internet Application模板和Razor视图引擎创建一个新的ASP.NET MVC 5应用程序,最终结果如图1-9所示。