🚫 Ad Blocker Detected

Please disable your AD blocker to continue using this site. Ads help us keep the content free! please press keyboard F5 to refresh page after disabled AD blocker

請關閉廣告攔截器以繼續使用本網站。廣告有助於我們保證內容免費。謝謝! 關閉後請按 F5 刷新頁面

0%

6個基本(ActionResult) View是如何被建立(二) (第23天)

Agenda

前言

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

最後透過InvokeActionResult來呼叫ExecuteResult方法來執行ActionResultExecuteResult方法,基本上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
// 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);
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 許可協議。轉載請註明出處!

如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^