Agenda
前言 上一篇介紹到CreateActionResult方法會產生一個ActionResult物件利用MethodInfo資訊.
最後透過InvokeActionResult來呼叫ExecuteResult方法來執行ActionResult的ExecuteResult方法,基本上MVC 找到且執行Action 方法後面就沒再做甚麼特別的事情了(後面做資源釋放…)
1 2 3 4 protected virtual void InvokeActionResult (ControllerContext controllerContext, ActionResult actionResult ){ actionResult.ExecuteResult(controllerContext); }
本篇來介紹常用的ActionResult其內部運作程式碼
我有做一個可以針對於Asp.net MVC Debugger 的專案,只要下中斷點就可輕易進入Asp.net MVC原始碼.
6種基本的ActionResult 下面這六個類別是直接繼承於ActionResult的類別(其中有標註Base class 代表這是抽象類別另外有類別繼承它)
ContentResult:回傳一組字串,利用response.Write方法
EmptyResult:什麼都不動作(當Action回傳void使用)
FileResult(Base class):把檔案當作回傳
HttpStatusCodeResult:回傳HTTP 狀態碼
RedirectResult & RedirectToRouteResult:使用Response.Redirect轉導到其他頁面
ViewResultBase(Base class):會找尋相對應View檔案(cshtml會編譯成一個DLL)來執行
ViewResultBase會在另一篇介紹(因為機制比較複雜)
ContentResult 在ContentResult有三個屬性
Content:響應內容.
ContentType:設置Http Header攔位ContentType
ContentEncoding:設置Encoding方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class ContentResult : ActionResult { public string Content { get ; set ; } public Encoding ContentEncoding { get ; set ; } public string ContentType { get ; set ; } public override void ExecuteResult (ControllerContext context ) { if (context == null ) { throw new ArgumentNullException("context" ); } HttpResponseBase response = context.HttpContext.Response; if (!String.IsNullOrEmpty(ContentType)) { response.ContentType = ContentType; } if (ContentEncoding != null ) { response.ContentEncoding = ContentEncoding; } if (Content != null ) { response.Write(Content); } } }
ContentResult操作很簡單透過response.Write把內容Print出來
RedirectResult & RedirectToRouteResult RedirectResult這個ActionResult如其名就是導轉頁面.
Permanent:屬性判斷是否需要Permanently導轉頁面(Http-Code :RedirectPermanent=301,Redirect=302)
Url:轉導的URL透過UrlHelper.GenerateContentUrl產生URL.(在GenerateContentUrl會判斷第一個字元是否是~波浪號,如果是代表站內導轉.)
最後利用Permanent布林判斷使用RedirectPermanent還是Redirect方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class RedirectResult : ActionResult { public bool Permanent { get ; private set ; } public string Url { get ; private set ; } public override void ExecuteResult (ControllerContext context ) { if (context == null ) { throw new ArgumentNullException("context" ); } if (context.IsChildAction) { throw new InvalidOperationException(MvcResources.RedirectAction_CannotRedirectInChildAction); } string destinationUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext); context.Controller.TempData.Keep(); if (Permanent) { context.HttpContext.Response.RedirectPermanent(destinationUrl, endResponse: false ); } else { context.HttpContext.Response.Redirect(destinationUrl, endResponse: false ); } } }
RedirectToRouteResult基本流程跟上面一樣只是透過UrlHelper.GenerateUrl產生要導轉URL
EmptyResult EmptyResult這個類別很有趣,只有override ExecuteResult方法但沒有實做,上篇小結有提到這裡使用一個設計模式null object pattern .
1 2 3 4 5 6 7 8 9 10 11 12 13 public class EmptyResult : ActionResult { private static readonly EmptyResult _singleton = new EmptyResult(); internal static EmptyResult Instance { get { return _singleton; } } public override void ExecuteResult (ControllerContext context ) { } }
FileResult FileResult是一個抽象類別,提供一個抽象方法給abstract void WriteFile(HttpResponseBase response)子類提供覆寫.
有兩個類別繼承於FileResult抽象類別
FilePathResult
FileContentResult
FileResult抽象類別在ExecuteResult設置傳輸檔案需要的前置作業(設置Content-Type…),最後的資料傳輸透過各個子類別去實現.
其中headerValue實做Http回應擋頭對於RFC規範.
1 2 3 4 5 6 string headerValue = ContentDispositionUtil.GetHeaderValue(FileDownloadName);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public abstract class FileResult : ActionResult { private string _fileDownloadName; protected FileResult (string contentType ) { if (String.IsNullOrEmpty(contentType)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "contentType" ); } ContentType = contentType; } public string ContentType { get ; private set ; } public string FileDownloadName { get { return _fileDownloadName ?? String.Empty; } set { _fileDownloadName = value ; } } public override void ExecuteResult (ControllerContext context ) { if (context == null ) { throw new ArgumentNullException("context" ); } HttpResponseBase response = context.HttpContext.Response; response.ContentType = ContentType; if (!String.IsNullOrEmpty(FileDownloadName)) { string headerValue = ContentDispositionUtil.GetHeaderValue(FileDownloadName); context.HttpContext.Response.AddHeader("Content-Disposition" , headerValue); } WriteFile(response); } protected abstract void WriteFile (HttpResponseBase response ) ; } }
FileContentResult FileContentResult將檔案已位元組方式轉存給Client端.
透過HttpResponseBase.OutputStream.Write方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class FileContentResult : FileResult { public FileContentResult (byte [] fileContents, string contentType ) : base (contentType ) { if (fileContents == null ) { throw new ArgumentNullException("fileContents" ); } FileContents = fileContents; } public byte [] FileContents { get ; private set ; } protected override void WriteFile (HttpResponseBase response ) { response.OutputStream.Write(FileContents, 0 , FileContents.Length); } }
FilePathResult FilePathResult透過檔案名稱FileName將檔案提供給Client
藉由HttpResponseBase.TransmitFile方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class FilePathResult : FileResult { public FilePathResult (string fileName, string contentType ) : base (contentType ) { if (String.IsNullOrEmpty(fileName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "fileName" ); } FileName = fileName; } public string FileName { get ; private set ; } protected override void WriteFile (HttpResponseBase response ) { response.TransmitFile(FileName); } }
小結: 本篇介紹了幾個實現ActionResult類別,跟其內部程式碼,這裡能了解到MVC 返回結果機於ActionResult方法.(這個概念我運用在Web Api服務,建立ResponseBase共同簽章,因為在做服務串接每個服務都有自己的加解密,回傳格式攔位.我可以統一透過一個ResponseBase類別裝載資料再藉由過濾器來幫忙組成相對應的資料回傳….)
下篇會來介紹繼承ActionResult最複雜的ViewResultBase相關程式碼.
__此文作者__:Daniel Shih(石頭) __此文地址__: https://isdaniel.github.io/Ithelp-day23/ __版權聲明__:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!