我们知道针对客户端的请求,最终都会转换为对 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. js写基础insertAfter()方法

    //DOM没有提供insertAfter()方法 function insertAfter(newElement, targetElement){ var parent = targetElement ...

  2. permission denied for window type 2003

    今天在做系统悬浮窗的时候出现权限拒绝,类型是2003,这里要说下,做系统悬浮窗需要申请权限,6.0以上的 还需要动态申请下,这里我就不过多描述了, 我在申请完权限后仍然不行,这里主要是出现在了这个类型 ...

  3. sql优化原则与技巧

    加快sql查询是非常重要的技巧,简单来说加快sql查询的方式有以下几种:一.索引的引用 1.索引一般可以加速数据的检索速度,加速表与表之间的链接,提高性能,所以在对海量数据进行处理时,考虑到信息量比较 ...

  4. Flask 框架 简介

    一.Flask介绍 Flask是一个基于Werkzeug,Jinja 2 轻量级的web开发框架, 使用Python开发, 上手简单. 二.安装Flask 三.第一个Flask程序 1.编写app.p ...

  5. php mysql语句预编译(preparestatement)

    预处理语句用于执行多个相同的 SQL 语句,并且执行效率更高. 预处理语句的工作原理如下: 预处理:创建 SQL 语句模板并发送到数据库.预留的值使用参数 "?" 标记 .例如: ...

  6. android自定义文本框,后面带清空按钮

    android常见的带清空按钮的文本框,获得焦点时如果有内容则显示,否则不显示 package com.qc.health.weight; import com.qc.health.R; import ...

  7. 【Tarjan】洛谷P3379 Tarjan求LCA

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  8. React Native随笔——警告处理方法(持续更新)

    一.警告propTypes was defined as an instance property on commonTabar. Use a static property to define pr ...

  9. (๑•̀ㅂ•́)و✧随笔总目录ヾ(≧▽≦*)o

    SSM整合进阶篇 日常手记 开源博客My Blog系列 短信接口攻击事件 读书笔记 SSM整合优化篇 SSM整合基础篇 SSM整合进阶篇 Spring+SpringMVC+MyBatis+easyUI ...

  10. Mysql与PostgreSQL小pk

    普通增删改查 表结构及数据都极其简单,命名也及其不讲究.均为默认配置,mysql表默认InnoDB引擎.表x包含三个int字段a b c,100W条数据均a=1 b=2 c=3 sql语句 建表: c ...