我们知道针对客户端的请求,最终都会转换为对 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. 找回Git中丢失的Commit

    在使用Git的过程中,有时候会因为一些误操作,比如reset.rebase.merge等.特别是在Commit之后又执行了git reset --hard HEAD强制回滚本地记录以及文件到服务器版本 ...

  2. angular4.0单个标签不能同时使用ngFor和ngIf

    这个问题估计是ng4严格了语法规范的原因. 介于这篇太短,附上图助助兴致 解决办法: <div *ngFor="表达式"> <ng-container *ngIf ...

  3. Python面向对象篇(1)-类和对象

    面向对象编程 1.编程范式   我们写代码的目的是什么?就是为了能够让计算机识别我们所写的代码并完成我们的需求,规范点说,就是通过编程,用特定的语法+数据结构+特殊算法来让计算机执行特定的功能,实现一 ...

  4. 3、UML建模技术

    UML(统一建模语言)是当前软件开发中使用最为广泛的建模技术之一,通过使用UML可以构造软件系统的需求模型(用例模型).静态模型.动态模型和架构模型 UML类图 1.UML类图图示 在UML中,类使用 ...

  5. 2018年手机应用UI设计趋势预测

    用户需求瞬息万变,而手机软件UI设计为适应变化的用户需求,也相应的发生着变化.但是,这并不意味着用户需求和UI设计趋势就是无迹可寻的.事实上,根据前几年的手机app界面设计变化的特点,尤其是2017年 ...

  6. express学习之session

    最新版本的express没有包含session依赖项,需要我们自己配置并安装. 安装方法:npm install express-session 使用:var session = require('e ...

  7. .NET使用Office Open XML导出超大数量数据到 Excel

    我相信很多人在做项目的都碰到过Excel数据导出的需求,我从最开始使用最原始的HTML拼接(将需要导出的数据拼接成TABLE标签)到后来happy的使用开源的NPOI, EPPlus等开源组件导出EX ...

  8. Lua和C的语法差别

    没有main函数 Lua是脚本语言,没有固定入口的main函数.当lua解析器解析某个lua代码文件时,lua解析器一样一行的解析lua脚本. print("Hello lua") ...

  9. MIME---multipart类型

    1.3  multipart类型 MIME邮件中各种不同类型的内容是分段存储的,各个段的排列方式.位置信息都通过Content-Type域的multipart类型来定义.multipart类型主要有三 ...

  10. SpringBoot+Mybatis+Freemark 最简单的例子

    springboot-sample 实现最简单的 SpringBoot + Mybatis + Freemarker 网页增删改查功能,适合新接触 Java 和 SpringBoot 的同学参考 代码 ...