[转]了解ASP.NET MVC几种ActionResult的本质:EmptyResult ContentResult

本文转自:http://www.cnblogs.com/artech/archive/2012/08/13/action-result-01.html

定义在Controller中的Action方法大都返回一个ActionResult对象。ActionResult是对Action执行结果的封装,用于最终对请求进行响应。ASP.NET MVC提供了一系列的ActionResult,它们本质上是通过怎样的方式来响应请求的呢?这是这个系列着重讨论的主题。[本文已经同步到《How ASP.NET MVC Works?》中]

目录      一、ActionResult对请求的响应       二、EmptyResult       三、ContentResult       四、实例演示:执行返回类型为非ActionResult的Action方法得到的ActionResult对象       五、实例演示:通过ContentResult实现主题定制

一、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: }

在重写的ExecuteResult方法中,ContentResult利用作为参数的ControllerContext对象得到当前HttpContext的HttpResponse对象,并借助它将指定的内容按照希望的编码和媒体类型对请求进行响应,具体的实现如下面的代码片断所示。

   1: public class ContentResult : ActionResult
   2: {
   3:     //其他成员
   4:     public override void ExecuteResult(ControllerContext context)
   5:     {       
   6:         HttpResponseBase response = context.HttpContext.Response;
   7:         if (!string.IsNullOrEmpty(this.ContentType))
   8:         {
   9:             response.ContentType = this.ContentType;
  10:         }
  11:         if (this.ContentEncoding != null)
  12:         {
  13:             response.ContentEncoding = this.ContentEncoding;
  14:         }
  15:         if (this.Content != null)
  16:         {
  17:             response.Write(this.Content);
  18:         }
  19:     }
  20: }

上面我们说过,ASP.NET MVC为了能够采用相同的流程来处理所有的请求,不论是Action是否具有返回值,具有怎样的返回值,ActionInvoker都会创建相应的ActionResult。对于不具有返回值或者返回Null的Action方法调用来说,最终创建的是一个EmptyResult对象,那么如果返回值不是一个ActionResult对象,ActionInvoker最终会创建怎样一个ActionResult对象呢

实际上对于一个非Null的返回值,ActionInvoker采用这样的方式来创建相应的ActionResult:如果返回对象是一个ActionResult,直接返回该对象,否则将对象转换成字符串并以此创建一个ContentResult对象。ControllerActionInvoker根据Action方法的返回指生成相应ActionResult的逻辑体现在如下一个受保护的虚方法CreateActionResult中,最后一个参数(actionReturnValue)表示Action方法的返回值。而另一个受保护的InvokeActionMethod负责执行Action方法并返回响应的ActionResult对象,该方法在执行Action方法得到返回值后通过调用CreateActionResult方法返回相应的ActionResult对象。

   1: public class ControllerActionInvoker : IActionInvoker
   2: {
   3:     //其他成员
   4:     protected virtual ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters);
   5:     protected virtual ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue);
   6: }

四、实例演示:执行返回类型为非ActionResult的Action方法得到的ActionResult对象

我们可以通过一个简单的实例来验证ActionInvoker针对Action方法返回值对ActionResult的创建逻辑。在一个ASP.NET MVC应用中我们定义了如下一个HomeController,其中定义了4个无参数的Action方法。Foo返回一个RedirectResult对象,Bar的返回类型为viod,Baz返回值为Null,而Qux则返回一个double类型的数字。

   1: public class HomeController : Controller
   2: {
   3:     //其他成员
   4:     public ActionResult Foo()
   5:     {
   6:         return new RedirectResult("http://www.asp.net");
   7:     }
   8:     public void Bar(){ }
   9:     public ActionResult Baz()
  10:     {
  11:         return null;
  12:     }
  13:     public double Qux()
  14:     {
  15:         return 1.00;
  16:     }
  17: }

然后我们在HomeController定义如下一个Action方法Index。在该方法中我们通过反射的方式调用ActionInvoker的GetControllerDescriptor方法得到用于描述当前Controller的ControllerDescriptor对象。然后调用ControllerDescriptor的FindAction方法得到用于描述上述四个Action的ActionDescriptor对象。最后我们同样采用反射的方式调用ActionInvoker的InvokeActionMethod方法执行这4个Action并得到4个ActionResult对象。我们将4个得到ActionResult连同对应的ActionDescriptor对象构建一个Dictionary<ActionDescriptor, ActionResult>对象,并作为Model呈现在默认的View中。

   1: public class HomeController : Controller
   2: {
   3:     //其他成员
   4:     public ActionResult Index()
   5:     {
   6:         Dictionary<ActionDescriptor, ActionResult> actionResults = new Dictionary<ActionDescriptor, ActionResult>();
   7:         MethodInfo getControllerDescriptor = this.ActionInvoker.GetType().GetMethod("GetControllerDescriptor", BindingFlags.Instance | BindingFlags.NonPublic);
   8:         ControllerDescriptor controllerDescriptor = (ControllerDescriptor)getControllerDescriptor.Invoke(this.ActionInvoker, new object[] { ControllerContext });
   9:         MethodInfo invokeActionMethod = this.ActionInvoker.GetType().GetMethod("InvokeActionMethod", BindingFlags.Instance | BindingFlags.NonPublic);
  10:  
  11:         string[] actions = new string[] { "Foo", "Bar", "Baz", "Qux" };
  12:         Array.ForEach(actions, action =>
  13:             {
  14:                 ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(ControllerContext, action);
  15:                 ActionResult actionResult = (ActionResult)invokeActionMethod.Invoke(this.ActionInvoker, new object[] { ControllerContext, actionDescriptor, new Dictionary<string, object>() });
  16:                 actionResults.Add(actionDescriptor, actionResult);
  17:             });
  18:         return View(actionResults);
  19:     }
  20: }

如下所示的是Action方法Index对应View的定义,IDictionary<ActionDescriptor, ActionResult>作为该View的Model类型。在该View中我们将存在于字典中的ActionResult对象的类型和对应的Action名称以表格的形式呈现出来。

   1: @model IDictionary<ActionDescriptor, ActionResult>
   2: <html>
   3:     <head>
   4:         <title>ActionResults</title>
   5:     </head>
   6:     <body>
   7:        <table rules="all">
   8:             <tr><th>ActionName</th><th>ActionResult</th></tr>
   9:             @foreach (var item in Model)
  10:             { 
  11:                <tr>
  12:                   <td>@item.Key.ActionName</td><td>@item.Value.GetType().Name</td>
  13:                </tr>
  14:             }
  15:        </table>
  16:     </body>
  17: </html>

运行该程序后会在浏览器中得到如下图所示的输出结果,我们可以看到返回类型为void的Action方法Bar和返回值为Null的Action方法Baz执行后得到的都是一个EmptyResult对象。而返回非ActionResult(double类型)类型的Action方法Qux执行之后返回的是一个ContentResult。

clip_image002

五、实例演示:通过ContentResult实现主题定制

由于可以通过ContentResult的ContentType属性指定媒体类型,所以我们不仅仅可以利用它来返回最终会在浏览器中显示的文本,还可以返回其他一些类型的文本内容,比如JavaScript脚本(“text/javascript”)和CSS样式(“text/css”)等。通过ContentResult我们可以实现“静态文本的动态化”,也就是说我们可以在某个Action中根据当前的请求动态地生成一些文本(比如CSS样式),而这些文本内容原本是定义在静态文本文件中。

在接下来的这个实例演示中,我们将利用ContentResult实现对界面主题的定制。实现的机制非常简单:让一个返回类型为ContentResult的Action方法返回基于当前主题的CSS样式,而当前的主题通过一个可持久化的Cookie保存下来。我们在一个ASP.NET MVC应用中定义了如下一个HomeController,其Action方法Css返回一个表示CSS样式的ContentResult。在该Action方法中,我们从请求中提取表示主题的Cookie,并根据它生成基于当前主题的CSS样式(这里仅仅设置了字体类型和大小)。

   1: public class HomeController : Controller
   2: {
   3:     //其他成员
   4:     public ActionResult Css()
   5:     {
   6:         HttpCookie cookie = Request.Cookies["theme"] ?? new HttpCookie("theme","default");
   7:         switch (cookie.Value)
   8:         {
   9:             case "Theme1": return Content("body{font-family: SimHei; font-size:1.2em}", "text/css");
  10:             case "Theme2": return Content("body{font-family: KaiTi; font-size:1.2em}", "text/css");
  11:             default: return Content("body{font-family: SimSong; font-size:1.2em}", "text/css");
  12:         }
  13:     }
  14: }

我们在HomeController中定义了如下两个Index方法,无参的Index方法(针对HTTP-GET请求)从预定义Cookie中提取当前的主题(如果没有则采用默认的主题default)并以ViewBag的形式传递给View。另一个应用HttpPostAttribute特性的Index方法中接收用户提交的主题名称并设置为响应的Cookie,同样通过ViewBag的形式 保存当前的主题名称。两个Index方法最终都将默认的View呈现出来。

   1: public class HomeController : Controller
   2: {
   3:     //其他成员
   4:     public ActionResult Index()
   5:     {
   6:         HttpCookie cookie = Request.Cookies["theme"] ?? new HttpCookie("theme", "default");
   7:         ViewBag.Theme = cookie.Value;
   8:         return View();
   9:     }
  10:  
  11:     [HttpPost]
  12:     public ActionResult Index(string theme)
  13:     {
  14:         HttpCookie cookie = new HttpCookie("theme", theme);
  15:         cookie.Expires = DateTime.MaxValue;
  16:         Response.SetCookie(cookie);
  17:         ViewBag.Theme = theme;
  18:         return View();
  19:     }
  20: }

通过Css方法 的定义看出我们定义了三个主题(Theme1、Theme2和Default),它们采用不同的中文字体(黑体、楷体和宋体)。Action方法Index对应View具有如下一个表单,该表单中为这三种主题添加了相应的RadioButton使用户可以对主题进行定制。这个View最核心的部分是用于引用CSS文件的<link>元素,可以看到它的href属性指向的地址正是对应着HomeController的Action方法Css,也就是说最终用于控制页面样式的CSS是通过调用该Action得到的。

   1: <html>
   2: <head>
   3:     <title>主题设置</title>    
   4:     <link type="text/css"  rel="Stylesheet" href="@Url.Action("Css")" />
   5: </head>
   6: <body>
   7:     @using(Html.BeginForm())
   8:     {
   9:         string theme = ViewBag.Theme.ToString();        
  10:         @Html.RadioButton("theme", "Default", theme == "Default")<span>默认主题(宋体)</span><br/>
  11:         @Html.RadioButton("theme", "Theme1", theme == "Theme1")<span>主题1(黑体)</span><br/>
  12:         @Html.RadioButton("theme", "Theme2", theme == "Theme2")<span>主题2(楷体)</span><br />
  13:         <input type="submit" value="保存" />
  14:     }
  15: </body>
  16: </html>

现在我们直接运行我们的程序,并在出现的“主题设置”界面中设置不同的主题,界面的样式(字体)将会根据我们选择的主题而动态改变,具体的显示效果如下图所示。

clip_image004

了解ASP.NET MVC几种ActionResult的本质:EmptyResult & ContentResult 了解ASP.NET MVC几种ActionResult的本质:FileResult 了解ASP.NET MVC几种ActionResult的本质:JavaScriptResult & JsonResult 了解ASP.NET MVC几种ActionResult的本质:HttpStatusCodeResult & RedirectResult/RedirectToRouteResult

作者:Artech 出处:http://artech.cnblogs.com/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载于:https://www.cnblogs.com/freeliver54/archive/2013/03/15/2961808.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/295791.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为nova6计算机在哪,华为nova6开发者选项在哪

大家好&#xff0c;我是时间财富网智能客服时间君&#xff0c;上述问题将由我为大家进行解答。打开华为nova6开发者选项的步骤是&#xff1a;1、首先进入手机桌面&#xff0c;点击设置。2、点击关于手机。3、连续点击版本号7次&#xff0c;输入锁屏密码&#xff0c;点击返回。4…

他是发明声呐的物理天才,被妻子家暴后出轨守寡的师母,爱因斯坦却公开支持.........

全世界只有3.14 % 的人关注了爆炸吧知识情不知所起一往而深1911年11月4日&#xff0c;巴黎新闻报的头版头条标题是《爱情故事&#xff1a;居里夫人与朗之万教授》。报纸一出&#xff0c;顷刻售罄&#xff1b;巴黎上下&#xff0c;满城“风流”。#寡妇科学家“乱伦”相差半旬已婚…

如何在 .NET Core 中获取 CPU 使用率

这篇文章我们分享一种如何在 .NETCore 中获取 CPU使用率的方法, 它所报告的这个值和 任务管理器 中报告的 CPU 使用值 差不多是一致的。在 .NET Framework 中&#xff0c;很多人会用 PerformanceCounter 类做这件事情&#xff0c;参考如下代码&#xff1a;public class Program…

uml 类图聚合与组合

聚合 聚合是关联关系的一种特例&#xff0c;他体现的是整体与部分、拥有的关系&#xff0c;即has-a的关系&#xff0c;此时整体与部分之间是可分离的&#xff0c;他们可以具有各自的生命周期&#xff0c;部分可以属于多个整体对象&#xff0c;也可以为多个整体对象共享&#xf…

Android之ViewPager使用(用美女做的背景,给你疲惫的眼睛视觉冲击)

ViewPager的使用 我们先爆照,来点视觉冲击,其它的ViewPager弱爆了 照片看完了,接来下我们就来实现,just do it 第一步 你的项目需要有android-support-v4.jar的包,你懂的,有了才会支持ViewPager 第二步 看下项目的结构图片

深入探讨用位掩码代替分支(8):SSE指令集速度测试

在上一篇测试了MMX指令集&#xff0c;这次我们来测试SSE指令集。说的更精确一点&#xff0c;是测试SSE2指令集。  本篇致力于解决以下问题——1.SSE/SSE2指令集是什么&#xff1f;2.如何阅读Intel/AMD的手册&#xff1f;3.如何运用SSE指令集&#xff1f;如何将MMX代码升级为S…

mysql字段简索引_MySQL优化看这一篇就够了

本文概要概述为什么要优化系统的吞吐量瓶颈往往出现在数据库的访问速度上随着应用程序的运行&#xff0c;数据库的中的数据会越来越多&#xff0c;处理时间会相应变慢数据是存放在磁盘上的&#xff0c;读写速度无法和内存相比如何优化设计数据库时&#xff1a;数据库表、字段的…

.NET 6 Talk Party 2|.NET Core 与行业

关注我们微软 Reactor 为帮助广开发者&#xff0c;技术爱好者&#xff0c;更好的学习 .NET Core, C#, Python&#xff0c;数据科学&#xff0c;机器学习&#xff0c;AI&#xff0c;区块链, IoT 等技术&#xff0c;将每周三到周六&#xff0c;组织 3~5 场线上分享活动。欢迎跟着…

施一公:中国还缺乏真正的世界顶尖大学,研究生该听听这些建议

全世界只有3.14 % 的人关注了爆炸吧知识“中国的科技发展很快&#xff0c;变得很大&#xff0c;但还不够强&#xff1b;中国的人才众多&#xff0c;变得很大&#xff0c;但还不够强。中国是一个高等教育大国&#xff0c;但从权威的世界大学排名来看&#xff0c;中国缺乏真正的世…

Android之Fragment 真正的完全解析(上)

转载出处&#xff1a;http://blog.csdn.net/lmj623565791/article/details/37970961 自从Fragment出现&#xff0c;曾经有段时间&#xff0c;感觉大家谈什么都能跟Fragment谈上关系&#xff0c;做什么都要问下Fragment能实现不~~~哈哈&#xff0c;是不是有点过~~~ 本篇博客力求…

第零讲.1 tapestry项目创建与运行

2019独角兽企业重金招聘Python工程师标准>>> 1、在eclipse工程目录下创建项目&#xff1a; 第一次运行eclipse的时候会弹出选择工程项目存放地点&#xff0c;如我的存放路径D:\workspace。我们就把tapestry创建的项目放到这个目录方便统一管理。 打开系统的命令提示…

阿里云-数据盘挂载

2019独角兽企业重金招聘Python工程师标准>>> 硬盘分区及挂载操作步骤&#xff1a; 1. 查看未挂载的硬盘&#xff08;名称为/dev/xvdb&#xff09; # fdisk -l Disk /dev/xvdb doesnt contain a valid partition table 2. 创建分区 # fdisk /dev/xvdb ... 输入n Comm…

composer 查看php 版本_最常用的PHP版本:PHP 7.3取代7.2

php中文网最新课程每日17点准时技术干货分享自2014年以来&#xff0c;Private Packagist的联合创始人Jordi Boggiano一直在撰写半年度报告&#xff0c;介绍各种PHP版本的使用情况。他从packagist.io上的Composer安装中获取数据库。从2019年5月开始&#xff0c;PHP 7.3已在2019年…

钱少也就算了,为啥我们还越来越忙?

全世界只有3.14 % 的人关注了爆炸吧知识你是否时常感觉自己的生活总是不富裕&#xff0c;但工作却越来越忙&#xff1f;那是因为我们想得到的东西太多&#xff0c;但能够燃烧的生命却太少。那些对未来的焦虑、恐惧&#xff0c;说白了&#xff0c;就是想的太多。随着经历和阅历的…

Hello Blazor:(14)CSS隔离

前言上次我们说到&#xff0c;FindRazorSourceFile使用有一定限制.查看它的源码&#xff0c;发现它仅查找以b-开头属性名的HTML元素&#xff1a;function getScope(element: Element): string | null {return element.getAttributeNames().filter(name > name.startsWith(b-…

CSS- 横向和纵向时间轴

时间轴在展示公司发展信息&#xff0c;服务流程中用的比较多&#xff0c;常见的注册登录有的是通过引导&#xff0c;一步一步的来完成&#xff0c;上面会通过时间轴告诉用户当前在哪一步&#xff0c;公司在关于我们或者发展流程的时候也特别喜欢用时间轴来展示&#xff0c;简单…

互联网巨头基于全球产业链打造ARM CPU

日前&#xff0c;“四十大盗”发布服务器CPU屠龙710。就“四十大盗”公司公布的数据来看&#xff0c;屠龙710是一款非常优秀的ARM芯片&#xff0c;在SPECInt2017基础测试中屠龙710跑分达到440分&#xff0c;超过行业标杆20%。不过&#xff0c;和以前FT、HW的ARM服务器CPU类似&a…

modbus 台达a2_驱控智造未来 台达重磅发布多款工业自动化新品

呼应智能制造发展需求&#xff0c;8月22日&#xff0c;“驱控智造未来-——2019台达工业自动化新品发布会”在北京举行&#xff0c;台达推出PC-Based运动控制器AX864E系列、伺服驱动系统ASDA-B3系列、精巧迷你型矢量控制变频器ME300系列、高功能通用型矢量控制变频器C2000 Plus…

当充气娃娃过于逼真......

1 朋友一生一起走....▼2 总裁爹是被吓到了吗&#xff1f;▼3 我宣布&#xff1a;本届舞林大会&#xff0c;冠军已出&#xff01;▼4 哥哥&#xff0c;我来了&#xff01;▼5 为了防疫&#xff0c;泰国的小朋友们很不容易▼6 这位爸爸真的是非常巧妙了&#xff01;▼7 生…

(转)Android基础类之BaseAdapter

BaseAdapter就Android应用程序中经常用到的基础数据适配器&#xff0c;它的主要用途是将一组数据传到像ListView、Spinner、Gallery及GridView等UI显示组件&#xff0c;它是继承自接口类Adapter&#xff0c;1、Adapter类简介1)、Adapter相关类结构如下图所示&#xff1a;自定义…