iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 23
0

前言

上一篇介紹到CreateActionResult方法會產生一個ActionResult物件利用MethodInfo資訊.

最後透過InvokeActionResult來呼叫ExecuteResult方法來執行ActionResultExecuteResult方法,基本上MVC找到且執行Action方法後面就沒再做甚麼特別的事情了(後面做資源釋放...)

protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
{
	actionResult.ExecuteResult(controllerContext);
}

本篇來介紹常用的ActionResult其內部運作程式碼

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方式
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方法.

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.

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規範.

// 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

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

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相關程式碼.


上一篇
[Day22] View是如何被建立(一)
下一篇
[Day24] 探討ViewEngine機制 View是如何被建立(三)
系列文
從Asp.net框架角度進入Asp.net MVC原始碼30

尚未有邦友留言

立即登入留言