博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[转载]深入理解ASP.NET MVC之ActionResult
阅读量:4579 次
发布时间:2019-06-09

本文共 5309 字,大约阅读时间需要 17 分钟。

Action全局观

在上一篇最后,我们进行到了Action调用的“门口”:

1
if
 
(!ActionInvoker.InvokeAction(ControllerContext, actionName))

在深入研究调用过程的细节前,先有一个总体的认识是很有帮助的。InvokeAction方法大致是按照这样的顺序进行的:

 

查找action:MVC内部查找action的方法似乎有点复杂,涉及到一个ActionDescriptor的东西,但是原理上是通过反射,在以后的文章中会有所涉及。

验证和过滤:众所周知的IActionFilterIAuthorizationFilter在这部分生效,它们在真正执行action之前,事实上对于IResultFilterIExceptionFilter这样的过滤器是在action执行之后执行的,图中对于这个没有画出。

执行action:真正进入用户代码执行,通过反射调用,调用之前还涉及到复杂的参数提供和绑定,在以后的文章中会涉及。

执行结果:ActionResult在这部起到了关键的作用,ActionResult有多个派生,其中最为常见的就是ViewResult。ActionResult是前面步骤执行的最终“果实”,通过执行ActionResult的ExecuteResult抽象方法,一个HttpRespose被正确的构造好,准备传回客户端。

 

从ActionResult开始说起

就像上一篇讲到的,我们可以在ControllerExecute方法中直接对HttpContext.Response操作,绕过action;即便我们走了action这一路,仍然可以在action中像下面这样直接操作Response:

1
2
3
4
5
6
7
8
9
10
11
public
 
class
 
SimpleController : Controller
{
    
public
 
void
 
MyActionMethod()
    
{
        
Response.Write(
"I'll never stop using the <blink>blink</blink> tag"
);
        
// ... or ...
        
Response.Redirect(
"/Some/Other/Url"
);
        
// ... or ...
        
Response.TransmitFile(
@"c:\files\somefile.zip"
);
    
}
}

然而这种方式难以维护,而且难以单元测试,于是MVC框架建议action返回ActionResult,并由框架调用ActionResultExecuteResult方法,这类似于设计模式中的command模式。你会看到这种设计模式在这里的运用实在是十分精辟的。

ActionResult是一个十足的抽象类,抽象到不能再抽象了,它定义了唯一的ExecuteResult方法,参数为一个ControllerContext,其中封装了包括HttpContext在内的许多对象,也是重写这个方法唯一的上下文信息:

1
2
3
4
5
6
7
8
9
namespace
 
System.Web.Mvc {
 
    
public
 
abstract
 
class
 
ActionResult {
 
        
public
 
abstract
 
void
 
ExecuteResult(ControllerContext context);
 
    
}
 
}

MVC内置了很多实用的ActionResult,如下图:

我们可以看个简单的实现类RedirectResult是如何实现ExecuteResult的。在这里我发现了我曾经遇到过的一个异常的原因:Child actions are not allowed to perform redirect actions,意思是在子action不允许重定向。

1
2
3
4
5
6
7
8
9
10
11
12
public
 
override
 
void
 
ExecuteResult(ControllerContext context) {
    
if
 
(context ==
null
) {
        
throw
 
new
 
ArgumentNullException(
"context"
);
    
}
    
if
 
(context.IsChildAction) {
        
throw
 
new
 
InvalidOperationException(MvcResources.RedirectAction_CannotRedirectInChildAction);
    
}
 
    
string
 
destinationUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext);
    
context.Controller.TempData.Keep();
    
context.HttpContext.Response.Redirect(destinationUrl,
false
 
/* endResponse */
);
}

在这段代码中ExecuteResult的实现相对简单的多,事实上像ViewResult的实现就复杂很多,关于ViewResult将在视图中涉及到。

在Controller中有很多辅助方法便于我们在action中返回需要的ActionResult,下面列出:

Content():返回ContentResult

大家都很少用到这个ActionResult,因为它的基本用法是返回文本,也许下面这段代码可以说服你

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public
 
ContentResult RSSFeed()
{
    
Story[] stories = GetAllStories();
// Fetch them from the database or wherever
  
    
// Build the RSS feed document
    
string
 
encoding = Response.ContentEncoding.WebName;
    
XDocument rss =
new
 
XDocument(
new
 
XDeclaration(
"1.0"
, encoding,
"yes"
),
        
new
 
XElement(
"rss"
,
new
 
XAttribute(
"version"
,
"2.0"
),
            
new
 
XElement(
"channel"
,
new
 
XElement(
"title"
,
"Example RSS 2.0 feed"
),
                
from
 
story
in
 
stories
                
select
 
new
 
XElement(
"item"
,
                      
new
 
XElement(
"title"
, story.Title),
                      
new
 
XElement(
"description"
, story.Description),
                      
new
 
XElement(
"link"
, story.Url)
                   
)
            
)
        
)
    
);
     
return
 
Content(rss.ToString(),
"application/rss+xml"
);
}

上面的代码返回了一个RSS。值得注意的是,Content的第二个参数是个contentType(MIME类型,参见),如果不指定这个参数将使用text/html的contentType。

事实上我们的action可以返回一个非ActionResult,MVC在执行action的返回结果前,会确保将返回值转换成一个ActionResult,其中一步,就是对非空和非ActionResult的结果转换成string,并包装成ContentResult:

1
2
3
4
5
6
7
8
9
protected
 
virtual
 
ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor,
object
 
actionReturnValue) {
    
if
 
(actionReturnValue ==
null
) {
        
return
 
new
 
EmptyResult();
    
}
 
    
ActionResult actionResult = (actionReturnValue
as
 
ActionResult) ??
        
new
 
ContentResult { Content = Convert.ToString(actionReturnValue, CultureInfo.InvariantCulture) };
    
return
 
actionResult;
}

 

Json():返回JsonResult

Controller的Json方法能返回一个JsonResult,出于安全性的考虑JsonResult只支持POST方式,设置response.ContentType = "application/json";并利用JavaScriptSerializer序列化对象,并返回给客户端。像下面这样使用JsonResult:

1
2
3
4
5
6
7
8
9
10
11
12
class
 
CityData {
public
 
string
 
city;
public
 
int
 
temperature; }
  
[HttpPost]
public
 
JsonResult WeatherData()
{
    
var
 
citiesArray =
new
[] {
        
new
 
CityData { city =
"London"
, temperature = 68 },
        
new
 
CityData { city =
"Hong Kong"
, temperature = 84 }
    
};
  
    
return
 
Json(citiesArray);
}

JavaScript():返回JavaScriptResult

JavaScript方法实例化一个JavaScriptResult,JavaScriptResult只是简单的设置response.ContentType = "application/x-javascript";

 

File():返回二进制数据或文件

FileResult是个抽象类,File方法的多个重载返回不同的FileResult:

FilePathResult:直接将一个文件发送给客户端

1
2
3
4
5
public
 
FilePathResult DownloadReport()
{
    
string
 
filename =
@"c:\files\somefile.pdf"
;
    
return
 
File(filename,
"application/pdf"
,
"AnnualReport.pdf"
);
}

FileContentResult:返回byte字节给客户端比如图片

1
2
3
4
5
public
 
FileContentResult GetImage(
int
 
productId)
{
    
var
 
product = productsRepository.Products.First(x => x.ProductID == productId);
    
return
 
File(product.ImageData, product.ImageMimeType);
}
" />

FileStreamResult:返回流

public FileStreamResult ProxyExampleDotCom() {     WebClient wc = new WebClient();     Stream stream = wc.OpenRead("http://www.example.com/");     return File(stream, "text/html"); }

 

PartialView()和View():分别返回PartialViewResult和ViewResult

PartialViewResult和ViewResult十分复杂,涉及到视图,将在以后详细讨论。

 

Redirect():返回RedirectResult

产生重定向结果,上面已经展示了RedirectResult的实现了。

 

RedirectToAction(),RedirectToRoute():返回RedirectToRouteResult

RedirectToRouteResult同样是产生跳转的结果,但是它具有“路由表遍历能力”,也就是具有Url outbound的特点,参见

 

原文地址:

转载于:https://www.cnblogs.com/iack/p/3520129.html

你可能感兴趣的文章
【翻译】go memory model
查看>>
深入学习webpack(一)
查看>>
mac 配置nginx 虚拟域名(转载)
查看>>
Xshell6设置字体大小
查看>>
软件开发模式
查看>>
Java总结篇系列:Java多线程(三)
查看>>
【项目管理】PM如何给组员分配任务(转载)
查看>>
Spring中的applicationContext文件详解
查看>>
Zookeeper安装和部署
查看>>
201621123042《java程序设计》第十三次作业
查看>>
CAST 和 CONVERT
查看>>
tr:even 与tr:odd
查看>>
Visual Studio2008 代码度量
查看>>
sql CONVERT函数应用
查看>>
洛谷 P3927 Factorial
查看>>
Wince 6.0获取设备的分辨率 自动设置窗体位置
查看>>
git
查看>>
[转帖]一文读懂 HTTP/2
查看>>
sql游标模板
查看>>
php将图片保存到mysql数据库及从数据库中读取图片的方法源码 转
查看>>