我们知道针对客户端的请求,最终都会转换为对 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. React日常填坑手册(持续更新)

    1.react中自己定义的组件第一个字母一定要大写,如<app />会不显示,<App />才能正常显示. 2.在react中点击事件里面setState时会使this重新定义 ...

  2. ASP.NET Core学习之二 菜鸟踩坑

    对于像我这样没接触过core的人,坑还是比较多的,一些基础配置和以前差别很大,这里做下记录 一.Startup 1.注册服务 // This method gets called by the run ...

  3. sed中引用变量

    sed 中引用变量 eval sed 's/string/$REPLACE/g' file awk 中引用变量 awk 在匹配字符串的时候,有时候需要需要引用变量. $pid= eval " ...

  4. 为什么树莓派不会受到 Spectre 和 Meltdown 攻击

    最近爆出来的 Intel CPU 的底层漏洞可谓是影响巨大,过去20年的电脑都可能会受影响.前几天 Raspberry Pi 的官方 Twitter(@Raspberry_Pi) 转推了这篇文章,通过 ...

  5. [js高手之路] vue系列教程 - 绑定class与行间样式style(6)

    一.绑定class属性的方式 1.通过数组的方式,为元素绑定多个class <style> .red { color:red; /*color:#ff8800;*/ } .bg { bac ...

  6. Heroku 如何上重置 PostgreSQL 数据库

      如果你要在 Heroku 上重置 PostgreSQL 数据库,可以使用以下命令 : $ heroku pg:reset DATABASE $ heroku run php artisan mig ...

  7. Python爬取视频(其实是一篇福利)

    窗外下着小雨,作为单身程序员的我逛着逛着发现一篇好东西,来自知乎 你都用 Python 来做什么?的第一个高亮答案. 到上面去看了看,地址都是明文的,得,赶紧开始吧. 下载流式文件,requests库 ...

  8. Oracle死锁情况

    ORACLE EBS操作某一个FORM界面,或者后台数据库操作某一个表时发现一直出于"假死"状态,可能是该表被某一用户锁定,导致其他用户无法继续操作 复制代码 代码如下: --锁表 ...

  9. Java学习笔记15---instanceof与向下转型

    感冒咳嗽停更了几天,今天恢复更新了. 先来看下instanceof与向下转型的概念: 1.instanceof instanceof是一个二元操作符,用法是:boolean result = a in ...

  10. IE8及其以下浏览器关于圆角表框的问题

    css部分 yuan { border: 2px solid #C0C0C0; -moz-border-radius: 50%; -webkit-border-radius: 50%; border- ...