1 概述 1 概述 在閱讀本篇博文時,建議結合上篇博文:詳解ASP.NET MVC 路由 一起閱讀,效果可能會更好些。 Controller(控制器)在ASP.NET MVC中負責控制所有客戶端與服務端的交互,並且負責協調Model與View之間數據傳遞,是ASP.NET MVC框架核心。Contr ...
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

1 //方法1: ViewResult作為返回類型
2 public ViewResult Index()
3 {
4 return View();
5 }
6
7
8
9 //方法二: ViewResultBase作為返回類型
10 public ViewResultBase Index()
11 {
12 return View();
13 }
14
15 //方法三: ActionResult作為返回類型
16 public ActionResult Index()
17 {
18 return View();
19 }
View Code
eg2:EmptyResult

1 // GET: /ControllerDemo/
2 public EmptyResult Index()
3 {
4 return null;
5 }
View Code
eg3:ContentResult

1 //方法1:ContentResult作為返回類型
2 public ContentResult Index()
3 {
4 return Content("Hello World");
5 }
6
7
8 //方法2:ActionResult作為返回類型
9 public ActionResult Index()
10 {
11 return Content("Hello World");
12 }
View Code
eg4:JsonResult

1 public JsonResult jsonResult()
2 2 {
3 3 TechInfoCompanay jsonCompany=new TechInfoCompanay(){id="S001",CompanyName="信息科技有限公司"};
4 5 return Json(jsonCompany,JsonRequestBehavior.AllowGet);
5 6 }
6 7
7 8
8 9 //定義一個公司類
9 10 public class TechInfoCompanay
10 11 {
11 12 public string id { set; get; }
12 13 public string CompanyName { set; get; }
13 14 }
View Code
eg5:RedirectResult

1 //方法1:RedirectResult作返回類型
2 public RedirectResult redirectResult()
3 {
4 return Redirect("https://www.google.com.hk/");//具體的URL
5 }
6
7 //方法1: ActionResult作返回類型
8 public ActionResult redirectResult()
9 {
10 return Redirect("https://www.google.com.hk/");//具體的URL
11 }
View Code
eg6:RedirectToRouteResult

1 public ActionResult redirectResult()
2 {
3 return Redirect("https://www.google.com.hk/");//具體的URL
4 }
5
6 public RedirectToRouteResult redirectToRouteResult()
7 {
8 return RedirectToAction("Index");
9 }
View Code
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

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 }
View Code
ControllerDemoController.action

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 public string GeneralFunction()
13 {
14 return "自定義一般方法";
15 }
16 }
17 }
View Code
測試結果
6 ViewBag、ViewData和TempData概述
6.1 三者在MVC框架里的定義
在MVC框架中,System.Web.Mvc命名空間下的ControllerBase中,對ViewBag、ViewData和TempData三個屬性的定義如下
ViewBag

1 [Dynamic]
2 public object ViewBag
3 {
4 [return: Dynamic]
5 get
6 {
7 Func<ViewDataDictionary> viewDataThunk = null;
8 if (this._dynamicViewDataDictionary == null)
9 {
10 if (viewDataThunk == null)
11 {
12 viewDataThunk = () => this.ViewData;
13 }
14 this._dynamicViewDataDictionary = new DynamicViewDataDictionary(viewDataThunk);
15 }
16 return this._dynamicViewDataDictionary;
17 }
18 }
View Code
ViewData

1 public ViewDataDictionary ViewData
2 {
3 get
4 {
5 if (this._viewDataDictionary == null)
6 {
7 this._viewDataDictionary = new ViewDataDictionary();
8 }
9 return this._viewDataDictionary;
10 }
11 set
12 {
13 this._viewDataDictionary = value;
14 }
15 }
View Code
TempData

1 public TempDataDictionary TempData
2 {
3 get
4 {
5 if ((this.ControllerContext != null) && this.ControllerContext.IsChildAction)
6 {
7 return this.ControllerContext.ParentActionViewContext.TempData;
8 }
9 if (this._tempDataDictionary == null)
10 {
11 this._tempDataDictionary = new TempDataDictionary();
12 }
13 return this._tempDataDictionary;
14 }
15 set
16 {
17 this._tempDataDictionary = value;
18 }
19 }
View Code
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 "<h1>Index</h1>"; 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("<h1>404</h1>"); 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();