Action的返回值类型到底有几个?我们来数数看。javascript
ASP.NET MVC 1.0 目前一共提供了如下十几种Action返回结果类型:html
1. ActionResult(base)java
2. ContentResultjson
3. EmptyResult数组
4. HttpUnauthorizedResult浏览器
5. JavaScriptResult安全
6. JsonResult网络
7. FileResult (base)架构
8. FileContentResultapp
9. FilePathResult
10. FileStreamResult
11. RedirectResult
12. RedirectToRouteResult
13. ViewResultBase (base)
14. ViewResult
15. PartialViewResult
一个列表下来看得人眼花缭乱,由于可用的Result不少,接着再瞧瞧类关系图以佐辨析:
如图中可见,ActionResult可谓人丁兴旺,目前膝下有儿9子(如图中红色所圈的类),ViewResultBase与FileResult又各有子两三口,这些儿孙们各司所长。那么各个 Result 都会干点啥事儿呢?这个问题说来话长,不过根据诸如“虎父无犬子”、“种瓜得瓜,种豆得豆”、“龙生龙,凤生凤,老鼠的孩子打地洞”的俗语,孩子们多少从他爹那儿遗传了点什么,因此要说明它们的才干以前,得先唠叨唠叨一下 ActionResult这个爹,这个爷,所以这事情仍是得先从ActionResult提及。
全部的 Result 都派生自 ActionResult抽象类,所以 ActionResult 做为基类提供了最基础的功能,ActionResult 是一个抽象类,其声明以下:
public abstract class ActionResult {
public abstract void ExecuteResult(ControllerContext context);
}
看看普通人民、相貌平平的ActionResult,ActionResult 是个朴素老百姓,没啥特长,就一个 ExecuteResult() 抽象方法,这个ExecuteResult() 抽象方法还啥都不干,遗传给儿女孙子们让它们去发挥,那么它的责任其实就很明确了,它就是为遗传做准备的,繁殖下一代用的,是只公猪种。由于ActionResult是全部Result的基类,所以你能够在全部的Action上使用它做为返回值类型,而无需动脑筋来明确与返回值相同的类型。
EmptyResult 是ActionResult 最没用的儿子,虽然生儿都想生孙仲谋,但愿儿子们都是八斗之才,国家栋梁,惋惜第一胎 EmptyResult 就严重破坏了它的梦想,看来也只能痛恨本身种子不够好。咱来瞧瞧这个没用的阿斗:
//表示一个啥都不干的结果,就像 controller action 返回 null
public class EmptyResult : ActionResult {
private static readonly EmptyResult _singleton = new EmptyResult();
internal static EmptyResult Instance {
get {
return _singleton;
}
}
public override void ExecuteResult(ControllerContext context) {
}
}
EmptyResult 遗传并实现了ActionResult的ExecuteResult()方法,同时也遗传了ActionResult的天真朴实的想法,也想“仍是等下一代吧”,它有点老子的“无为”味道,因此它的ExecuteResult()方法像足了它的老爹,啥也不干。
EmptyResult 类使用了简单的单例模式,看来这样不思进取的儿子,整个家族里头生一个就够糟糕了,用广东人的话说,生它还不如生块叉烧肉。
在Action中,若要返回一个空的页面(不经常使用),则可以下:
public ActionResult Index()
{
return new EmptyResult();
}
执行后页面将缺省返回一个body为空的HMTL架构:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head>
<meta content="text/html; charset=gb2312" http-equiv=content-type></head>
<body></body></html>
EmptyResult的“无为”给ActionResult 的打击着实不小,只好将期待落在其余孩子身上,RedirectResult虽然不是什么大才,起码有一技之长,咱们看看它的 ExecuteResult() 方法:
public override void ExecuteResult(ControllerContext context) {
if (context == null) {
throw new ArgumentNullException("context");
}
string destinationUrl = UrlHelper.Content(Url, context.HttpContext);
context.HttpContext.Response.Redirect(destinationUrl, false /* endResponse */);
}
RedirectResult用于执行转移。事实上 RedirectResult 最终调用了 Response.Redirect() 进行转移,因此您可使用RedirectResult跳转到任意的包括当前项目或网络上的Url,例如:http://www.cnblogs.com,对于当前项目的路径,由于使用了UrlHelper.Content() 方法获取目标路径,因此RedirectResult传递的Url同时支持当前项目目录标识符 ~ (即应用程序目录)。
RedirectToRouteResult对于RedirectResult而言,其做用有所局限,仅能转移到路由(路由匹配的结果最终是一条相对当前项目的Url,例如: /Home/Index ),总的来讲与RedirectResult的最终做用是同样的,都是执行转移。RedirectResult较为直接地转移到任意指定的Url,而RedirectToRouteResult则转移到指定的路由(路由匹配所得结果最终也是一个的Url):
public override void ExecuteResult(ControllerContext context) {
if (context == null) {
throw new ArgumentNullException("context");
}
string destinationUrl = UrlHelper.GenerateUrl(RouteName, null /* actionName */, null /* controllerName */, RouteValues, Routes, context.RequestContext, false /* includeImplicitMvcValues */);
if (String.IsNullOrEmpty(destinationUrl)) {
throw new InvalidOperationException(MvcResources.ActionRedirectResult_NoRouteMatched);
}
context.HttpContext.Response.Redirect(destinationUrl, false /* endResponse */);
}
RedirectToRouteResult先经过调用UrlHelper.GenerateUrl()来得到路由匹配所得的最终Url,接着的执行转移过程与RedirectResult相同。
路由配置的过程在Global.asax文件中进行,在以MVC模板方式建立的MVC项目中都带有此文件,可在文件中的MvcApplication类的 RegisterRoutes()方法中进行配置路由,该方法缺省的内容以下:
public static void RegisterRoutes( RouteCollection routes )
{
routes.IgnoreRoute( "{resource}.axd/{*pathInfo}" );
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
RedirectToRouteResult 可跳转至任何一条匹配的路由规则。是以利用路由转移能够跳转到其余控制器的 Action。
ContentResult用于将字符串直接向客户端输出。ContentResult的ExecuteResult方法其实是调用了 Response.Write( string… ),输入并没有特别之处,可是在 ASP 时代,这个Response.Write() 倒是能够纵横页面。从输出一个简单的字符串到整个页面,Response.Write()都能胜任,因此ContentResult显得特别强大:
public override void ExecuteResult(ControllerContext context) {
if (context == null) {
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType)) {
response.ContentType = ContentType;
}
if (ContentEncoding != null) {
response.ContentEncoding = ContentEncoding;
}
if (Content != null) {
response.Write(Content);
}
}
若没有提供任何输出的内容,ContentResult呈现的结果与EmptyResult 是同样的,都是输出最基本的<body>标记内容为空的HTML,若内容不为空,则直接输出这些内容(再也不输出其余任何 HTML 代码),例如:
public ActionResult Index()
{
return Content( "a" );
}
其页面的HTML代码也将只有一个字符 a,要补全全部基本标记须要在字符串中编写,例如:
public ActionResult Index()
{
return Content( "<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 Transitional//EN"">" +
"<html>" +
"<head><meta content=""text/html; charset=gb2312"" http-equiv=content-type></head>" +
"<body>" +
"abc" +
"</body>" +
"</html>"
);
}
固然不建议使用此方法来输出页面标记,ContentResult 用在Ajax中颇为合适,由于只要内容不为空,输出的字符串与传送到客户端的内容一致,没有额外的附加内容。
事实上从ContentResult咱们能够看到一个ActionResult其实并没有特别,从前面几个Result 来看,其实不过是Response.Redirect或Response.Write,此外还能够利用二进制流Response.OutputStream.Write向客户端上载文件……据此咱们因此拓展编写更多针对实际意义的Result。例如 XmlResult(文件)、RssResult(跟XmlResult实际上是同样的)等等。
JsonResult首先将指定的对象序列化为Json字符串,而后将字符串写入到HTTP输出流。撇开对象序列化为Json字符串这一过程,实际上与ContentResult实际上是同样的,由于JsonResult与ContentResult都是调用Response.Write()向HTTP输出流写入一些内容。因此对此再也不赘述:
public override void ExecuteResult(ControllerContext context) {
if (context == null) {
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType)) {
response.ContentType = ContentType;
}
else {
response.ContentType = "application/json";
}
if (ContentEncoding != null) {
response.ContentEncoding = ContentEncoding;
}
if (Data != null) {
// The JavaScriptSerializer type was marked as obsolete prior to .NET Framework 3.5 SP1
#pragma warning disable 0618
JavaScriptSerializer serializer = new JavaScriptSerializer();
response.Write(serializer.Serialize(Data));
#pragma warning restore 0618
}
}
有个地方想唠叨两句,在代码中的:
response.ContentType = "application/json";
若要直接向页面输出的话须要更改成文本类型,例如 text/html,不然你要以文件形式下载JsonResult的结果内容。不过这对于将Json用于Ajax而言不会有什么影响。
道与 JsonResult、ContentResult相同。因此也不赘述,徒费唇舌:
public override void ExecuteResult(ControllerContext context) {
if (context == null) {
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = "application/x-javascript";
if (Script != null) {
response.Write(Script);
}
}
8、HttpUnauthorizedResult
HttpUnauthorizeResult 设置客户端错误代号为 401,即未经受权浏览状态,若设置了Form验证而且客户端没有任何身份票据,那么将转跳到指定的页面(例如登录页):
public override void ExecuteResult(ControllerContext context) {
if (context == null) {
throw new ArgumentNullException("context");
}
// 401 is the HTTP status code for unauthorized access - setting this
// will cause the active authentication module to execute its default
// unauthorized handler
context.HttpContext.Response.StatusCode = 401;
}
能够学习HttpUnauthorizeResult来编写更多同类的返回结果,例如设置 Response.StatusCode = 404,这个是常见的“页面未找到”错误,403 禁止访问等等。
FileResult是一个抽象类,主要属性包括声明内容类型信息ContentType 及文件名称FileDownloadName,客户端下载工具中将显示此名称(若是有指定,ContentType可指定任意非空字符串),若是不指定文件名,ContentType须要正确指定,不然没法识别待下载的文件类型。
FileResult 用做其余向客户端上载文件的类的基类。
FilePathResult 继承自 FileResult,使用 FilePathResult 类向客户端上载文件只须要给出文件的路径便可。FilePathResult 将调用 Response.TransmitFile() 传输该文件:
protected override void WriteFile(HttpResponseBase response) {
response.TransmitFile(FileName);
}
FileContentResult继承自 FileResult。
FileContentResult 将指定的字节内容写入二进制流(客户端将以文件形式下载),对比 FilePathResult 所不一样的是 FilePathResult是给出文件路径,而后将整份文件上载给客户,而 FileContentResult 则能够传输某一个字节数组,例如:
public ActionResult Index()
{
return File( System.Text.Encoding.UTF8.GetBytes( "你好吗" ), "unknown", "t.txt" );
}
FileContentResult 使用 Response.OutputStream.Write 输出内容:
protected override void WriteFile(HttpResponseBase response) {
response.OutputStream.Write(FileContents, 0, FileContents.Length);
}
FileStreamResult 继承自 FileResult。
FileStreamResult 向指定文件流读取数据,其余的内容与FileContentResult道同。请参考FileContentResult。
代码 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.IO;
namespace MVC.Controllers
{
/// <summary>
/// Controller 类必须以字符串 "Controller" 作类名称的结尾,字符串 Controller 以前的字符串为 Controller 的名称,类中的方法名为 Action 的名称
/// </summary>
public class ControllerDemoController : Controller
{
// [NonAction] - 当前方法仅为普通方法,不解析为 Action
// [AcceptVerbs(HttpVerbs.Post)] - 声明 Action 所对应的 http 方法
/// <summary>
/// Action 能够没有返回值
/// </summary>
public void Void()
{
Response.Write(string.Format("<span style='color: red'>{0}</span>", "void"));
}
/// <summary>
/// 若是 Action 要有返回值的话,其类型必须是 ActionResult
/// EmptyResult - 空结果
/// </summary>
public ActionResult EmptyResult()
{
Response.Write(string.Format("<span style='color: red'>{0}</span>", "EmptyResult"));
return new EmptyResult();
}
/// <summary>
/// Controller.Redirect() - 转向一个指定的 url 地址
/// 返回类型为 RedirectResult
/// </summary>
public ActionResult RedirectResult()
{
return base.Redirect("~/ControllerDemo/ContentResult");
}
/// <summary>
/// Controller.RedirectToAction() - 转向到指定的 Action
/// 返回类型为 RedirectToRouteResult
/// </summary>
public ActionResult RedirectToRouteResult()
{
return base.RedirectToAction("ContentResult");
}
/// <summary>
/// Controller.Json() - 将指定的对象以 JSON 格式输出出来
/// 返回类型为 JsonResult
/// </summary>
public ActionResult JsonResult(string name)
{
System.Threading.Thread.Sleep(1000);
var jsonObj = new { Name = name, Age = new Random().Next(20, 31) };
return base.Json(jsonObj);
}
/// <summary>
/// Controller.JavaScript() - 输出一段指定的 JavaScript 脚本
/// 返回类型为 JavaScriptResult
/// </summary>
public ActionResult JavaScriptResult()
{
return base.JavaScript("alert('JavaScriptResult')");
}
/// <summary>
/// Controller.Content() - 输出一段指定的内容
/// 返回类型为 ContentResult
/// </summary>
public ActionResult ContentResult()
{
string contentString = string.Format("<span style='color: red'>{0}</span>", "ContentResult");
return base.Content(contentString);
}
/// <summary>
/// Controller.File() - 输出一个文件(字节数组)
/// 返回类型为 FileContentResult
/// </summary>
public ActionResult FileContentResult()
{
FileStream fs = new FileStream(Request.PhysicalApplicationPath + "Content/loading.gif", FileMode.Open);
int length = (int)fs.Length;
byte[] buffer = new byte[length];
fs.Read(buffer, 0, length);
fs.Close();
return base.File(buffer, "image/gif");
}
// <summary>
/// Controller.File() - 输出一个文件(文件地址)
/// 返回类型为 FileContentResult
/// </summary>
public ActionResult FilePathResult()
{
var path = Request.PhysicalApplicationPath + "Content/loading.gif";
return base.File(path, "image/gif");
}
// <summary>
/// Controller.File() - 输出一个文件(文件流)
/// 返回类型为 FileContentResult
/// </summary>
public ActionResult FileStreamResult()
{
FileStream fs = new FileStream(Request.PhysicalApplicationPath + "Content/loading.gif", FileMode.Open);
return base.File(fs, @"image/gif");
}
/// <summary>
/// HttpUnauthorizedResult - 响应给客户端错误代码 401(未经受权浏览状态),若是程序启用了 Forms 验证,而且客户端没有任何身份票据,则会跳转到指定的登陆页
/// </summary>
public ActionResult HttpUnauthorizedResult()
{
return new HttpUnauthorizedResult();
}
/// <summary>
/// Controller.PartialView() - 寻找 View ,即 .ascx 文件
/// 返回类型为 PartialViewResult
/// </summary>
public ActionResult PartialViewResult()
{
return base.PartialView();
}
/// <summary>
/// Controller.View() - 寻找 View ,即 .aspx 文件
/// 返回类型为 ViewResult
/// </summary>
public ActionResult ViewResult()
{
// 若是没有指定 View 名称,则寻找与 Action 名称相同的 View
return base.View();
}
/// <summary>
/// 用于演示处理 JSON 的
/// </summary>
public ActionResult JsonDemo()
{
return View();
}
/// <summary>
/// 用于演示上传文件的
/// </summary>
public ActionResult UploadDemo()
{
return View();
}
/// <summary>
/// 用于演示 Get 方式调用 Action
/// id 是根据路由过来的;param1和param2是根据参数过来的
/// </summary>
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetDemo(int id, string param1, string param2)
{
ViewData["ID"] = id;
ViewData["Param1"] = param1;
ViewData["Param2"] = param2;
return View();
}
/// <summary>
/// 用于演示 Post 方式调用 Action
/// </summary>
/// <remarks>
/// 能够为参数添加声明,如:[Bind(Include = "xxx")] - 只绑定指定的属性(参数),多个用逗号隔开
/// [Bind(Exclude = "xxx")] - 不绑定指定的属性(参数),多个用逗号隔开
/// [Bind] 声明一样能够做用于 class 上
/// </remarks>
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult PostDemo(FormCollection fc)
{
ViewData["Param1"] = fc["param1"];
ViewData["Param2"] = fc["param2"];
// 也能够用 Request.Form 方式获取 post 过来的参数
// Request.Form 内的参数也会映射到同名参数。例如,也可用以下方式获取参数
// public ActionResult PostDemo(string param1, string param2)
return View("GetDemo");
}
/// <summary>
/// 处理上传文件的 Action
/// </summary>
/// <param name="file1">与传过来的 file 类型的 input 的 name 相对应</param>
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult UploadFile(HttpPostedFileBase file1)
{
// Request.Files - 获取须要上传的文件。固然,其也会自动映射到同名参数
// HttpPostedFileBase hpfb = Request.Files[0] as HttpPostedFileBase;
string targetPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "Upload", Path.GetFileName(file1.FileName));
file1.SaveAs(targetPath);
return View("UploadDemo");
}
}
}
在ASP.NET Core MVC中,咱们有时候须要在Controller的Action中直接输出数据到Response.Body这个Stream流中,例如若是咱们要输出一个很大的文件到客户端浏览器让用户下载,那么在Controller的Action中用Response.Body这个Stream流,来逐步发送文件数据到客户端浏览器是最好的办法。
可是我今天在ASP.NET Core MVC的Controller的Action中使用Response.Body输出数据到客户端浏览器的时候遇到了个问题,咱们来看看下面这个Controller:
using Microsoft.AspNetCore.Mvc; using System.IO; namespace AspNetCoreActionFilter.Controllers { public class HomeController : Controller { /// <summary> /// 显示一个网页供测试 /// </summary> public IActionResult Index() { return View(); } /// <summary> /// 调用此Action,采用Response.Body的Stream流发送字符串数据到客户端浏览器 /// </summary> /// <returns>返回一个IActionResult对象</returns> public IActionResult WriteResponseWithReturn() { Response.ContentType = "text/html"; using (StreamWriter sw = new StreamWriter(Response.Body)) { sw.Write("Write a string to response in WriteResponseWithReturn!"); } return null; } } }
能够看到这个HomeController很是简单就两个Action:
结果我在浏览器(IE浏览器)上输入Url地址"Home/WriteResponseWithReturn"返回的结果以下:
能够看到页面报错了,可是返回的Http状态码是200,表示Http响应又是成功的。。。总之来讲HomeController的WriteResponseWithReturn这个Action执行出问题了,咱们输出到Response.Body的Stream流中的文字,没能正确发送到客户端浏览器。
接着我修改了下HomeController的WriteResponseWithReturn这个Action,让其返回一个EmptyResult对象,以下所示:
using Microsoft.AspNetCore.Mvc; using System.IO; namespace AspNetCoreActionFilter.Controllers { public class HomeController : Controller { /// <summary> /// 显示一个网页供测试 /// </summary> public IActionResult Index() { return View(); } /// <summary> /// 调用此Action,采用Response.Body的Stream流发送字符串数据到客户端浏览器 /// </summary> /// <returns>返回一个IActionResult对象</returns> public IActionResult WriteResponseWithReturn() { Response.ContentType = "text/html"; using (StreamWriter sw = new StreamWriter(Response.Body)) { sw.Write("Write a string to response in WriteResponseWithReturn!"); } return new EmptyResult(); } } }
再尝试在浏览器上输入Url地址"Home/WriteResponseWithReturn",此次返回的结果以下:
此次浏览器成功将咱们输出到Response.Body的Stream流中的文字显示出来了,并且返回的Http状态码也是200
接着我在HomeController中又定义了一个Action叫WriteResponseWithoutReturn,仍是输出一段字符串数据到Response.Body的Stream流中,发送给客户端浏览器,可是WriteResponseWithoutReturn这个Action方法没有返回类型,返回类型为void,以下所示:
using Microsoft.AspNetCore.Mvc; using System.IO; namespace AspNetCoreActionFilter.Controllers { public class HomeController : Controller { /// <summary> /// 显示一个网页供测试 /// </summary> public IActionResult Index() { return View(); } /// <summary> /// 调用此Action,采用Response.Body的Stream流发送字符串数据到客户端浏览器 /// </summary> /// <returns>返回一个IActionResult对象</returns> public IActionResult WriteResponseWithReturn() { Response.ContentType = "text/html"; using (StreamWriter sw = new StreamWriter(Response.Body)) { sw.Write("Write a string to response in WriteResponseWithReturn!"); } return new EmptyResult(); } /// <summary> /// 调用此Action,采用Response.Body的Stream流发送字符串数据到客户端浏览器 /// </summary> public void WriteResponseWithoutReturn() { Response.ContentType = "text/html"; using (StreamWriter sw = new StreamWriter(Response.Body)) { sw.Write("Write a string to response in WriteResponseWithoutReturn!"); } } } }
而后我在客户端浏览器上输入Url地址"Home/WriteResponseWithoutReturn"来访问咱们新加的这个Action方法,此次返回的结果以下:
能够看到若是Action返回的类型是void,那么输出到Response.Body的Stream流中的数据也能够成功发送到客户端浏览器。
小结:
因此当咱们在ASP.NET Core MVC中Controller的Action中,要用Response.Body的Stream流输出数据到客户端时,有两种办法能够确保数据能够成功发送到客户端: