代码改变世界

【转】ASP.Net MVC Action 返回类型

2012-03-10 16:13  cnb_mtime  阅读(291)  评论(0编辑  收藏  举报

 

[转]

一、         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 newInvalidOperationException(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