转载

ASP.NET MVC 04 - 控制器

PS:

唉、本来这一篇前几天早就应该发了的,可是谁每月没有那么几天啊。。。

呵呵。开个玩笑。反正就是各种烦气,所以也就一直没上来继续发了。

年底了,摆正一下心态吧。好好干,整点钱,过年回家能跟亲朋好友好好装装逼啊是不。哈哈。

本篇目录:

1.控制器的角色

1.1控制器简史

2.控制器基础

2.1简单示例:HomeController

2.2创建第一个控制器

2.2.1创建新控制器

2.2.2编写操作方法

2.2.3经验总结

3.小结

▁▃▅ 控制器 ▅▃▁

本篇将阐述ASP.NET MVC中的控制器是如何响应用户的HTTP请求 并将处理的结果信息返回给浏览器,重点介绍和演示控制器和控制器操作的功能。

由于咱们还没涉及到视图和模型(也就是MVC中的V和M),所以本篇涉及到的视图和模型的内容算是超前的,大家瞅瞅就行了,后面会单独着重介绍。

正好也为后面学习打下一点点基础。

在前面几篇内容中,咱们概括性的介绍了MVC模式,然后对ASP.NET Web Forms和ASP.NET MVC进行了比较。

那么下面,也就是本篇内容,我们将深入学习MVC模式中的三个核心元素之一 —— 控制器。

1. 控制器的角色

讨论一个问题最好的方式是从其定义开始,然后在深入讨论其细节。

在本篇内容中,牢记控制器的定义,这将让你理解控制器的含义及其应用有非常大的帮助。

MVC模式中的控制器(Controller)主要负责响应用户的输入,并且在响应时修改模型(Model)。

通过这种方式,MVC模式中的控制器主要关注的就是 应用程序流输入参数的处理 ,以及 对相关视图(View)输出数据的提供 了。

过去的Web服务器支持访问以静态文件存储在磁盘上的HTML页面。随着动态网页的盛行,Web服务器也支持由存储在服务器上的动态脚本生成的HTML页面了。

MVC则略有不同,请求的URL地址首先告知路由机制(后面的内容将会详细介绍)去实例化哪个控制器、调用哪个操作方法、并为该方法提供需要的参数。然后控制器的方法决定使用那个视图,并对该视图进行渲染。

URL并不与存储在Web服务器磁盘上的文件有直接对应关系,而是与控制器类的方法有关。

ASP.NET MVC对MVC模式中的前端控制器进行了改造,正如咱们后面要学习的,路由系统在前面,之后才是控制器。

理解MVC模式在Web场景中工作原理的便捷方法就是记住、多练!

MVC提供的是方法调用的结果,而不是动态生成的页面。

1.1 控制器简史

对历史不感冒的可以跳过了,不过个人建议,哪怕读不懂,哪怕一目十行,瞅一遍总是有好处的。

MVC已经出现了很长一段时间 —— 可以追溯到现代Web应用程序时代来临前的几十年。

当MVC第一次开发出来的时候,图形用户界面(GUI)才刚刚起步,并且在不断演化发展。

当时,当用户按下一个按键或单击屏幕时,某个进程会“监听到”他们的动作,这个进程就是控制器。

控制器主要负责接收和解释输入,并更新任何需要的数据类(模型Model),然后通知用户进行的修改或程序更新界面显示(视图View)。

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必须重建整个控件层次结构,然后解释请求,并利用请求的内容来恢复应用程序中用户的当前状态。

究其本质,所有这些都是因为Webb是无状态的。 因此,当使用富客户端的Windows GUI应用程序时,没必要每当用户单击一个UI小部件时就重建整个屏幕和控件层级结构, 因为应用程序保持了原状态,不曾改变。

对于Web程序而言,用户的应用程序状态实质上是消失的。只不过是后来用户每次单击后都会恢复。

虽然这会极大地简化程序,但是以HTML形式出现的用户界面需要从服务器发送到客户端浏览器。这就引发一个问题:“应用程序在哪里?”。

对于大多数的Web页面而言,应用程序就在客户端和服务器之间交互。每次都维持一个小状态,可能是客户端的一个cookie或是服务器上的一块内存,一切都被小心的设计用来掩盖一个小小的“谎言”,这个“谎言”就是Internet和HTTP之间可以进行有状态的编程。不管怎么掩盖都是明显的扯犊子。

当进行Web开发时,事件驱动编程方法(即“状态”概念)的支撑作用将不复存在,并且许多人不愿接受这个虚拟有状态平台的“谎言”。鉴于此,业界已经见证了MVC模式的复兴(尽管对其做了一点轻微的改动)。

下面给出一个改动的示例。在传统的MVC模式中,模型可以通过与视图的间接联系来“观察”视图,这就允许模型根据视图的事件来进行自我调整。

对于在Web开发中应用MVC模式而言,当视图被发送到客户端浏览器时,模型通常已经不再内存当众,所以不再能观察视图上的事件(后面当我们讨论Ajax的时候,将会看到这个例外)。

在Web开发中采用MVC模式,控制器再次走在了前列。应用MVC模式要求Web应用程序中的每一个用户输入只采用请求的方式。

例如,在ASP.NET MVC中,每个请求都被路由转到控制器的一个方法,该控制器全权负责解释这些请求,如有必要,还要操纵模型数据,然后选择一个视图反馈给用户。

上面扩展了一部分理论知识,接下来深入讲解ASP.NET MVC控制器的具体实现。我们将继续使用前面已经创建好的项目。

如果您跳过了直接看本篇的,回头瞅瞅就行了。

2. 控制器基础

在MVC入门时会遇到像先有鸡还是先有蛋这样的问题,需要理解三个部分(模型、视图和控制器),但在不理解其他部分的情况下,要深入其中一部分是很难的。

因此,再开始学习MVC时,需要首先概括地了解控制器,暂时先不管模型和视图。

讲解了控制器的基本工作原理之后,然后咱们再深入学习视图、模型和其他ASP.NET MVC开发主题。最后的文章中,咱们还会再学习高级控制器。

2.1 简单示例:Home Controller

再开始实质性地编写代码之前,首先了解一下在一个新的项目中默认都包含那些内容。

用Internet Application模板创建的项目包含两个控制器类:

  • Home Controller:负责网站根目录下的“home page”、“about page”、“contact page”。
  • Account Controller:响应与账户相关的请求,比如登录和账户注册。

在Visual Studio的项目中,展开/Controller文件夹,打开HomeController.cs文件,如图1所示:

ASP.NET MVC 04 - 控制器

图1 - 控制器 HomeController.cs

注意这是一个相当简单的类,它继承了Controller基类。HomeController类的Index方法负责决定当浏览网站首页时触发的事件。

下面按照以下步骤对程序进行简单的修改,然后运行程序。

(1)用自己想要的短语替换About方法中的“Your application description page.”,比如“Boom、ShaKaLaKa!”。

1 using System;  2 using System.Collections.Generic;  3 using System.Linq;  4 using System.Web;  5 using System.Web.Mvc;  6   7 namespace BookStore.Controllers  8 {  9     public class HomeController : Controller 10     { 11         public ActionResult Index() 12         { 13             return View(); 14         } 15  16         public ActionResult About() 17         { 18             ViewBag.Message = "Boom、ShaKaLaKa!"; 19  20             return View(); 21         } 22  23         public ActionResult Contact() 24         { 25             ViewBag.Message = "Your contact page."; 26  27             return View(); 28         } 29     } 30 }  修改About方法后的HomeController.cs

(2)按下F5键或者使用“调试 - 启动调试”菜单项运行应用程序,Visual Studio编译应用程序并启动运行在IIS Express下的站点。

IIS Express和ASP.NET开发服务器

Visual Studio 2013包括IIS Express,这是IIS的本地开发版本,可以用来在一个随机的空闲端口上运行网站。

如图2,网站在“http://localhost:5831/”上运行,因此它采用的端口号是5831,你运行时的端口号可能与这个不同。

Visual Studio 2010及其以下版本使用的是Visual Studio Development Swerver(有时也称它的老代号Cassini),而不是IIS Express。

尽管Development Server很像IIS,但IIS Express实际上是IIS的优化版本,优化后使它更适用于开发。

想更多地了解IIS Express,请自行Google。

(3)接下来,会打开一个浏览器窗口,显示网站的首页,如图2所示:

ASP.NET MVC 04 - 控制器

图2 - 初次启动后的网站首页

(4)点击导航菜单的“关于”,浏览到/Home/About,打开关于页面,你修改的消息就显示出来了,如图3:

ASP.NET MVC 04 - 控制器

图3 - 显示修改后的信息“Boom、ShaKaLaKa!”

现在已经创建了一个新项目并在屏幕上显示了指定的信息,接下来通过创建一个新的控制器来创建一个实际的应用程序。

2.2 创建第一个控制器

首先创建一个控制器来处理有关浏览图书目录的URL。这个控制器支持一下三个功能:

    1. 索引页面列出商店里包含的图书类型;
    2. 单击一个类型,跳转到一个列出该类型下所有图书的页面;
    3. 单击一个图书,跳转到一个列出有关该图书的所有信息的页面。

2.2.1 创建新控制器

创建控制器,首先需要添加一个新的StoreController类。具体方法如下:

(1)右击“解决方案资源管理器”下项目里面的“Controller”文件夹,右键“添加 - 控制器”,如图4所示;

(2)模板类型选择“MVC5 控制器 - 空”,如图5所示;

(3)强控制器命名为“StoreController”,然后单击添加按钮,如图6所示。

ASP.NET MVC 04 - 控制器

图4 - 右键项目Controller文件夹 - 新建控制器

ASP.NET MVC 04 - 控制器

图5 - 选择控制器类型

ASP.NET MVC 04 - 控制器

图6 - 输入控制器名称

2.2.2 编写操作方法

新创建的StoreController控制器已经有了一个Index()方法,下面将利用这个Index()方法实现在页面上列出图书商店里面所有图书类别的功能。

另外,还需要添加两个额外的方法来实现上面说到的其他两项功能,分别是Category()和Details()。

控制器中的这些方法(Index()、Category()、Details())成为控制器操作。

正如上述的HomeController.Index()操作方法那样,控制器操作的是响应URL请求,执行相应的操作,并向浏览器或是单击这个URL的用户做出响应。

要了解控制器操作的工作原理,可按照以下的步骤操作:

(1)将Index()方法的返回数据类型改为  string (而不是  ActionResult );

然后将返回值 return View(); 改为  return "请求来自:Store.Index() ! " ;

修改后的StoreController.cs如下所示:

1 using System;  2 using System.Collections.Generic;  3 using System.Linq;  4 using System.Web;  5 using System.Web.Mvc;  6   7 namespace BookStore.Controllers  8 {  9     public class StoreController : Controller 10     { 11         public string Index() 12         { 13             return "请求来自:Store.Index() !"; 14         } 15     } 16 }

(2)添加对商店的Category()方法,将返回设置为  return " 请求来自:Store.Category() ! " ;

添加浏览详细信息的Details()方法,将返回设置为 return " 请求来自:Store.Details() ! " ;

控制器StoreController的完整代码如下所示:

1 using System;  2 using System.Collections.Generic;  3 using System.Linq;  4 using System.Web;  5 using System.Web.Mvc;  6   7 namespace BookStore.Controllers  8 {  9     public class StoreController : Controller 10     { 11         public string Index() 12         { 13             return "请求来自:Store.Index() !"; 14         } 15  16         public string Category() 17         { 18             return "请求来自:Store.Category() !"; 19         } 20  21         public string Details() 22         { 23             return "请求来自:Store.Details() !"; 24         } 25     } 26 }

(3)重新运行项目,然后浏览一下URL:

    • /Store
    • /Store/Category
    • /Store/Details

访问这些URL会调用控制器中的操作方法,然后返回相应字符串,如图7、8、9所示:

ASP.NET MVC 04 - 控制器

图7 - 浏览/Store

ASP.NET MVC 04 - 控制器

图8 - 浏览/Store/Category

ASP.NET MVC 04 - 控制器

图9 - 浏览/Store/Details

2.2.3 经验总结

从以上这个姜丹实验中可以得出以下几个结论:

  • 不需要做任何额外的配置,浏览到/Store/Details就可以执行StoreController类中的Details()操作方法,这就是操作中的路由为我们提供的。
    稍后还会简单介绍路由,详细的介绍我们后面单独再谈。
  • 尽管是使用Visual Studio工具来创建这个控制器类,但它的确是一个非常简单的类。
    判别一个类是否是控制器类的唯一方式,就是查看该类是否继承自System.Web.Mvc.Controller。
  • 已经利用一个控制器在浏览器中显示了文本 —— 没有用到模型和视图。
    尽管在ASP.NET MVC中模型和视图非常有用,但控制器才是真正的核心。每一个请求都必须通过控制器处理,然而其中有些请求是不需要模型和视图的。

2.3 控制器操作中的参数

前面的例子返回输出的是常量字符串。 下一步就是让它们通过响应URL传进来的参数动态地执行操作。按以下步骤来实现:

(1)把Category()操作方法修改为,查找URL传过来的查询字符串值。

可以通过在操作方法中添加一个string类型的“categoryName”参数来实现这个功能。

然后,当这个方法被调用时,ASP.NET MVC会自动将名为“categoryName”的查询字符串或表单提交参数传递给Category()操作方法。

1 public string Category(string categoryName) 2 { 3     string message = HttpUtility.HtmlEncode("Store.Category(),CategoryName=" + categoryName); 4     return message; 5 }

HTML编码的用户输入

利用方法HttpUtility.HtmlEncode()来预处理用户输入。这样就能阻止用户用链接向视图中注入Javascript代码或HTML标记,也就可以防止XSS注入了。

例如用户访问: /Store/Category?categoryName=<script>window.location= ' http://www.geeksss.com ' </script> ,甚至插入其他恶意Javascript等等。

(2)上面的操作完事儿了之后,我们浏览  /Store/Category?categoryName=Java ,结果如图10所示:

ASP.NET MVC 04 - 控制器

图10 - 浏览Category()操作方法,并传递参数categoryName=Java

这表明控制器操作可以将查询字符串作为其操作方法的参数来接收。

(3)修改Details()操作方法,使其读取和显示一个名为ID的输入参数。

这里不像前面的方法那样把ID值作为一个查询字符串参数,而是将ID直接嵌入到URL中,如: /Store/Details/ 520

ASP.NET MVC再不需要任何额外配置的情况下可以很容易地做到这一点。

ASP.NET MVC的默认路由约定,就是将操作方法名称后面URL的这个片段( 例如上面的 /Store/Details/ 520 中的520)作为一个参数,该参数的名称为ID。

如果操作方法中又名为ID的参数,那么ASP.NET MVC会自动将这个URL片段作为ID参数传递过来。

1 public string Details(int id) 2 { 3     string message = "Store.Details(),ID=" + id; 4     return message; 5 }

(4)运行应用程序,浏览到/Store/Details/520,如图11所示:

ASP.NET MVC 04 - 控制器

图11 - 为Details()操作方法添加id参数并访问

像前面示例演示的那样,控制器操作感觉就像是Web浏览器直接调用控制器类中对应的操作方法。

类、操作方法和参数都被具体化为URL中的特定路径片段或查询字符串,结果就是一个返回给浏览器的字符串。

这就是进行了极大的简化,而忽略了下面这些的细节:

  • 路由将URL映射到操作方法的具体方式;
  • 将试图作为模板生成返回给浏览器的字符串(通常是HTML格式);
  • 操作很少返回原始的字符串,它通常返回合适的ActionResult类处理像HTTP状态码和调用视图模板系统这样的事项。

控制器提供了很多自定义和扩展的功能,但是我们很少能用到这些内容。

在一般应用程序中,控制器通过URL被调用,然后执行自定义的代码并返回一个视图。先记住这些内容,后面我们会详细介绍关于控制器如何定义、调用和扩展的底层细节。

3. 小结

现在已经学习了足够的控制器知识,下面就可以与视图结合起来使用了,下一篇我们将对此进行详细的介绍。

正文到此结束
Loading...