MVC筆記 Controller相關技術


一、Controller的責任

    MVC的核心就是Controller(控制器),它負責處理瀏覽器傳送過來的所有請求,並決定要將什么內容響應給瀏覽器。但Controller並不負責決定內容應該如何顯示,而是將特定形態的內容響應給MVC架構,最后才由MVC架構依據響應的形態來決定如何將內容響應給瀏覽器。如何決定響應內容是View的責任。

二、Controller的類與方法

    Controller本身就是一個類(Class),該類有許多方法(Method)。在這些方法中,只要是公開方法,該方法就會被視為是一種動作(Action);只要有動作存在,就可以通過該動作方法接收網頁請求並決定響應視圖。

    由上可知編寫Controller的基本要求:

  • Controller必須為公開類。
  • Controller的名稱必須以"Controller"結尾。
  • 必須繼承自MVC內置的Controller類,或繼承自實現IController接口的自定義類,或自行實現IController接口。
  • 所有方法必須為公開方法。改方法可以沒有參數,也可以有多個參數。

三、Controller的執行過程

    Controller被MvcHandler選中之后,下一步就是通過ActionInvoker選取適當的Action來執行。在Controller中,每一個Action可以定義0到多個參數。ActionInvoke會依據當前的RouteValue及客戶端傳過來的信息准備好可輸入Action參數的依據,最后正式調用被Controller選中的那個Action方法。

     Action執行完后的返回值通常是ActionResult類的。事實上,ActionResult類是一個抽象類,因此,MVC本身就實現了許多不同ActionResult類的子類。Controller得到ActionResult類之后,就會開始執行ActionResult類的ExecuteResult()方法,並將執行的結果返回客戶端。這時,Controller的任務就算完成了。

      Controller在執行時還有一個動作過濾器(Action Filter)機制,可以分成以下4中類型。

  • 授權過濾器(Authorization Filter);
  • 動作過濾器(Action Filter);
  • 結果過濾器(Result Filter);
  • 例外過濾器(Exception Filter)。

    此外,在Controller的執行過程中還必須考慮動作過濾器的執行順序。除上述說明之外,在執行Action與ActionResult類時嗨會有一些事件被執行,這部分將在九中說明。

四、動作名稱選取器

    通過ActionInvoker選取Controller中的公開方法時,默認會用Reflection(映像)的方式取得Controller中具有相同名字的方法(不區分大小寫)。如下程序范例表示得很清楚:當RouteValue表達式中的Action是"Index",默認會執行Index()方法。

 1   public class HomeController : Controller
 2     {
 3         /// <summary>
 4         /// 要求網址 http://localhost/Home/Index
 5         /// </summary>
 6         public ActionResult Index()
 7         {           
 8             return View();
 9         }
10 
11     }

    如果在以上的Action中加入ActionName屬性,並將其指名為"Default",此時,若RouteValue表達式中的Action是"Index",就不會執行Index()方法,而必須使RouteValue表達式中的Action為"Default",Index()方法才能被正確執行,這就是動作名稱選取器(Action Name Selector)的作用,示例如下。

 1   public class HomeController : Controller
 2     {
 3         /// <summary>
 4         /// 要求網址 http://localhost/Home/Index
 5         /// </summary>
 6         [ActionName("Default")]
 7         public ActionResult Index()
 8         {           
 9             return View();
10         }
11 
12     }

    唯一需要特別注意的是,如果你使用默認的"return View()"方法返回ActionResult類,由於應用了[ActionName("Default")]屬性,所以MVC會去尋找"/Views/Home/Default.aspx"頁面而不是"/Views/Home/Index.aspx"頁面來執行。

五、動作方法選取器

   5.1  NonAction屬性

    若將NonAction屬性應用在Controller中的Action方法上,即便該Action方法是公開方法,也會告知ActionInvoke不要選取這個Action來執行。這個屬性主要用來保護Controller中的特定公開方法不會被發布到Web上。或是當功能尚未開發完成就要進行部署時,若暫時不想將此方法刪除,也可應用這個屬性,表示"不要對外公開"。

1        [NonAction]
2         public ActionResult Index()
3         {
4             return View();
5         }

    將Action方法中的"public"修改成"privare",也可以達到同樣的目的,示例如下:

1        private ActionResult Index()
2         {
3             return View();
4         }

   5.2  HttpGet屬性、HttpPost屬性、HttpDelete屬性和HttpPut屬性

    HttpGet、HttpPost、HttpDelete和HttpPut屬性是動作方法選取器的一部分,我們以下列程序為例進行介紹。若應用了[httpPost]屬性,表示只有當客戶端瀏覽器發送HTTP POST請求時才可以選取這個Action。

1       [HttpPost]
2         private ActionResult Index()
3         {
4             return View();
5         }

    相反的,若果沒有應用這些屬性,客戶端瀏覽器發送任何HTTP動詞,都會自動選取對應的Action。

    這些屬性常用在需要接受窗口數據的時候。你可以創建兩個同名的Action,一個應用[HttpGet]屬性來顯示窗口HTML,另一個應用[HttpPost]屬性來接收窗口送出的值,范例程序如下。

 1         [HttpGet]
 2         public ActionResult Create()
 3         {
 4             return View();
 5         }
 6         [HttpPost]
 7         private ActionResult Create(FormCollection c)
 8         {
 9             UpdateToDB(c);
10             return RedirectToAction("Index");
11         }

  NOTE   由於HTML窗口無法送出"Delete"這個Http動詞,所以如果希望Action能提供像RESET協議那樣的方式來處理刪除動作,又能通過同一個窗口使用這個只允許"Delete"的動作的話,可以用Html.HttpMethodOverride()方法的HTML輔助方法來模擬Http Delete方法的行為,但實際上窗口還是以Http Post的方式送出去的。

六、 ActionResult類

    ActionResult類是Action執行的結果,但ActionResult中並不包含執行結果,而是包含執行響應時所需的信息。當Action返回ActionResult類之后,會由MVC執行。先看看ActionResult抽象類的程序代碼。在ActionResult抽象類中僅定義了一個ExecuteResult()方法來執行結果。

 1  namespace System.Web.Mvc
 2   {
 3     // 摘要:
 4     //     封裝一個操作方法的結果並用於代表該操作方法執行框架級操作。
 5     public abstract class ActionResult
 6     {
 7         // 摘要:
 8         //     初始化 System.Web.Mvc.ActionResult 類的新實例。
 9         protected ActionResult();
10 
11         // 摘要:
12         //     通過從 System.Web.Mvc.ActionResult 類繼承的自定義類型,啟用對操作方法結果的處理。
13         //
14         // 參數:
15         //   context:
16         //     用於執行結果的上下文。上下文信息包括控制器、HTTP 內容、請求上下文和路由數據。
17         public abstract void ExecuteResult(ControllerContext context);
18     }
19   }

    MVC定義的ActionResult如表所示:

Contro輔助方法

用 途

ContentResult

Content

返回一段用戶自定義的文字內容

EmptyResult

 

不返回任何數據,即不響應任何數據

JsonResult

Json

將數據序列化成JSON格式返回

RedirectResult

Redirect

重定向到指向的URL

RedirectToRouteResult

RedirectToAction、RedirectToRoute

與RedirectResult類似,但它將新定向到一個Action或Route

ViewResult

View

使用IViewInstance接口和IViewEngine接口,實際輸出數據的是IViewEngine接口和View

PartialViewResult

PartialView

與ViewResult類相似,返回的是”部分顯示”,即”UserControls”目錄下的View

FileResult

File

以二進制串流的方式返回一個文件數據

JavaScriptResult

JavaScript

返回的是JavaScript指令碼

    表中的Controller輔助方法在Controller類中為返回ActionResult類提供支持,如下程序可用於跳轉到另一個頁面。

1      [HttpPost]
2         public ActionResult Post(FormCollection c)
3         {
4             return new RedirectResult("/");
5         }

    如果使用Controller輔助方法,就可以將以上程序改寫如下:

1      [HttpPost]
2         public ActionResult Post(FormCollection c)
3         {
4             return Redirect("/");
5         }

    以上兩段程序代碼其實差不多,但實際操作中則是以使用Controller輔助方法居多。

  6.1  ViewResult類

    ViewResult類是在MVC中最常用的ActionResult類,用於返回一個標准的視圖。通過Controller輔助方法,我們能更方便地定義如何輸出視圖。指定要輸出的View名稱、指定該View要應用哪個MasterPage、指定要輸入的View的Model。

  6.2  PartialViewResult類

    PartialViewResult類與ViewResult類非常相似,但它無法為View賦值MasterPage,通常用在前端為Ajax應用程序的情況下,並可以通過Ajax來取得網頁中的部分內容。

    如下程序會執行"/Views/Home/About.ascx"頁面,並將結果輸出至客戶端。

1        public ActionResult About()
2         {
3             return PartialView();
4         }

  6.3  EmptyResult類

    有些Action在執行后其實不需要返回任何數據,例如一個頁面執行完后直接轉到其他頁面的情況。EmptyResult類不會執行任何響應客戶端的程序,所以也不會返回任何數據,使用方法如下:

1       public ActionResult Empty()
2         {
3             return new EmptyResult();
4         }

    在MVC中,還有一種表達EmptyResult類的方式,即將上述語法寫成如下:

1        public void Empty()
2         {
3             return;
4         }

    有一種情況是用EmptyResult類搭配Response.Redirect()方法進行HTTP 302暫時轉向,示例如下:

1       public void Redirect()
2         {
3             Response.Redirect("/Home/Index");
4         }

    如果已經開始使用.NET 4.0,也可以考慮使用4.0新增的Respo.RedirectPermanent()方法建立HTTP 301永久轉向,示例如下。

1       public void Redirect()
2         {
3             Response.RedirectPermanent("/Home/Index");
4         }

  6.4  ContentResult類

    ContentResult類可以響應文字內容的結果。可以讓ContentResult類響應任意指定文字內容。Content-Type和文字編碼(Encoding)。

    如下范例將會響應一段XML文字,並設定響應的Content-Type為text/xml(已指定響應的文字編碼方式為Encoding.UTF8)。

1       public ActionResult Content()
2         {
3             return Content("<ROOT><TEXT>123</TEXT></ROOT>","text/xml",Encoding.UTF8);
4         }

    如果只響應一串HTML字符串,可以只使用第一個參數,如下:

1       public ActionResult Content()
2         {
3             string strHTML = "........";   //省略 HTML的內容
4             return Content(strHTML);
5         }

    還有一種方法可以表達如上一樣簡單的返回類,直接將返回類設定成"string"即可。MVC會進行判斷,只要Action返回的不是ActionResult類,就會將返回的類轉換成字符串類輸出。

  6.5  FileResult類

   FileResult類可以響應任意的文件內容,包括二進制格式的數據,例如圖像文件。PDF文檔或ZIP文件,可以輸入byte數組、文件路徑、stream數據、Content-Type、下載文件名等參數並將其返回客戶端。事實上,FileResult是一個抽象類,在MVC中實現FIleResult類的子類共有3個,分別是:

  • FilePathResult:響應一個實體文件;
  • FileContentResult:響應一個byte數組的內容;
  • FileStreamResult:響應一個Stream數據。

    但通過Controller類中所提供的File輔助方法可以讓你不用記憶這么多。File()輔助方法能自動選取不同的FileResult類進行響應。

    如果通過Action輸出一個存儲在"App_Data"目錄欄下的PNG圖像文件,可以參考以下代碼:

1        public ActionResult GetFile()
2         {
3             return File(Server.MapPath("~/App_Data/UserA/a.png"),"image/png");
4         }

    若希望直接通過瀏覽器下載文件,而不是在瀏覽器打開文件,可以再第3個參數中輸入要求下載的文件名。如:PDF文件來自於數據庫,並希望讓用戶下載,可以先取得一個byte數組或Stream數據,並在File()輔助方法的第2個參數中指定正確的Content-Type,最后再指定要下載的文件名即可。

1       public ActionResult GetFile()
2         {
3             byte[] fileContent = GetFileByteArrayFromDB();
4 
5             return File(fileContent,"application/pdf","Report.pdf");
6         }


  6.6  強制下載文件時需注意中文文件名的問題

    由於MVC支持中文文件名文件下載的能力有限,若要開發出跨瀏覽器下載中文文件名文件的解決方案,就不能只靠FileResult類了,可以自定義ActionResult類來克服這個問題,也可直接用較為底層的方法來解決。一下將說明強制下載文件的原理及解決方法。

    強制下載文件功能時,通常是通過設定"Content-Disposition"這個HTTP響應標頭(Response Header)的方式將強制下載文件的要求告知客戶端,示例如下。

1        string fileName = "ExportData.csv";
2        string strContentDisposition = String.Format("{0};filename=\"{1}\"", "attachment", fileName);
3        Response.AddHeader("Content-Disposition", strContentDisposition);

    通過上述程序代碼,就可以讓客戶端強制下載此頁面的內容,也就是說,改頁面的內容(可能是文件或二進制文件)不會直接在瀏覽器中打開,下載后也不會打開相關程序。

    Content-Disposition標頭的第一組參數是"attachment",代表此文件是一個附件文件,也就是"要求下載"的意思。如果將"attachment"改成".inline"的話,就代表這是一個內嵌於其他網頁的文件(如圖像文件、CSS、JavaScript、Flash等),而這也是默認的設定,等同於不添加Content-Disposition標頭的情況。

  6.7  JavaScriptResult類

    JavaScriptResult類的用途是將javaScript程序代碼響應給瀏覽器。通過Ajax環境,可以利用JavaScriptResult類來響應適當的JavaScript程序代碼並將其交給瀏覽器動態執行,由於Ajax功能屬於View.

  6.8  JsonResult類

    JSON是Web在實現Ajax應用程序時經常使用的一種數據傳輸格式,JsonResult類可自動將任意對象的數據序列轉換成JSON格式返回。JsonResult類默認的Content-Type為application/json,對於某些JavaScript Framework來說,這是必要的需求,如jQuery。 建議盡量避免使用HTTP GET方法獲取JSON數據。但若只使用HTTP POST方法獲取JSON數據也存在一個問題,那就是數據無法被瀏覽器緩存。如果數據敏感度不高且想實現緩存的話,還需讓JsonResult類能夠對HTTP GET請求進行響應,解決方案就是為JSON()輔助方法加上一個JsonResultBehavior列舉參數,這樣就可以通過GET方法取得JSON數據了。

1       public ActionResult JSON()
2         {
3             return Json(new
4             {
5                 id = 1,
6                 name = "will",
7                 CreatedOn = DateTime.Now
8             }, JsonRequestBehavior.AllowGet);
9         }

  6.9  RedirectResult類

    RedirectResult類主要用途是執行指向其他頁面的重定向。在RedirectResult類的內部,基本上還是用Response.Redirect()方法響應HTTP 302暫時定向的。

1        public ActionResult Redirect()
2         {
3             return Redirect("/Home/Index");
4         }

  6.10 RedirectToRoute類

    Controller類中有兩個與RedirectToRoute類有關的輔助方法,分別是RedirectToAction()和RedirectToRoute().

    1. RedirectToAction()輔助方法

       RedirectToAction()方法比較簡答,既可通過直接輸入Action名稱來設定讓瀏覽器轉向該Action網址,也可以輸入新建的RouteValue值,如下:

  • 轉到同一個Controller中的另一個Action。
1       public ActionResult RedirectToActionSample()
2         {
3             return RedirectToAction("SamplePage");
4         }
  • 轉到指定Controller的特定Action。
1       public ActionResult RedirectToActionSample()
2         {
3             return RedirectToAction("List", "Member");
4         }
  • 轉到MemberController的List Action,並且加上"page"這個RouteValue值。
1       public ActionResult RedirectToActionSample()
2         {
3             return RedirectToAction("List", "Member", new { page = 3 });
4         }

  2. RedirectToRoute()輔助方法

    RedirectToRoute()輔助方法較為復雜,可利用Global.asax文件中定義的網址路由表來指定不同的轉向網址。

  • 轉到同一個Controller中的另一個Action。
1       public ActionResult RedirectToRouteSample()
2         {
3             return RedirectToRoute(new { action = "SamplePage" });
4         }
  • 轉到指定Controller的特定Action。
1       public ActionResult RedirectToRouteSample()
2         {
3             return RedirectToRoute(new { controller = "Member", action = "list" });
4         }
  • 轉到MemberController的List Action,並且加上"page"這個RouteValue。
1       public ActionResult RedirectToRouteSample()
2         {
3             return RedirectToRoute(new { controller = "Member", action = "list", page = 3 });
4         }

七、ViewData與TempData概述

  7.1  ViewData

    ViewData屬性是一個ViewDataDictionary類,可用於存儲任意對象的數據,但存儲的鍵值必須為字符串。

    ViewData有一個特性,就是它只會存在於當前的HTTP請求中,而不像Session一樣可以講數據帶到下一個HTTP請求。

  7.2  TempData

    TempData的數據結構與ViewData一樣,都屬於字典類,不過TempData屬性的類是TempDataDictionary。TempData嗨有一點不一樣的地方,就是它的內部是用Session來存儲數據的。存儲在TempData中的數據只會暫存:1次網頁請求!

    1次網頁請求的定義:當窗口數據被傳送到以下Action中進行存儲時,如果發生新建數據失敗的情況,我們會希望這次傳送的數據可以保留至下一個頁面,此時,就會將這個只希望出現1此的信息保存到TempData中,並在下一個頁面中執行。

 1        [HttpPost]
 2         public ActionResult Create(Message msg)
 3         {
 4             if (!UpdateMessageToDB(msg))
 5             {
 6                 TempData["PostedMessage"] = msg;
 7                 return RedirectToAction("Create");
 8             }
 9             return RedirectToAction("Index");
10         }

    當新建數據失敗的情況發生后,將會返回"Create"這個Action,並將原來的數據從TempData中讀出,示例如下:

1         [HttpGet]
2         public ActionResult Create()
3         {
4             string data = TempData["PostedMessage"] as Message;
5             return View(data);
6         }

    此時,數據就會回到Create()方法,並在此被傳送到Create視圖。在這個Controller生命周期結束的前一刻,由於MVC會記錄"TempData["PostedMessage"]"語句已經被讀取了,因此,在網頁請求結束之前會將"TempData["PostedMessage"]"語句刪除。

八、模型綁定

    8.1  簡單模型綁定

    8.2  使用FormCollection類獲取窗口數據

    除了可以通過簡單模型綁定機制獲取窗口傳過來的單欄數據之外,我們也可以通過FormCollection類一次獲取整個窗口傳送過來的數據。只要設定一個FormCollection類的參數,就可以獲取所有從窗口傳送過來的數據。這種用法與使用Request.Form()方法一樣,不過在MVC里還是建議盡量不要用Request.Form()方法來獲取窗口數據。

1       public ActionResult GetFormCollection(FormCollection form)
2         {
3             ViewData["uName"] = form["uName"];
4             return View("ModelBinderDemo");
5         }

    8.3  復雜模型綁定

    8.4  多個復雜模型的綁定

    好比一個HTML代碼中只有一個Form(表單)窗口,但是該窗口內有兩組字段,是因為在編寫數據綁定類里面設定了兩組參數,分別為form1和form2,而這兩組參數的類都是GuestbookForm,如:

1       public Action ComplexModelBinding(GuestbookForm form1, GuestbookForm form2)
2         {
3             InsertIntoDB(form1);
4             InsertIntoDB(form2);
5             return Redi("/");
6         }

    只需要做好模型綁定即可。

   8.5  判斷模型綁定的結果

    驗證數據,用自身的方法如Html.ValidationMessageFor()方法用來顯示特定字段的錯誤信息。也可在Controller中判斷業務邏輯。

九、動作過濾器(略)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM