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


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

一、         ASP.NET MVC 1.0 Result 幾何?

 

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

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

1.       ActionResult(base)

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

 

二、朴實的 ActionResult

 

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

 

public abstract class ActionResult {

 

        public abstract void ExecuteResult(ControllerContext context);

 

}

 

 

看看普通人民、相貌平平的ActionResult,ActionResult 是個朴素老百姓,沒啥特長,就一個 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 遺傳並實現了ActionResult的ExecuteResult()方法,同時也遺傳了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用於將字符串直接向客戶端輸出。ContentResult的ExecuteResult方法實際上是調用了 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.Redirect或Response.Write,此外還可以利用二進制流Response.OutputStream.Write向客戶端上載文件……據此我們所以拓展編寫更多針對實際意義的Result。例如 XmlResult(文件)、RssResult(跟XmlResult其實是一樣的)等等。

 

六、JsonResult

 

JsonResult首先將指定的對象序列化為Json字符串,然后將字符串寫入到HTTP輸出流。撇開對象序列化為Json字符串這一過程,實際上與ContentResult其實是一樣的,因為JsonResult與ContentResult都是調用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

 

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

 

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。

 


免責聲明!

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



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