MVC Action和ActionResult


【轉自】http://www.cnblogs.com/P_Chou/archive/2010/11/26/details-asp-net-mvc-06.html

在上一篇最后,我們進行到了Action調用的“門口”:

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

在深入研究調用過程的細節前,先有一個總體的認識是很有幫助的。InvokeAction方法大致是按照這樣的順序進行的:

image

 

查找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

Action Result Helper Method Description
ViewResult View Returns a view as a webpage
PartialViewResult PartialView Returns a partial view. A partial view has the same role as a user control. A partial view will be rendered within a main view
RedirectResult Redirect Redirects to another action method based on its URL
RedirectToRouteResult RedirectToAction

RedirectToRoute

Redirects to another action method
ContentResult Content Returns a user defined content type
JsonResult Json Returns a serialized JSON object
JavaScriptResult JavaScript Returns a script that can be executed on the client
FileResult File Returns binary output to write to the response
FileStreamResult File Returns a file stream
FilePathResult File Returns a file path
FileContentResult File Returns the content of a file
EmptyResult (None) Used with void action methods which doesn’t return a value
HttpStatusCodeResult   Returns an HTTP Status code
HttpNotFoundResult   Returns HTTP Not Found
HttpUnauthorizedResult   Return HTTP Not Authorized

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Thanks to: http://msdn.microsoft.com/en-us/library/dd410269.aspx

我們可以看個簡單的實現類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類型,參見www.iana.org/assignments/media-types),如果不指定這個參數將使用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);
}
?
1
< img   src = "<%: Url.Action(" GetImage", "Products",  new { Model.ProductID }) %>" />

FileStreamResult:返回流

?
1
2
3
4
5
6
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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM