我们知道针对客户端的请求,最终都会转换为对 Controller 中的一个 Action 方法的调用,指定的 Action 方法会返回一个 ActionResult 类型的实例来响应该请求,但 ActionResult 类型的实例是如何转换为请求终端最终呈现的页面的呢?这就是我们这里要介绍的。

ActionResult

  每个标准的 Action 方法总会返回一个 ActionResult 类型的对象,该类型是一个抽象类,该类的定义如下:

public abstract class ActionResult
{
public abstract void ExecuteResult(ControllerContext context);
}

  可以看出,该类中仅定义了一个 void ExecuteResult(ControllerContext context) 方法,该方法的作用就是将当前的 ActionResult 处理为能够直接响应给请求终端的内容。

ActionResult 类型的子类

ContentResult

  ContentResult 用于返回一些文本类型的内容给请求终端。该类型的定义如下示:

public class ContentResult : ActionResult
{
//要返回的响应的内容
public string Content { get; set; } //返回内容所使用的的编码规则,如utf-8等
public Encoding ContentEncoding { get; set; } //返回内容的 MIME 类型,如 application/json、text/plain 等
public string ContentType { get; set; } public override void ExecuteResult(ControllerContext context);
}

  下面看看它的的 ExecuteResult 方法是如何实现的。

public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
} HttpResponseBase response = context.HttpContext.Response; //设置 contentType
if (!String.IsNullOrEmpty(ContentType))
{
response.ContentType = ContentType;
}
//设置 Encoding
if (ContentEncoding != null)
{
response.ContentEncoding = ContentEncoding;
}
if (Content != null)
{
//向客户端输出
response.Write(Content);
}
}
EmptyResult

  该类型用于向客户端返回一个空的响应,该类型的 ExecuteResult 方法为一个空方法,即没有任何的实现,在该类型中使用单例的方式定义了一个该类型的静态的实例 Instance,但该属性的访问权限为 internal,即只能在 System.Web.Mvc 程序集下使用,该类型并没有采用完全的单例模式,亦对外公开了 public 的构造函数。

JavaScriptResult

  该类型用于向客户端返回一串 JavaScript 脚本,该类型仅定义的一个 string 类型的属性 Script,用于表示返回的脚本的内容,其 ExecuteResult 方法的实现基本上与 ContentResult 相同,不同在于其响应的 Content-Type 被固定的设置为了 application/x-javascript

JsonResult

  该类型用于向客户端返回一个 Json 类型的响应。该类型的定义如下:

public class JsonResult : ActionResult
{
//属性部分
public Encoding ContentEncoding { get; set; }//返回内容的编码格式 //返回内容的 contentType
public string ContentType { get; set; } //用于序列化返回的对象
public object Data { get; set; } //枚举,设置是否允许 Get 请求
public JsonRequestBehavior JsonRequestBehavior; //序列化生成的字符串的最大长度
public int? MaxJsonLength { get; set; } //序列化允许递归的最大层数
public int? RecursionLimit { get; set; }
}

  其 ExecuteResult 方法的实现如下示:

public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
} //当 JsonRequestBehavior 设置为 DenyGet 时,若当前请求方法为 Get,则抛出异常
if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(MvcResources.JsonRequest_GetNotAllowed);
} HttpResponseBase response = context.HttpContext.Response; //设置 ContentType
if (!String.IsNullOrEmpty(ContentType))
{
response.ContentType = ContentType;
}
else
{
response.ContentType = "application/json";
}
//设置编码
if (ContentEncoding != null)
{
response.ContentEncoding = ContentEncoding;
} //对对象进行序列化处理
if (Data != null)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
if (MaxJsonLength.HasValue)
{
serializer.MaxJsonLength = MaxJsonLength.Value;
}
if (RecursionLimit.HasValue)
{
serializer.RecursionLimit = RecursionLimit.Value;
}
//写入到输出
response.Write(serializer.Serialize(Data));
}}

  在 Controller 下定义有一个 Json 方法,其内部便会创建该类型的实例并返回。

  从上面的代码可以看出,该类型内部是通过 JavaScriptSerializer 类型对类型进行序列化的,且并没有提供对序列化进行进一步设置的选项,如对 DateTime 的格式问题等,如果有特殊的序列化需求,那么,此时,可以使用自定义的序列化工具进行序列化,然后使用 ContentResult 返回序列化后的字符串,并将其 ContentType 设置为 application/json 即可。

RedirectResult

  该类型用于返回一个重定向的响应。该类型的定义如下示:

public class RedirectResult : ActionResult
{
public RedirectResult(string url): this(url, permanent: false) //url:重定向的url,permanent:是否为永久重定向
public RedirectResult(string url,bool permanent)
}

  其 ExecuteResult 方法实现如下示:

public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
} //子 Action 不能执行重定向
if (context.IsChildAction)
{
throw newInvalidOperationException(MvcResources.RedirectAction_CannotRedirectInChildActi;
} //如果Url中不是以 ~ 字符开头,则直接返回,如果是,则对其进行转换,例如:~/home/index,将会被转换为 /home/index
string destinationUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext); //持久化 TempDataDictionary
context.Controller.TempData.Keep(); //永久重定向
if (Permanent)
{
context.HttpContext.Response.RedirectPermanent(destinationUrl, endResponse:false);
}
else
{
//临时重定向
context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
}}
RedirectToRouteResult

  该类型根据传入的路由数据和路由名称生成目标 Url,然后重定向到该 Url,该类型的定义如下:

public class RedirectToRouteResult : ActionResult
{
//构造函数
public RedirectToRouteResult(RouteValueDictionary routeValues):this(null, routeValues); public RedirectToRouteResult(string routeName, RouteValueDictionary routeValues): this(routeName, routeValues, permanent: false); public RedirectToRouteResult(string routeName, RouteValueDictionary routeValues, bool permanent); //属性
public bool Permanent { get; private set; }//是否为永久重定向 //生成 Url 使用的路由的名称
public string RouteName { get; private set; } //生成 Url 使用的路由数据
public RouteValueDictionary RouteValues { get; private set; } }

   其 ExecuteResult 方法的实现基本上同 RedirectResult 的同名方法,唯一不同的就是生成重定向的目标 Url 处不同。后者后调用如下的方法生成目标 Url。

string destinationUrl = UrlHelper.GenerateUrl(
RouteName,
null /* actionName */,
null /* controllerName */,
RouteValues,
Routes, //全局注册的路由表
context.RequestContext,
false /* includeImplicitMvcValues */是否包含隐式的路由数据,即 RequestContext.RouteData.Values
);

  在该方法中首先对路由数据进行合并,首先根据 includeImplicitMvcValues(从上看出这里为false) 决定是否使用隐式的路由数据,然后根据方法是否指定了 controllerNameactionName(从上面可以看出这里全为null),如果指定了这两个参数,且在 RouteValues 中同时设置了 key 为 controlleraction 的项,则前者优先使用。然后调用 RouteCollection 类型的实例(即前面的全局的路由表)的 GetVirtualPath 方法返回一个 VirtualPathData,该类型是对当前使用的 Route 对象及虚拟路径的封装。最后将其虚拟路径采用与 RedirectResult 中同样的方式进行转换,将转换后的路径作为重定向的目标 Url

HttpStatusCodeResult

  该类型用于返回一些 Http 状态码信息。该类型的定义如下示:

public class HttpStatusCodeResult
{
//属性,均可以通过对应的构造函数重载进行设置
//除了指定具体的状态码,亦可以使用框架内预定义的 HttpStatusCode 类型的枚举,其中包括了绝大多数常用的状态码 public int StatusCode { get; private set; }//状态码 //状态短语,如 200 对应的 ok,404 对应的 Not Found 等
public string StatusDescription { get; private set; } }

  其 ExecuteResult 方法的实现如下示:

  

public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
} //设置响应的状态码
context.HttpContext.Response.StatusCode = StatusCode;
if (StatusDescription != null)
{
//设置响应的状态描述短语
context.HttpContext.Response.StatusDescription = StatusDescription;
}
}

  该类型还具有两个子类:HttpNotFoundResultHttpUnauthorizedResult,其内部也仅仅时调用基类的构造函数分别转入状态码 404401,两者都有一个可以设置状态描述短语的重载。

FileResult

  该类型用于返回文件信息给客户端。该类型是一个抽象类,其定义如下示:

  

public class FileResult
{
//属性部分 //文件的 ContentType,MIME 字符串
public string ContentType { get; private set; } // 文件下载时的名称
public string FileDownloadName; //方法部分 //将文件数据写入的响应流
protected abstract void WriteFile(HttpResponseBase response);
}

  其 ExecuteResult 方法的实现如下示:

  

public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
} HttpResponseBase response = context.HttpContext.Response;
response.ContentType = ContentType;//设置文件的 ContentType if (!String.IsNullOrEmpty(FileDownloadName))
{ //这里会对文件的下载名称进行处理
string headerValue = ContentDispositionUtil.GetHeaderValue(FileDownloadName); //设置该头部会使浏览器将其识别为一个下载项
context.HttpContext.Response.AddHeader("Content-Disposition", headerValue);
} WriteFile(response);
}

  下载文件的名称中除去以下的字符外,其余的字符均为非法字符

  

a-z、A-Z、0-9、.、-、_、+、$、&、!、:、~、

  对于非法的字符,便会对其进行 Url 编码处理,详情请参考

  该类型具有三个子类,FileContentResultFilePathResultFileStreamResult,下面对这三个类一一进行说明。

  • FileContentResult

      该类型的定义如下示:
public class FileContentResult : FileResult
{
public FileContentResult(byte[] fileContents, string contentType)
: base(contentType); public byte[] FileContents { get; private set; } protected override void WriteFile(HttpResponseBase response)
{
response.OutputStream.Write(FileContents, 0, FileContents.Length);
}
}

  从上可以看出该类型接受一个字节数组,然后将其写入到响应的输出流中。

  • FilePathResult

      该类型接受一个要返回的文件的路径(相对根目录的路径),然后读取文件并返回,该类的定义如下示:
public class FilePathResult : FileResult
{
public FilePathResult(string fileName, string contentType)
: base(contentType) //文件的名称(含路径)
public string FileName { get; private set; } protected override void WriteFile(HttpResponseBase response)
{
response.TransmitFile(FileName);
}}
  • FileStreamResult

      该类型接受一个文件流,然后将该流写入到响应的输出流中,该类的定义如下示:  
 public class FileStreamResult : FileResult
{
// default buffer size as defined in BufferedStream type
private const int BufferSize = 0x1000; public FileStreamResult(Stream fileStream, string contentType)
: base(contentType) //输出文件流
public Stream FileStream { get; private set; } protected override void WriteFile(HttpResponseBase response)
{
// grab chunks of data and write to the output stream
Stream outputStream = response.OutputStream;
using (FileStream)
{
byte[] buffer = new byte[BufferSize]; while (true)
{
int bytesRead = FileStream.Read(buffer, 0, BufferSize);
if (bytesRead == 0)
{
// no more data
break;
}
outputStream.Write(buffer, 0, bytesRead);
}
}
}

  在 Controller 下定义的 File 方法具有多个不同的重载,根据重载参数的不同在方法内部分别会创建上述三种类型其一的实例。

  至此,**Asp.net mvc ** 中大多数 ActionResult 类型已介绍完毕,还有一个 ViewResult,由于其太重要了,因此,单独放在下一节进行说明。

Asp.net mvc 中View的呈现(一)的更多相关文章

  1. Asp.net mvc 中View 的呈现(二)

    [toc] 上一节介绍了 Asp.net mvc 中除 ViewResult 外的所有的 ActionResult,这一节介绍 ViewResult. ViewResultBase ViewResul ...

  2. ASP.NET MVC 中 View 的设计

    1. 前言  感觉有好长时间没有接触View 了,周末闲来无事,翻翻书桌上的书来回顾回顾ASP.NET MVC中View的相关内容. 2. View概述  View 通过应用程序在Action 中返回 ...

  3. ASP.NET MVC中controller和view相互传值的方式

    ASP.NET MVC中Controller向view传值的方式: ViewBag.ViewData.TempData 单个值的传递 Json 匿名类型 ExpandoObject Cookie Vi ...

  4. 白话学习MVC(九)View的呈现一

    一.概述 本节来看一下ASP.NET MVC[View的呈现]的内容,View的呈现是在Action执行之后进行,Action的执行生成一个ActionResult,[View的呈现]的功能就是:通过 ...

  5. Asp.Net MVC中Controller、Action、View是如何激活调用的

    上篇我们介绍了MVC的路由,知道在注册路由的时候会创建一个MvcHandler将其和Url规则一起放入到了RouteCollection中,之后请求通过UrlRoutingModule,根据当前的UR ...

  6. Asp.net MVC中 Controller 与 View之间的数据传递

    在ASP.NET MVC中,经常会在Controller与View之间传递数据 1.Controller向View中传递数据 (1)使用ViewData["user"] (2)使用 ...

  7. 在ASP.NET MVC中使用区域来方便管理controller和view

    在ASP.NET MVC中使用区域来方便管理controller和view 在mvc架构中,一般在controllers和views中写所有控制器和视图, 太多控制器时候,为了方便管理,想要将关于pe ...

  8. TransactionScope事务处理方法介绍及.NET Core中的注意事项 SQL Server数据库漏洞评估了解一下 预热ASP.NET MVC 的VIEW [AUTOMAPPER]反射自动注册AUTOMAPPER PROFILE

    TransactionScope事务处理方法介绍及.NET Core中的注意事项   作者:依乐祝 原文链接:https://www.cnblogs.com/yilezhu/p/10170712.ht ...

  9. ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则

    ASP.NET MVC 学习笔记-7.自定义配置信息   ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...

随机推荐

  1. TurnipBit开发板掷骰子小游戏DIY教程实例

    转载请以链接形式注明文章来源(MicroPythonQQ技术交流群:157816561,公众号:MicroPython玩家汇) 0x00前言 下面带大家用TurnipBit开发板实现一个简单的小游戏- ...

  2. Android WebView存在跨域访问漏洞(CNVD-2017-36682)介绍及解决

    Android WebView存在跨域访问漏洞(CNVD-2017-36682).攻击者利用该漏洞,可远程获取用户隐私数据(包括手机应用数据.照片.文档等敏感信息),还可窃取用户登录凭证,在受害者毫无 ...

  3. Linux 文本处理工具(grep sed awk )

    ^test: 以test开头; test$: 以test结尾: ^$: 表示空行,不是空格: . :代表且只代表任意一个字符(其他功能:当前目录,加载文件): \ : 代表转义字符,表示特殊字符: * ...

  4. 文件及Linux目录结构

    什么是文件 在linux系统上,所有的资源都是文件,Linux系统下的文件类型包括 普通文件(-) 目录(d) 符号链接(l) 字符设备文件(c) 块设备文件(b) 套接字(s) 命令管道(p) 普通 ...

  5. 【数论·欧拉函数】SDOI2008仪仗队

    题目描述 作为体育委员,C君负责这次运动会仪仗队的训练.仪仗队是由学生组成的N * N的方阵,为了保证队伍在行进中整齐划一,C君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如右图 ...

  6. lodash源码分析之List缓存

    昨日我沿着河岸/漫步到/芦苇弯腰喝水的地方 顺便请烟囱/在天空为我写一封长长的信 潦是潦草了些/而我的心意/则明亮亦如你窗前的烛光/稍有暧昧之处/势所难免/因为风的缘故 --洛夫<因为风的缘故& ...

  7. Zedboard(一)开发环境Vivado

    Vivado是Xilinx(赛灵思)公司出品的开发软件平台,适用于Zedboard开发板. 下面介绍Vivado搭建的过程: 一.注册Xilinx账号.下载安装包 推荐到Xilinx(赛灵思)英文官网 ...

  8. Swagger的简单入门【转载】

    一.Swagger简介 上一篇文章中我们介绍了Spring Boot对Restful的支持,这篇文章我们继续讨论这个话题,不过,我们这里不再讨论Restful API如何实现,而是讨论Restful ...

  9. 12、ABPZero系列教程之拼多多卖家工具 拼团提醒功能登录拼多多实现

    上篇文章已经完成了整个拼多多拼团提醒功能,本篇继续完成拼多多帐号登录,拼多多帐号登录的目的是为了获取拼团商品的SKU和订单号,便于商家备货. 以下是拼多多官方的后台登录,要实现的功能并不是直接在这里登 ...

  10. Django模板中的数字自增

    Django框架的模板提供了{% for %} 标签来进行循环 例如对集合进行循环是比较简单的 {% for row in v1 %} <div>{{row.name}}</div& ...