【轉自】http://www.cnblogs.com/P_Chou/archive/2010/11/26/details-asp-net-mvc-06.html
在上一篇最后,我們進行到了Action調用的“門口”:
|  
               
                1 
                 |  
              
              if 
                  
                (!ActionInvoker.InvokeAction(ControllerContext, actionName)) 
                |  
            
在深入研究調用過程的細節前,先有一個總體的認識是很有幫助的。InvokeAction方法大致是按照這樣的順序進行的:
查找action:MVC內部查找action的方法似乎有點復雜,涉及到一個ActionDescriptor的東西,但是原理上是通過反射,在以后的文章中會有所涉及。
驗證和過濾:眾所周知的IActionFilter和IAuthorizationFilter在這部分生效,它們在真正執行action之前,事實上對於IResultFilter或IExceptionFilter這樣的過濾器是在action執行之后執行的,圖中對於這個沒有畫出。
執行action:真正進入用戶代碼執行,通過反射調用,調用之前還涉及到復雜的參數提供和綁定,在以后的文章中會涉及。
執行結果:ActionResult在這部起到了關鍵的作用,ActionResult有多個派生,其中最為常見的就是ViewResult。ActionResult是前面步驟執行的最終“果實”,通過執行ActionResult的ExecuteResult抽象方法,一個HttpRespose被正確的構造好,准備傳回客戶端。
從ActionResult開始說起
 
        就像上一篇講到的,我們可以在Controller的Execute方法中直接對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,並由框架調用ActionResult的ExecuteResult方法,這類似於設計模式中的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();  
                    
                return 
                  
                File(stream,  
                "text/html" 
                );  
               } 
                |  
            
PartialView()和View():分別返回PartialViewResult和ViewResult
PartialViewResult和ViewResult十分復雜,涉及到視圖,將在以后詳細討論。
Redirect():返回RedirectResult
產生重定向結果,上面已經展示了RedirectResult的實現了。
RedirectToAction(),RedirectToRoute():返回RedirectToRouteResult

