定義在Controller中的Action方法大都返回一個ActionResult對象。ActionResult是對Action執行結果的封裝,用於最終對請求進行響應。ASP.NET MVC提供了一系列的ActionResult,它們本質上是通過怎樣的方式來響應請求的呢?這是這個系列著重討論的主題。
一、ActionResult對請求的響應
HTTP是一個單純的采用請求/回復消息交換模式的網絡協議,Web服務器在接收並處理來自客戶端的請求後會根據處理結果對請求予以響應。對於來自客戶端的訪問請求,最終的處理體現在針對目標Action方法的執行,我們可以在定義Action方法的時候人為地控制對請求的響應。如果下面的代碼片斷所示,抽象類Controller具有一個只讀的Response屬性表示當前的HttpResponse,我們可以直接利用它來實現對請求的響應。我們也可以間接地通過表示當前HTTP上下文的HttpContext屬性和表示Controller上下文的ControllerContext屬性來獲取用於響應請求的HttpResponse對象。
1: public abstract class Controller : ControllerBase, ...
2: {
3: //其他成員
4: public HttpResponseBase Response { get; }
5: public HttpContextBase HttpContext { get; }
6: }
7:
8: public abstract class ControllerBase : IController
9: {
10: //其他成員
11: public ControllerContext ControllerContext { get; set; }
12: }
原則上講,我們可以利用HttpResponse對請求響應作百分之一百地控制,但是我們一般並不這麼做,而是將針對請求的響應實現在一個ActionResult對象中。如下面的代碼片斷所示,ActionResult是一個抽象類型,最終的請求響應實現在抽象方法ExecuteResult方法中。
1: public abstract class ActionResult
2: {
3: //其他成員
4: public abstract void ExecuteResult(ControllerContext context);
5: }
顧名思義,ActionResult就是執行Action的結果。ActionInvoker在完成對Action方法的執行後,如果返回一個ActionResult對象,ActionInvoker會將當前Controller上下文作為參數調用其ExecuteResult方法。View的最終呈現是通過ActionResult的子類ViewResult來完成的,除了ViewResult,ASP.NET MVC還為我們定義了額外一些具體的ActionResult。
二、EmptyResult
上面我們談到Action方法返回的ActionResult對象被ActionInvoker調用以實現對當前請求的響應,其實這種說法不夠准確。不論Action方法是否具有返回值,也不論它的返回值是什麼類型,ActionInvoker最終都會創建相應的ActionResult對象。如果Action方法返回類型為void,或者返回值為Null,最終生成的就是一個EmptyResult對象。
如下面的代碼片斷所示,在重寫的ExecuteResult方法中EmptyResult其實什麼都沒有做,所以EmptyResult是一個“空”的ActionResult。EmptyResult的設計體現了一種設計思想:我們采用一種管道式的設計來完成針對某類請求的處理,比如ASP.NET MVC針對請求的處理流程是“Action方法的執行=〉根據執行結果生成ActionResult=〉執行ActionResult”,但是這個流程不適合某些特殊的請求(比如Action方法不具有返回值或者返回值為Null,那麼後面的兩個環節可以忽略),我們對這些例外的場景進行一些適配工作使我們可以按照統一的方式來處理所有的請求,所以EmptyResult在這裡起到了一個適配器的作用。
1: public class EmptyResult : ActionResult
2: {
3: public override void ExecuteResult(ControllerContext context)
4: {
5: }
6: }
三、ContentResult
ContentResult使ASP.NET MVC按照我們指定的內容對請求予以響應。如下面的代碼片斷所示,我們可以利用ContentResult的Content屬性以字符串的形式指定響應的內容,另外兩個屬性ContentEncoding和ContentType則用於指定字符編碼方式和媒體類型(MIME類型)。抽象類Controller定義了如下三個受保護的Content方法重載根據指定的內容、編碼和媒體類型創建相應的ContentResult。
1: public class ContentResult : ActionResult
2: {
3: public override void ExecuteResult(ControllerContext context);
4:
5: public string Content { get; set; }
6: public Encoding ContentEncoding { get; set; }
7: public string ContentType { get; set; }
8: }
9:
10: public abstract class Controller : ControllerBase, ...
11: {
12: //其他成員
13: protected ContentResult Content(string content);
14: protected ContentResult Content(string content, string contentType);
15: protected virtual ContentResult Content(string content, string contentType, Encoding contentEncoding);
16: }