ASP.NET MVC – 关于Action返回结果类型的事儿(上)
本文轉(zhuǎn)自:博客園-文超的技術(shù)博客
一、???????? ASP.NET MVC 1.0 Result 幾何?
?
Action的返回值類型到底有幾個(gè)?咱們來數(shù)數(shù)看。
ASP.NET MVC 1.0 目前一共提供了以下十幾種Action返回結(jié)果類型:
1.?????? ActionResult(base)
2.?????? ContentResult
3.?????? EmptyResult
4.?????? HttpUnauthorizedResult
5.?????? JavaScriptResult
6.?????? JsonResult
7.?????? FileResult (base)
8.?????? FileContentResult
9.?????? FilePathResult
10.?? FileStreamResult
11.?? RedirectResult
12.?? RedirectToRouteResult
13.?? ViewResultBase(base)
14.?? ViewResult
15.?? PartialViewResult
?
一個(gè)列表下來看得人眼花繚亂,因?yàn)榭捎玫?/span>Result很多,接著再瞧瞧類關(guān)系圖以佐辨析:
?
?<圖>
?
如圖中可見,ActionResult可謂人丁興旺,目前膝下有兒9子(如圖中紅色所圈的類),ViewResultBase與FileResult又各有子兩三口,這些兒孫們各司所長(zhǎng)。那么各個(gè) Result 都會(huì)干點(diǎn)啥事兒呢?這個(gè)問題說來話長(zhǎng),不過根據(jù)諸如“虎父無犬子”、“種瓜得瓜,種豆得豆”、“龍生龍,鳳生鳳,老鼠的孩子打地洞”的俗語,孩子們多少?gòu)乃莾哼z傳了點(diǎn)什么,所以要說明它們的才干之前,得先嘮叨嘮叨一下 ActionResult這個(gè)爹,這個(gè)爺,因此這事情還是得先從ActionResult說起。
?
二、樸實(shí)的 ActionResult
?
所有的 Result 都派生自 ActionResult抽象類,因此 ActionResult 作為基類提供了最基礎(chǔ)的功能,ActionResult 是一個(gè)抽象類,其聲明如下:
?
public abstract class ActionResult {
?
??????? public abstract void ExecuteResult(ControllerContext context);
?
}
?
看看普通人民、相貌平平的ActionResult,ActionResult 是個(gè)樸素老百姓,沒啥特長(zhǎng),就一個(gè) ExecuteResult() 抽象方法,這個(gè)ExecuteResult() 抽象方法還啥都不干,遺傳給兒女孫子們讓它們?nèi)グl(fā)揮,那么它的責(zé)任其實(shí)就很明確了,它就是為遺傳作準(zhǔn)備的,繁殖下一代用的,是只公豬種。因?yàn)?/span>ActionResult是所有Result的基類,因此你可以在所有的Action上使用它作為返回值類型,而無需動(dòng)腦筋來明確與返回值相同的類型。
?
二、???????? EmptyResult
?
EmptyResult 是ActionResult 最沒用的兒子,雖然生兒都想生孫仲謀,希望兒子們都是八斗之才,國(guó)家棟梁,可惜第一胎 EmptyResult 就嚴(yán)重破壞了它的夢(mèng)想,看來也只能痛恨自己種子不夠好。咱來瞧瞧這個(gè)沒用的阿斗:
?
//表示一個(gè)啥都不干的結(jié)果,就像 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 遺傳并實(shí)現(xiàn)了ActionResult的ExecuteResult()方法,同時(shí)也遺傳了ActionResult的天真樸實(shí)的想法,也想“還是等下一代吧”,它有點(diǎn)老子的“無為”味道,所以它的ExecuteResult()方法像足了它的老爹,啥也不干。
?
EmptyResult 類使用了簡(jiǎn)單的單例模式,看來這樣不思進(jìn)取的兒子,整個(gè)家族里頭生一個(gè)就夠糟糕了,用廣東人的話說,生它還不如生塊叉燒肉。
?
在Action中,若要返回一個(gè)空的頁(yè)面(不常用),則可如下:
?
public ActionResult Index()
{
return new EmptyResult();
}
?
執(zhí)行后頁(yè)面將缺省返回一個(gè)body為空的HMTL架構(gòu):
<!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>?
三、RedirectResult
EmptyResult的“無為”給ActionResult 的打擊著實(shí)不小,只好將期待落在其他孩子身上,RedirectResult雖然不是什么大才,起碼有一技之長(zhǎng),我們看看它的 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用于執(zhí)行轉(zhuǎn)移。事實(shí)上 RedirectResult 最終調(diào)用了 Response.Redirect() 進(jìn)行轉(zhuǎn)移,所以您可以使用RedirectResult跳轉(zhuǎn)到任意的包括當(dāng)前項(xiàng)目或網(wǎng)絡(luò)上的Url,例如:http://www.cnblogs.com,對(duì)于當(dāng)前項(xiàng)目的路徑,因?yàn)槭褂昧?/span>UrlHelper.Content() 方法獲取目標(biāo)路徑,所以RedirectResult傳遞的Url同時(shí)支持當(dāng)前項(xiàng)目目錄標(biāo)識(shí)符 ~ (即應(yīng)用程序目錄)。
?
四、RedirectToRouteResult
?
RedirectToRouteResult對(duì)于RedirectResult而言,其作用有所局限,僅能轉(zhuǎn)移到路由(路由匹配的結(jié)果最終是一條相對(duì)當(dāng)前項(xiàng)目的Url,例如: /Home/Index ),總的來說與RedirectResult的最終作用是一樣的,都是執(zhí)行轉(zhuǎn)移。RedirectResult較為直接地轉(zhuǎn)移到任意指定的Url,而RedirectToRouteResult則轉(zhuǎn)移到指定的路由(路由匹配所得結(jié)果最終也是一個(gè)的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先通過調(diào)用UrlHelper.GenerateUrl()來獲得路由匹配所得的最終Url,接著的執(zhí)行轉(zhuǎn)移過程與RedirectResult相同。
?
路由配置的過程在Global.asax文件中進(jìn)行,在以MVC模板方式創(chuàng)建的MVC項(xiàng)目中都帶有此文件,可在文件中的MvcApplication類的 RegisterRoutes()方法中進(jìn)行配置路由,該方法缺省的內(nèi)容如下:
?
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 可跳轉(zhuǎn)至任何一條匹配的路由規(guī)則。是以利用路由轉(zhuǎn)移可以跳轉(zhuǎn)到其他控制器的 Action。
?
五、ContentResult
?
ContentResult用于將字符串直接向客戶端輸出。ContentResult的ExecuteResult方法實(shí)際上是調(diào)用了 Response.Write( string… ),輸入并無特別之處,但是在 ASP 時(shí)代,這個(gè)Response.Write() 卻是可以縱橫頁(yè)面。從輸出一個(gè)簡(jiǎn)單的字符串到整個(gè)頁(yè)面,Response.Write()都能勝任,所以ContentResult顯得特別強(qiáng)大:
?
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);
??????????? }
??????? }
?
若沒有提供任何輸出的內(nèi)容,ContentResult呈現(xiàn)的結(jié)果與EmptyResult 是一樣的,都是輸出最基本的<body>標(biāo)記內(nèi)容為空的HTML,若內(nèi)容不為空,則直接輸出這些內(nèi)容(不再輸出其他任何 HTML 代碼),例如:
?
public ActionResult Index()
{
return Content( "a" );
}
?
其頁(yè)面的HTML代碼也將只有一個(gè)字符 a,要補(bǔ)全所有基本標(biāo)記需要在字符串中編寫,例如:
?
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>"
??????????????????????? );
??????? }
?
當(dāng)然不建議使用此方法來輸出頁(yè)面標(biāo)記,ContentResult 用在Ajax中頗為合適,因?yàn)橹灰獌?nèi)容不為空,輸出的字符串與傳送到客戶端的內(nèi)容一致,沒有額外的附加內(nèi)容。
?
事實(shí)上從ContentResult我們可以看到一個(gè)ActionResult其實(shí)并無特別,從前面幾個(gè)Result 來看,其實(shí)不過是Response.Redirect或Response.Write,此外還可以利用二進(jìn)制流Response.OutputStream.Write向客戶端上載文件……據(jù)此我們所以拓展編寫更多針對(duì)實(shí)際意義的Result。例如 XmlResult(文件)、RssResult(跟XmlResult其實(shí)是一樣的)等等。
?
六、JsonResult
?
JsonResult首先將指定的對(duì)象序列化為Json字符串,然后將字符串寫入到HTTP輸出流。撇開對(duì)象序列化為Json字符串這一過程,實(shí)際上與ContentResult其實(shí)是一樣的,因?yàn)?/span>JsonResult與ContentResult都是調(diào)用Response.Write()向HTTP輸出流寫入一些內(nèi)容。所以對(duì)此不再贅述:
?
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
??????????? }
??????? }
?
有個(gè)地方想嘮叨兩句,在代碼中的:
response.ContentType = "application/json";
?
若要直接向頁(yè)面輸出的話需要更改為文本類型,例如 text/html,否則你要以文件形式下載JsonResult的結(jié)果內(nèi)容。不過這對(duì)于將Json用于Ajax而言不會(huì)有什么影響。
?
七、JavaScriptResult
?
道與 JsonResult、ContentResult相同。所以也不贅述,徒費(fèi)唇舌:
?
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);
??????????? }
??????? }
?
八、HttpUnauthorizedResult
?
HttpUnauthorizeResult 設(shè)置客戶端錯(cuò)誤代號(hào)為 401,即未經(jīng)授權(quán)瀏覽狀態(tài),若設(shè)置了Form驗(yàn)證并且客戶端沒有任何身份票據(jù),那么將轉(zhuǎn)跳到指定的頁(yè)面(例如登陸頁(yè)):
?
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;
??????? }
?
可以學(xué)習(xí)HttpUnauthorizeResult來編寫更多同類的返回結(jié)果,例如設(shè)置 Response.StatusCode = 404,這個(gè)是常見的“頁(yè)面未找到”錯(cuò)誤,403 禁止訪問等等。
?
九、FileResult
?
FileResult是一個(gè)抽象類,主要屬性包括聲明內(nèi)容類型信息ContentType 及文件名稱FileDownloadName,客戶端下載工具中將顯示此名稱(如果有指定,ContentType可指定任意非空字符串),如果不指定文件名,ContentType需要正確指定,否則無法識(shí)別待下載的文件類型。
?
FileResult 用作其他向客戶端上載文件的類的基類。
?
十、FilePathResult
?
FilePathResult 繼承自 FileResult,使用 FilePathResult 類向客戶端上載文件只需要給出文件的路徑即可。FilePathResult 將調(diào)用 Response.TransmitFile() 傳輸該文件:
?
protected override void WriteFile(HttpResponseBase response) {
??????????? response.TransmitFile(FileName);
??????? }
?
十一、FileContentResult
?
FileContentResult繼承自 FileResult。
?
FileContentResult 將指定的字節(jié)內(nèi)容寫入二進(jìn)制流(客戶端將以文件形式下載),對(duì)比 FilePathResult 所不同的是 FilePathResult是給出文件路徑,然后將整份文件上載給客戶,而 FileContentResult 則可以傳輸某一個(gè)字節(jié)數(shù)組,例如:
?
public ActionResult Index()
{
return File( System.Text.Encoding.UTF8.GetBytes( "你好嗎" ), "unknown", "t.txt" );
}
?
FileContentResult 使用 Response.OutputStream.Write 輸出內(nèi)容:
protected override void WriteFile(HttpResponseBase response) {
??????????? response.OutputStream.Write(FileContents, 0, FileContents.Length);
}
?
十二、FileStreamResult
?
FileStreamResult 繼承自 FileResult。
?
FileStreamResult 向指定文件流讀取數(shù)據(jù),其他的內(nèi)容與FileContentResult道同。請(qǐng)參考FileContentResult。
?
?
?
?
ViewResult 及其相關(guān)內(nèi)容將在下回討論。
總結(jié)
以上是生活随笔為你收集整理的ASP.NET MVC – 关于Action返回结果类型的事儿(上)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL中各种字段的取值范围
- 下一篇: 我是架构师-基本类型-float