上一篇介紹到CreateActionResult
方法會產生一個ActionResult
物件利用MethodInfo
資訊.
最後透過InvokeActionResult
來呼叫ExecuteResult
方法來執行ActionResult
的ExecuteResult
方法,基本上MVC找到且執行Action方法後面就沒再做甚麼特別的事情了(後面做資源釋放...)
protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
{
actionResult.ExecuteResult(controllerContext);
}
本篇來介紹常用的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
有三個屬性
Content
:響應內容.ContentType
:設置Http Header
攔位ContentType
ContentEncoding
:設置Encoding
方式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
這個ActionResult
如其名就是導轉頁面.
Permanent
:屬性判斷是否需要Permanently
導轉頁面(Http-Code:RedirectPermanent=301
,Redirect=302
)Url
:轉導的URL
透過UrlHelper.GenerateContentUrl
產生URL
.(在GenerateContentUrl
會判斷第一個字元是否是~
波浪號,如果是代表站內導轉.)最後利用Permanent
布林判斷使用RedirectPermanent
還是Redirect
方法.
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
這個類別很有趣,只有override ExecuteResult
方法但沒有實做,上篇小結有提到這裡使用一個設計模式null object pattern.
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
是一個抽象類別,提供一個抽象方法給abstract void WriteFile(HttpResponseBase response)
子類提供覆寫.
有兩個類別繼承於FileResult
抽象類別
FilePathResult
FileContentResult
FileResult
抽象類別在ExecuteResult
設置傳輸檔案需要的前置作業(設置Content-Type
...),最後的資料傳輸透過各個子類別去實現.
其中
headerValue
實做Http回應擋頭對於RFC
規範.
// From RFC 2183, Sec. 2.3:
// The sender may want to suggest a filename to be used if the entity is
// detached and stored in a separate file. If the receiving MUA writes
// the entity to a file, the suggested filename should be used as a
// basis for the actual filename, where possible.
string headerValue = ContentDispositionUtil.GetHeaderValue(FileDownloadName);
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
將檔案已位元組方式轉存給Client
端.
透過HttpResponseBase.OutputStream.Write
方法.
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
透過檔案名稱FileName
將檔案提供給Client
藉由HttpResponseBase.TransmitFile
方法.
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
相關程式碼.