ASP.NET MVC – 關於Action返回結果類型的事兒(上)


本文轉自:博客園-文超的技術博客

一、         ASP.NET MVC 1.0 Result 幾何?

 

Action的返回值類型到底有幾個?咱們來數數看。

ASP.NET MVC 1.0 目前一共提供了以下十幾種Action返回結果類型:

1.       ActionResultbase

2.       ContentResult

3.       EmptyResult

4.       HttpUnauthorizedResult

5.       JavaScriptResult

6.       JsonResult

7.       FileResult (base)

8.       FileContentResult

9.       FilePathResult

10.   FileStreamResult

11.   RedirectResult

12.   RedirectToRouteResult

13.   ViewResultBase(base)

14.   ViewResult

15.   PartialViewResult

 

一個列表下來看得人眼花繚亂,因為可用的Result很多,接着再瞧瞧類關系圖以佐辨析:

 

 <圖>

 

如圖中可見,ActionResult可謂人丁興旺,目前膝下有兒9子(如圖中紅色所圈的類),ViewResultBaseFileResult又各有子兩三口,這些兒孫們各司所長。那么各個 Result 都會干點啥事兒呢?這個問題說來話長,不過根據諸如“虎父無犬子”、“種瓜得瓜,種豆得豆”、“龍生龍,鳳生鳳,老鼠的孩子打地洞”的俗語,孩子們多少從他爹那兒遺傳了點什么,所以要說明它們的才干之前,得先嘮叨嘮叨一下 ActionResult這個爹,這個爺,因此這事情還是得先從ActionResult說起。

 

二、朴實的 ActionResult

 

所有的 Result 都派生自 ActionResult抽象類,因此 ActionResult 作為基類提供了最基礎的功能,ActionResult 是一個抽象類,其聲明如下:

 

public abstract class ActionResult {

 

        public abstract void ExecuteResult(ControllerContext context);

 

}

 

看看普通人民、相貌平平的ActionResultActionResult 是個朴素老百姓,沒啥特長,就一個 ExecuteResult() 抽象方法,這個ExecuteResult() 抽象方法還啥都不干,遺傳給兒女孫子們讓它們去發揮,那么它的責任其實就很明確了,它就是為遺傳作准備的,繁殖下一代用的,是只公豬種。因為ActionResult是所有Result的基類,因此你可以在所有的Action上使用它作為返回值類型,而無需動腦筋來明確與返回值相同的類型。

 

二、         EmptyResult

 

EmptyResult ActionResult 最沒用的兒子,雖然生兒都想生孫仲謀,希望兒子們都是八斗之才,國家棟梁,可惜第一胎 EmptyResult 就嚴重破壞了它的夢想,看來也只能痛恨自己種子不夠好。咱來瞧瞧這個沒用的阿斗:

 

//表示一個啥都不干的結果,就像 controller action 返回 null

    public class EmptyResult : ActionResult {

 

        private static readonly EmptyResult _singleton = new EmptyResult();

 

        internal static EmptyResult Instance {

            get {

                return _singleton;

            }

        }

 

        public override void ExecuteResult(ControllerContext context) {

        }

    }

 

EmptyResult 遺傳並實現了ActionResultExecuteResult()方法,同時也遺傳了ActionResult的天真朴實的想法,也想“還是等下一代吧”,它有點老子的“無為”味道,所以它的ExecuteResult()方法像足了它的老爹,啥也不干。

 

EmptyResult 類使用了簡單的單例模式,看來這樣不思進取的兒子,整個家族里頭生一個就夠糟糕了,用廣東人的話說,生它還不如生塊叉燒肉。

 

Action中,若要返回一個空的頁面(不常用),則可如下:

 

public ActionResult Index()

{

return new EmptyResult();

}

 

執行后頁面將缺省返回一個body為空的HMTL架構:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head>
<meta content="text/html; charset=gb2312" http-equiv=content-type></head>
<body></body></html>

 

三、RedirectResult

EmptyResult的“無為”給ActionResult 的打擊着實不小,只好將期待落在其他孩子身上,RedirectResult雖然不是什么大才,起碼有一技之長,我們看看它的 ExecuteResult() 方法:

 

public override void ExecuteResult(ControllerContext context) {

            if (context == null) {

                throw new ArgumentNullException("context");

            }

 

            string destinationUrl = UrlHelper.Content(Url, context.HttpContext);

            context.HttpContext.Response.Redirect(destinationUrl, false /* endResponse */);

        }

 

RedirectResult用於執行轉移。事實上 RedirectResult 最終調用了 Response.Redirect() 進行轉移,所以您可以使用RedirectResult跳轉到任意的包括當前項目或網絡上的Url,例如:http://www.cnblogs.com,對於當前項目的路徑,因為使用了UrlHelper.Content() 方法獲取目標路徑,所以RedirectResult傳遞的Url同時支持當前項目目錄標識符 ~ (即應用程序目錄)。

 

四、RedirectToRouteResult

 

RedirectToRouteResult對於RedirectResult而言,其作用有所局限,僅能轉移到路由(路由匹配的結果最終是一條相對當前項目的Url,例如: /Home/Index ),總的來說與RedirectResult的最終作用是一樣的,都是執行轉移。RedirectResult較為直接地轉移到任意指定的Url,而RedirectToRouteResult則轉移到指定的路由(路由匹配所得結果最終也是一個的Url):

 

public override void ExecuteResult(ControllerContext context) {

            if (context == null) {

                throw new ArgumentNullException("context");

            }

 

            string destinationUrl = UrlHelper.GenerateUrl(RouteName, null /* actionName */, null /* controllerName */, RouteValues, Routes, context.RequestContext, false /* includeImplicitMvcValues */);

            if (String.IsNullOrEmpty(destinationUrl)) {

                throw new InvalidOperationException(MvcResources.ActionRedirectResult_NoRouteMatched);

            }

 

            context.HttpContext.Response.Redirect(destinationUrl, false /* endResponse */);

        }

 

RedirectToRouteResult先通過調用UrlHelper.GenerateUrl()來獲得路由匹配所得的最終Url,接着的執行轉移過程與RedirectResult相同。

 

路由配置的過程在Global.asax文件中進行,在以MVC模板方式創建的MVC項目中都帶有此文件,可在文件中的MvcApplication類的 RegisterRoutes()方法中進行配置路由,該方法缺省的內容如下:

 

public static void RegisterRoutes( RouteCollection routes )

        {

            routes.IgnoreRoute( "{resource}.axd/{*pathInfo}" );

 

            routes.MapRoute(

                "Default",                                              // Route name

                "{controller}/{action}/{id}",                           // URL with parameters

                new { controller = "Home", action = "Index", id = ""// Parameter defaults

            );

 

        }

 

RedirectToRouteResult 可跳轉至任何一條匹配的路由規則。是以利用路由轉移可以跳轉到其他控制器的 Action

 

五、ContentResult

 

ContentResult用於將字符串直接向客戶端輸出。ContentResultExecuteResult方法實際上是調用了 Response.Write( string… ),輸入並無特別之處,但是在 ASP 時代,這個Response.Write() 卻是可以縱橫頁面。從輸出一個簡單的字符串到整個頁面,Response.Write()都能勝任,所以ContentResult顯得特別強大:

 

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呈現的結果與EmptyResult 是一樣的,都是輸出最基本的<body>標記內容為空的HTML,若內容不為空,則直接輸出這些內容(不再輸出其他任何 HTML 代碼),例如:

 

public ActionResult Index()

{

return Content( "a" );

}

 

其頁面的HTML代碼也將只有一個字符 a,要補全所有基本標記需要在字符串中編寫,例如:

 

public ActionResult Index()

        {

           return Content( "<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 Transitional//EN"">" +

                              "<html>" +

                                "<head><meta content=""text/html; charset=gb2312"" http-equiv=content-type></head>" +

                               "<body>" +

 

                                "abc" +

 

                                "</body>" +

                              "</html>"

                        );

        }

 

當然不建議使用此方法來輸出頁面標記,ContentResult 用在Ajax中頗為合適,因為只要內容不為空,輸出的字符串與傳送到客戶端的內容一致,沒有額外的附加內容。

 

事實上從ContentResult我們可以看到一個ActionResult其實並無特別,從前面幾個Result 來看,其實不過是Response.RedirectResponse.Write,此外還可以利用二進制流Response.OutputStream.Write向客戶端上載文件……據此我們所以拓展編寫更多針對實際意義的Result。例如 XmlResult(文件)、RssResult(跟XmlResult其實是一樣的)等等。

 

六、JsonResult

 

JsonResult首先將指定的對象序列化為Json字符串,然后將字符串寫入到HTTP輸出流。撇開對象序列化為Json字符串這一過程,實際上與ContentResult其實是一樣的,因為JsonResultContentResult都是調用Response.Write()HTTP輸出流寫入一些內容。所以對此不再贅述:

 

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;

            }

            else {

                response.ContentType = "application/json";

            }

            if (ContentEncoding != null) {

                response.ContentEncoding = ContentEncoding;

            }

            if (Data != null) {

                // The JavaScriptSerializer type was marked as obsolete prior to .NET Framework 3.5 SP1

#pragma warning disable 0618

                JavaScriptSerializer serializer = new JavaScriptSerializer();

                response.Write(serializer.Serialize(Data));

#pragma warning restore 0618

            }

        }

 

有個地方想嘮叨兩句,在代碼中的:

response.ContentType = "application/json";

 

若要直接向頁面輸出的話需要更改為文本類型,例如 text/html,否則你要以文件形式下載JsonResult的結果內容。不過這對於將Json用於Ajax而言不會有什么影響。

 

七、JavaScriptResult

 

道與 JsonResultContentResult相同。所以也不贅述,徒費唇舌:

 

public override void ExecuteResult(ControllerContext context) {

            if (context == null) {

                throw new ArgumentNullException("context");

            }

 

            HttpResponseBase response = context.HttpContext.Response;

            response.ContentType = "application/x-javascript";

 

            if (Script != null) {

                response.Write(Script);

            }

        }

 

八、HttpUnauthorizedResult

 

HttpUnauthorizeResult 設置客戶端錯誤代號為 401,即未經授權瀏覽狀態,若設置了Form驗證並且客戶端沒有任何身份票據,那么將轉跳到指定的頁面(例如登陸頁):

 

public override void ExecuteResult(ControllerContext context) {

            if (context == null) {

                throw new ArgumentNullException("context");

            }

 

            // 401 is the HTTP status code for unauthorized access - setting this

            // will cause the active authentication module to execute its default

            // unauthorized handler

            context.HttpContext.Response.StatusCode = 401;

        }

 

可以學習HttpUnauthorizeResult來編寫更多同類的返回結果,例如設置 Response.StatusCode = 404,這個是常見的“頁面未找到”錯誤,403 禁止訪問等等。

 

九、FileResult

 

FileResult是一個抽象類,主要屬性包括聲明內容類型信息ContentType 及文件名稱FileDownloadName,客戶端下載工具中將顯示此名稱(如果有指定,ContentType可指定任意非空字符串),如果不指定文件名,ContentType需要正確指定,否則無法識別待下載的文件類型。

 

FileResult 用作其他向客戶端上載文件的類的基類。

 

十、FilePathResult

 

FilePathResult 繼承自 FileResult,使用 FilePathResult 類向客戶端上載文件只需要給出文件的路徑即可。FilePathResult 將調用 Response.TransmitFile() 傳輸該文件:

 

protected override void WriteFile(HttpResponseBase response) {

            response.TransmitFile(FileName);

        }

 

十一、FileContentResult

 

FileContentResult繼承自 FileResult

 

FileContentResult 將指定的字節內容寫入二進制流(客戶端將以文件形式下載),對比 FilePathResult 所不同的是 FilePathResult是給出文件路徑,然后將整份文件上載給客戶,而 FileContentResult 則可以傳輸某一個字節數組,例如:

 

public ActionResult Index()

{

return File( System.Text.Encoding.UTF8.GetBytes( "你好嗎" ), "unknown", "t.txt" );

}

 

FileContentResult 使用 Response.OutputStream.Write 輸出內容:

protected override void WriteFile(HttpResponseBase response) {

            response.OutputStream.Write(FileContents, 0, FileContents.Length);

}

 

十二、FileStreamResult

 

FileStreamResult 繼承自 FileResult

 

FileStreamResult 向指定文件流讀取數據,其他的內容與FileContentResult道同。請參考FileContentResult

 

 

 

 

ViewResult 及其相關內容將在下回討論。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM