1 概述
在阅读本篇博文时,建议结合上篇博文:详解ASP.NET MVC 路由 一起阅读,效果可能会更好些。
Controller(控制器)在ASP.NET MVC中负责控制所有客户端与服务端的交互,并且负责协调Model与View之间数据传递,是ASP.NET MVC框架核心。Controller为ASP.NET MVC框架的核心组成部分,其主要负责处理浏览器请求,并决定响应什么内容给浏览器,但并不负责决定内容应如何显示(View的职责)。
文章内容包括:Controller概述、Controller类别和方法、Controller运行过程、Controller方法类别、ViewData\ViewBag\TempData分析、ActionResult解说、Controller定义和参考文献,剩下有关Controller其他内容在本篇文章中不讲,如Controller激活机制(Controller类型解析、Controller类型缓存、Controller的释放和会话状态行为控制等)、ControllerFactory、ControllerBuilder等,除此之外,文中有些过于涉及到底层的内容,考虑篇幅等因素,只是简要提及了一下,并未做深入分析,根据后期情况,会酌情考虑是否再写一篇彻底深入的底层Controller的。
2 Controller类别和方法
Controller本身就是一个类(Class),该类别有许多方法(Method),这些方法中只要是公开方法(public method)就会被视为是一个动作(Action)或动作方法(Action Method),只要动作存在,就可以通过该动作方法接收客户端传来的要求与决定响应的检视(View)
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Web;
5 using System.Web.Mvc;
6
7 namespace MVCControllerDemo.Controllers
8 {
9 public class ControllerDemoController : Controller
10 {
11 //
12 // GET: /ControllerDemo/
13
14 [HttpGet]
15 public ActionResult Index()
16 {
17 return View();
18 }
19 }
20 }
从如上代码可以总结出Controller应具备如下几个基本条件:
(1)Controller必须为公开类别;
(2)Controller名称必须以Controller结尾;
(3)必须继承自ASP.NET MVC内建的Controller类别,或实现IController自定义类别;
(4)所以动作方法必须为公开方法,任何非公开的方法如声明为private或protected的方法都不会被视为一个动作方法;
3 Controller的运行过程
当Controller被MvcHandler选中之后,下一步就是通过ActionInvoker选定适当的Action来运行。在Controllr中的每个Action可以定义0到多个参数,ActionInvoker会依据当下的RouteValue与客户端传来的数据准备好可传入Action参数的数据,最后正式调用Controller中被选中的那个Action方法。参数传入的属性都是通过一种称为模型绑定(Model Binding)机制,从RequestContext取得数据,并将数据对应或传入方法的参数中,让Action不用再像之前ASP或ASP.NET Web Forms中经常使用的Request.Fomr或Request.QueryString等对象来取得客户端的数据,通过自定义的模型绑定,甚至可以让你对应除了Request.Form或Request.QueryString以外的数据来源,例如:HTTP Cookies、HTTP Headers等等。
Action运行完后的回传值通常是ActionResult类别或其衍生类别(Derived Class),事实上,ActionResult是一个抽象类,如ViewResult用来回传一个View、RedirectResult用来将网页重定向、Content回传文字内容、FileResult回传二进制文档等,这些均是继承ActionResult。MvcHandler从Controller得到ActionResult之后,就会开始运行ActionResult提供的ExecuteResult方法,并将运行结果响应到客户端,这时Controller的任务就算完成。
以上为Controller的基本运行过程。Controller在运行时还有一层所谓的动作过滤器机制,分为如下四种基本类型:
(1)授权过滤器(Authorization Filters);
(2)动作过滤器(Action Filters);
(3)结果过滤器(Result Filters);
(4)例外过滤器(Exception Fiters);
4 控制器方法类别
4.1 动作方法选定器
当通过ActionInvoker选定Controller内的公开方法时,ASP.NET MVC还有另一个特性称为"动作方法选定器(Action Method Selector)",该选定器可以套用在动作方法上,以便ActionInvoker"选定"适当的Action。
(1)NonAction属性
若控制器某个方法特性为NonAction,即使该Action方法是“公开方法”,也会告知ActionInvoker不要选定这个Action来运行。主要用途:a.保护Controller中的特定公开方法不要发布到Web上;b.功能尚未开发完成就要进行部署,暂时不想将此方法删除。
1 [NonAction]
2 public ActionResult Index()
3 {
4 return View();
5 }
也可将public改为private,达到保护的效果。
1 private ActionResult Index()
2 {
3 return View();
4 }
(2)HTTP动词限定属性
HttpGet、HttpPost、HttpDelete、HttpPut、HttpHead、HttpOptions、HttpPatch属性(Attributes)都是动作方法选定器的一部分。如下例子讲解HttpGet属性,即代表只有当客户端浏览器发送HTTP GET要求时,ActionInvoker才会选定到这个Action:
1 [HttpGet]
2 public ActionResult Index()
3 {
4 return View();
5 }
若将[HttpGet]改为[HttpPost],浏览器将找不到资源。
1 [HttPost]
2 public ActionResult Index()
3 {
4 return View();
5 }
注释:如果动作方法上没有嵌套任何限定属性,那么客户端浏览器发送任意HTTP动词都会自动选定到对应的Action。
当需要显示接收窗体信息时,可以创建两个同名的Action,分别用[HttpGet](显示窗体HTML)和[HttpPost](接收窗体输出的值)属性来限定。
1 [HttpGet]
2 public ActionResult Index()
3 {
4 return View();
5 }
6
7 [HttpGet]
8 public ActionResult Create()
9 {
10 return View();
11 }
12
13
14 [HttpPost]
15 public ActionResult Create(FormCollection fc)
16 {
17 //UpdateToDB(fc);
18 return RedirectToAction("Index");
19 }
4.2 操作过滤器
一个操作方法一旦被选中就会立即执行,并且如果它返回一个结果,返回的结果也会随后执行,ASP.NET MVC 5提供五种方式,分别列于如下:
除此之外,还有另外一种过滤器,即重写过滤器,它允许为全局或控制器的默认集合制定例外情况。
操作过滤器可以作为直接运用于操作方法或控制器类的特性来编写,或作为在全局过滤器列表中注册的单独类来编写。如果打算将编写的操作过滤器作为特性来使用,那么它必须继承自FilterAttribute或它的任何子类,如ActionFilterAttribute。不作为特性使用的全局操作过滤器没有对这个基类的要求。无论采用哪个路由,操作过滤器支持的过滤活动都由实现的接口决定。
5 Controller动作结果
5.1 控制器动作结果类型(ActionResult)
通常,在定义一个方法时,我们常规性地根据方法是否有返回值归结为有返回值和无返回值两大类,控制器的本质是类,控制器的action本质是方法,如果按照数学集合来定义,那么控制器是类的一个子集,同理,控制器action是方法的一个子集,因此,在研究控制器以及控制器action时,我们是可以才用研究类和方法的一般思维的。
控制器动作(具体的action)返回的结果叫做控制器动作结果,动作结果是控制器返回给浏览器请求的内容。ASP.NET MVC框架支持六种标准类型的动作结果。
(1)继承ActionResult的动作结果
(2)继承关系
(3)例子
eg1:ViewResult
eg2:EmptyResult
eg3:ContentResult
eg4:JsonResult
eg5:RedirectResult
eg6:RedirectToRouteResult
5.2一般方法
(1)如下只是给出方法样式,不做具体代码。
1 //无返回类型
2 public void functionName(形参)
3 {
4 //to add your content
5 }
6
7 //有返回类型
8 public 返回类型 functionName(形参)
9 {
10 //to add your content
11 return 与方法返回类型相匹配的结果;
12 }
(2)例子
eg:举个自定义返回string的方法
RouteConfig.cs
ControllerDemoController.action
测试结果
6 ViewBag、ViewData和TempData概述
6.1 三者在MVC框架里的定义
在MVC框架中,System.Web.Mvc命名空间下的ControllerBase中,对ViewBag、ViewData和TempData三个属性的定义如下
ViewBag
ViewData
TempData
6.2 三者比较
(1)ViewData和TempData属性均返回一个具有字典结构的数据容器,即字典类型的key/Value对,ViewBag为Dynamic类型。
三者方法签名为:
1 public TempDataDictionary TempData { get; set; }
2 public ViewDataDictionary ViewData { get; set; }
3 public object ViewBag { [return: Dynamic] get; }
(2)TempData存储临时数据,并且设置的变量在被第一次读取后会被移除,即TempData设置的变量只能被读取一次。(why?)
(3)ViewBag和ViewData属性是同一份数据的不同表现形式,二者的不同之处在于前者是一个动态对象,可以为其指定任意属性(动态属性名将作为数据字典的Key)。
(4)三者均是容器,即能存储常量,变量,也能存储集合。
7 ActionResult解说
在ASP.NET MVC框架中,对ActionResult定义如下:
1 // Generated by .NET Reflector from C:\Users\WJM\documents\visual studio 2013\Projects\DEMOMVC\packages\Microsoft.AspNet.Mvc.5.0.0\lib\net45\System.Web.Mvc.dll 2 namespace System.Web.Mvc 3 { 4 using System; 5 6 public abstract class ActionResult 7 { 8 protected ActionResult() 9 { 10 } 11 12 public abstract void ExecuteResult(ControllerContext context); 13 } 14 }
ActionResult是Action运行后的回传型别,但是当Action回传ActionResult的时候,其实并不包含这个ActionResult(例如ViewResult)的运行结果,而是包含运行这个ActionResult时所需的数据,当MvcHandler从Controller取得ActionResult之后才会去运行出ActionResult的结果。在ActionResult抽象类中仅仅定义了一个ExecuteResult()方法。
ASP.NET 定义了以下几种衍生型别。
8 控制器定义
一般地,在定义Controller时,采用两种方式,即实现IController和继承Controller。
8.1 实现IController
RouteConfig.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 using System.Web.Routing; 7 8 namespace MVCControllerDemo 9 { 10 public class RouteConfig 11 { 12 public static void RegisterRoutes(RouteCollection routes) 13 { 14 routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 15 16 routes.MapRoute( 17 name: "Default", 18 url: "{controller}/{action}/{id}", 19 defaults: new { Controller = "ControllerDemo", action = "Index", id = UrlParameter.Optional } 20 ); 21 } 22 } 23 }
RouteDemoController
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 7 using System.Reflection; 8 namespace MVCControllerDemo.Controllers 9 { 10 public class ControllerDemoController : IController 11 { 12 public String Index() 13 { 14 return "Index
"; 15 } 16 17 18 19 public void Execute(System.Web.Routing.RequestContext requestContext) 20 { 21 string action = requestContext.RouteData.Values["action"].ToString(); 22 Type typ = typeof(ControllerDemoController); 23 MethodInfo md = typ.GetMethod(action, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 24 if (md == null) 25 { 26 requestContext.HttpContext.Response.Write("404
"); 27 } 28 else 29 { 30 string s = md.Invoke(this, null).ToString(); 31 requestContext.HttpContext.Response.Write(s); 32 } 33 } 34 } 35 }
8.2 继承Controller
这种方法比较常用。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 7 namespace MVCControllerDemo.Controllers 8 { 9 public class ControllerDemoController : Controller 10 { 11 // 12 // GET: /ControllerDemo/ 13 14 [HttpGet] 15 public ActionResult Index() 16 { 17 return View(); 18 } 19 } 20 }