Controller作為持久層和展現層的橋樑, 封裝了應用程式的邏輯,是MVC中的核心元件之一。
本篇文章我們就來談談 Controller, 主要討論兩個方面:
- Controller執行機制簡介
- Controller資料傳遞方式
Controller執行機制簡介
實現自定義的Controller
我們自己要實現一個控制器有兩種方法:
一種是繼承IController介面,一種是繼承Controller或ControllerBase.
Controller繼承了ControllerBase, 另外Controller和ControllerBase本身也繼承了IController,總之需要實現IController介面.
首先我們到XEngine中隨便開啟一個Controller, 例如AccountController,
可以看到新建Controller時,腳手架幫我們繼承了Controller類
我們逐級檢視,如下幾張圖,右鍵 轉到Controller定義--> 檢視ControllerBase定義 --> 檢視IController定義,可以看到Controller需要實現IController中Execute方法。
接下來,我們就新建一個類MyCustomController,繼承IController介面,實現Execute方法。
namespace XEngine.Web.Controllers
{
public class MyCustomController:IController
{
public void Execute(RequestContext requestContext)
{
requestContext.HttpContext.Response.Write("Hello world.");
}
}
}
執行http://localhost/XEngine/mycustom,可以看到瀏覽器輸出了Hello world.
MVC框架將實現IController介面的類當作一個控制器,根據路由規則將請求傳送給它。
上例中,我們使用到了RequestContext的HttpContext屬性,該屬性用來獲取有關HTTP請求的資訊。
RequestContext另外還有一個屬性RouteData,用來獲取請求路由的資訊,例如可以通過如下方式可以獲得controller和action的名稱:
requestContext.RouteData.Values["controller"].ToString();
requestContext.RouteData.Values["action"].ToString();
實現IController介面的控制器需要負責處理請求的各個方面,包括生成對客戶端的響應。
實際應用中我們像腳手架一樣直接繼承System.Web.Mvc.Controller就可以了,這種方式我們就不需要自己實現Execute方法來輸出內容了,可以通過MVC Framework的action results來解決這個問題。
我們先舉個例子,看看我們原來一直使用的ActionReslut生成響應的方式,例如
public ActionResult NativeOutput()
{
return Redirect("~/Account/Login");
}
Action 方法不直接使用Response物件,而是返回ActionResult型別的物件。ActionResult類描述了response的型別,比如返回一個view或跳轉到另外一個頁面。
當MVC Framework從一個action方法接收一個ActionResult物件時,會呼叫那個物件的ExecuteResult方法。
namespace System.Web.Mvc
{
// 摘要:
// 表示操作方法的結果。
public abstract class ActionResult
{
// 摘要:
// 初始化 System.Web.Mvc.ActionResult 類的新例項。
protected ActionResult();
// 摘要:
// 通過從 System.Web.Mvc.ActionResult 類繼承的自定義型別,啟用對操作方法結果的處理。
//
// 引數:
// context:
// 用於執行結果的上下文。上下文資訊包括控制器、HTTP 內容、請求上下文和路由資料。
public abstract void ExecuteResult(ControllerContext context);
}
}
我們通過一個自定義的ActionResult實現來演示工作機制, 模擬實現這個簡單的跳轉功能, ExecuteResult實現如下:
namespace XEngine.Web.Utility
{
public class CustomRedirectResult:ActionResult
{
public string Url { get; set; }
public override void ExecuteResult(ControllerContext context)
{
string fullUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext);
context.HttpContext.Response.Redirect(fullUrl);
}
}
}
在Controller中使用
public CustomRedirectResult CustomOutput()
{
return new CustomRedirectResult { Url = "~/Account/Login" };
}
可以看到,實現了同樣的效果。
內建的action result 型別
類似於我們實現的CustomRedirectResult,MVC框架包含一些內建的action result型別,所有這些型別都繼承於ActionReslut型別。如下列表:
https://msdn.microsoft.com/en-us/library/system.web.mvc.actionresult.aspx
我們具體使用時可以明確指明返回型別,如
public ViewResult xxx()
{ xxx }
或統一返回 ActionResult, 如
public ActionResult xxx()
{ xxx }
一般我都是籠統的返回 ActionResult,這樣比較方便。(另外具體實現時,一個Action也可能根據不同情況返回不同種類的ActionResult,沒辦法明確返回型別)
上面這張表格, HttpStatusCodeResult、 HttpUnauthorizedResult、 EmptyResult這三個Action Result是沒有Helper Method的.
類似於我們自定義的CustomRedirectResult,使用時需要使用字面量來明確返回結果。下面看例子:
可以使用HttpStatusCodeResult 類將一個特定的HTTP狀態碼傳送給瀏覽器。下面看下HttpStatusCodeResult的例子,返回特定的HTTP結果碼:
這個類沒有具體的控制器輔助方法,因此必須對這個類進行例項化。
public HttpStatusCodeResult StatusCode()
{
return new HttpStatusCodeResult(404, "URL cannot beserviced");
}
401和404是HttpStatusCodeResult的兩個特例:
可以使用HttpNotFoundResult類取得上面的404效果
public HttpStatusCodeResult NotFoundStatusCode()
{
return HttpNotFound();
}
傳送401結果,通常是把使用者重定向到認證頁面
public HttpStatusCodeResult UnauthorizedStatusCode()
{
return new HttpUnauthorizedResult();
}
可以看到,執行後跳轉到認證頁面
資料的傳遞方式
我們討論下一話題:MVC中常用的傳遞資料方式。(傳統的Session, Cookie傳遞方式還可以繼續用,就不再做介紹了)
我們使用到的資料傳遞主要有 view到controller, controller到view, 跨view間的資料傳遞三種。下面我們分別加以說明。
一、 Controller接收View資料
Controller 經常需要訪問來自輸入請求的資料,如查詢字串值、表單值,以及路由系統根據輸入URL解析所得到的引數。訪問這些資料有兩個主要途徑:
1、通過context(和ASP.NET 之前版本的技術類似,如我們熟悉的Request)
2、通過action方法的引數(包括模型繫結),(MVC框架自動檢查上下文給這些引數賦值)
這兩個方式都很常用,我們來依次講解。
通過 Context獲取資料
當我們通過繼承ControllerBase類建立controller時, 我們可以利用context物件的一組屬性來獲取請求的相關資訊, 如Request, Response, RouteData, HttpContext和Server.
常用的歸納如下表:
這些使用方法有些之前的文章已經介紹過,其他的在用到時再介紹,就不重複說明了。
通過action方法的引數(利用模型繫結)
通過引數的方法可讀性更好。
如下的重寫例子,我們先用context讀取表單值,再改寫成引數方式讀取。
先定義一個View
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>GetDataFromView</title>
</head>
<body>
<h1>@ViewBag.Name</h1>
<form method="post" >
<input name="name" value="Tony"/>
<input type="submit" value="提交表單" />
</form>
</body>
</html>
通過context讀取
public ActionResult GetDataFromView()
{
ViewBag.Name = Request.Form["name"];
return View();
}
改成通過引數讀取
public ActionResult GetDataFromView(string name)
{
ViewBag.Name = name;
return View();
}
點選按鈕後,均返回如下頁面:
通過自動檢查上下文物件和屬性,MVC框架會給action method 引數提供值,這些物件包括Request.QueryString, Request.Form 和 RouteData.Values
模型繫結是MVC推薦的方式,個人感覺可以使程式碼更加乾淨。原理是通過Value Provider Model Binder 兩個元件。
有一組內建的 Value Provider,它們會抓取Request.Form, Request.QueryString, Request.Files以及 RouteData.Values的資料項,然後將這些值傳遞給Model Binder,嘗試將這些資料對映為action method引數的資料型別。
當然引數也可以是一個model, 這種方式前面文章已經使用多次,不再重複舉例。
二、Controller傳遞資料到View
直接將物件作為View的引數即可(即傳遞一個view model object)。
public ActionResult DateOutput()
{
DateTime date = DateTime.Now;
return View(date);
}
在View中使用Model關鍵字來訪問
@{
ViewBag.Title = "Index";
}
<h2> DateOutput </h2>
The day is: @(((DateTime)Model).DayOfWeek)
這種檢視是無型別檢視。該檢視不知道關於檢視模型的任何情況,而把它作為object的一個例項來看待。
可以通過建立強型別檢視來明確model型別,在強型別檢視中包含檢視模型物件型別的詳細資訊。
@model DateTime
@{
ViewBag.Title = "Index";
}
<h2>DateOutput</h2>
The day is: @Model.DayOfWeek
注意:指定模型型別是需要小寫的 m, 讀取時用大寫的 M
使用ViewBag
之前也用過多次,不再贅述。
個人認為ViewBag最大的一個優點是它便於將多個物件傳送給檢視。
三、跨請求傳遞
前面兩種請求方式都在一輪請求應答中。
還有一種跨請求的情況,例如重定向導致瀏覽器遞交新的HTTP請求。
如果需要將一個請求的資料傳遞到下一個請求,這種情況可以使用TempData.
使用時直接按Session一樣的語法就可以了。
TempData和Session的區別是,當讀取TempData值時,值就會被標記為待刪除,
當請求結束後就會被刪除。
有兩個小技巧:
1、利用Peek方法,可以得到TempData的值,而不把它標記為刪除
DateTime time = (DateTime)TempData.Peek("Date");
2、利用Keep方法,可以保留一個將被刪除的值
TempData.Keep("Date");
Keep方法不會永久保護一個值。如果這個值被再次讀取,它將被再次標記為刪除。
總結
關於Controller需要了解常用的ActionResult型別,掌握資料的傳遞的幾種方式。
歡迎大家多多評論,祝 學習進步:)