重定向到文本URL(Redirecting to a Literal URL)
最基本的重定向瀏覽器方式就是調用Redirect方法,該方法會返回一個RedirectResult類的實例。
例如:public RedirectResult Redirect() {return RedirectPermanent("/Example/Index"); }當然我們可以根據自己的喜好來選擇Redirect方法的重載,可以添加一個bool類型的參數指定是否永久重定向(不指定則是暫時重定向)
進行單元測試:

[TestMethod]
public void RedirectTest()
{
// Arrange - create the controller
ExampleController target = new ExampleController();
// Act - call the action method
RedirectResult result = target.Redirect();
// Assert - check the result
Assert.IsFalse(result.Permanent);
Assert.AreEqual("/Example/Index", result.Url);
}
重定向到路由系統URL(Redirecting to a Routing System URL)
如果我們正在重定向用戶到應用程序一個不同的部分,我們需要確保發送的URL是在URL架構里面驗證通過的。使用文本URL重定向的問題是對路由架構的任何的改變會導致遍歷整個代碼並更新URL。為了避免這個這問題,我們可以使用路由系統RedirectToRoute方法生成驗證的URL,即創建RedirectToRouteResult的實例。
如下所示:

public RedirectToRouteResult Redirect()
{
return RedirectToRoute(new {
controller = "Example",
action = "Index",
ID = "MyID"
});
}
RedirectToRoute方法發起的是一個暫時重定向(temporary redirection),如果要實現永久重定向可以使用RedirectToRoutePermanent方法。兩個方法都接收一個匿名類型參數傳遞給路由系統生成一個URL。下面是單元測試:

[TestMethod]
public void RedirectValueTest()
{
// Arrange - create the controller
ExampleController target = new ExampleController();
// Act - call the action method
RedirectToRouteResult result = target.Redirect();
// Assert - check the result
Assert.IsFalse(result.Permanent);
Assert.AreEqual("Example", result.RouteValues["controller"]);
Assert.AreEqual("Index", result.RouteValues["action"]);
Assert.AreEqual("MyID", result.RouteValues["ID"]);
}
重定向到Action方法(Redirecting to an Action Method)
使用RedirectToAction方法能夠很優雅的重定向到一個Action方法,這個方法僅僅是對RedirectToRoute方法的封裝,讓我們指定action方法的值並且controller不需要創建一個匿名類型。如public RedirectToRouteResult Redirect() {return RedirectToAction("Index"); }如果我們指定一個action方法,那么就假定了是在當前controller里面的額action方法。如果我們想重定向到其他的controller,我們需要提供一個controller名。像這樣:return RedirectToAction("Index", "MyController"); 當然還有其他的重載方法,讓我們能夠簡便的編碼。
注意:我們傳入的action參數或controller參數在它們被傳遞給路由系統之前是不會驗證的,所以我們要確保目標controller和action方法是存在的。
重定向會引起瀏覽器提交一整個新的HTTP請求,這樣意味着我們無法訪問原始請求的細節。如果我們想將數據從一個請求傳遞到下一個,可以使用TempData功能。
TempData類似與Session,不同的是TempData在被讀取以后會被標記刪除,並且當請求被處理的時候被移除。這是一個針對我們想在整個重定向過程中保持短期數據的非常完美的安排。下面我們通過一個例子來驗證TempData的值只被讀取一次:
public class ExampleController : Controller
{
public ViewResult Index()
{
TempData["Message"] = "Hello";
TempData["Date"] = DateTime.Now;
ViewBag.Date = DateTime.Now;
//DateTime time = (DateTime)TempData.Peek("Date");
//TempData.Keep("Message");
return View();
}
public ViewResult About()
{
return View();
}
}
接着添加兩個View,分別是Index和About,代碼如下:
//Index部分
<h2>
Index</h2>
The day is: @ViewBag.Date //只對當前的View有效,所以在About里面是null值
The day is:@TempData["Date"]//只讀取TempData["Date"]的值
<p />
//About部分
<h2>
Index</h2>
The day is: @ViewBag.Date
The day is:@TempData["Date"]
<p />
The message is: @TempData["Message"]//注意在Index里面是沒有讀取TempData["Message"]的值,但是Index和About都讀取TempData["Date"]的值。
//這樣做的目的是為了證明TempData里面的值在一次請求里面只能讀取一次。所以,當我們先瀏覽Index,然后請求About,因為TempData["Date"]的值已經在Index請求時被讀取了,
//后面在About里面就讀不到了,但是TempData["Message"]的值能夠在About里面顯示出來,因為它之前沒有被讀取過一次。
運行程序就一目了然了,如下所示:
如果我們想讀取TempData的值但是又不讓它被刪除,可以使用TempData.Peek("Date")方法。如果想在保持一次TempData里面的值,可以使用TempData.Keep("Date").
您可以將上面的controller里面的兩行注釋的代碼取消注釋並在Index里面加上讀取TempData["Message"]的代碼,運行程序就明白了。
返回文本數據(Returning Text Data)
除了HTML,我們可能需要讓我們的程序生成很多其他的基於文本的數據格式來響應請求。這些文本的格式包含:XML,JSON,CSV,一般文本。MVC框架明確對JSON格式提供了支持,對這些文本數據類型,我們使用ContentResult作為action的返回值類型。如下所示:
public ContentResult Index()
{
string message = "This is plain text";
return Content(message, "text/plain", Encoding.Default);
}
我們通過Content方法創建ContentResult,並獲取三個參數:1.發送的文本數據;2.響應的HTTP content-type header.3.編碼格式。
我們可以忽略最后兩個參數,在MVC框架假定數據是HTML(content type為text/html)情況下,它會查詢一種瀏覽器聲明支持的編碼格式。
比如,我們僅僅返回一個文本可以這樣:return Content("This is plain text");
實際上,如果我們返回任何一個不是ActionResult類型的對象,MVC框架會試圖序列化數據成一個字符串並作為HTML發送給瀏覽器。例如:
public object Test(){return "This is plain text";},此時運行程序可以看到瀏覽器顯示:This is plain text。
單元測試如下:

[TestMethod]
public void ContentTest()
{
// Arrange - create the controller
ExampleController target = new ExampleController();
// Act - call the action method
ContentResult result = target.Index();
// Assert - check the result
Assert.AreEqual("text/plain", result.ContentType);
Assert.AreEqual("This is plain text", result.Content);
}
返回XML數據(Returning XML Data )
從action方法返回XML數據是非常簡單的,特別是當我們使用LINQ2XML和XDocument API從對象創建XML時。如下所示:

public ContentResult XMLData()
{
StoryLink[] stories = GetAllStores();
XElement data = new XElement("StoryList", stories.Select(e =>
{
return new XElement("Story",
new XAttribute("title", e.Title),
new XAttribute("description", e.Description),
new XAttribute("link", e.Url));
}));
return Content(data.ToString(), "text/xml");
}
private StoryLink[] GetAllStores(){ ...}
public class StoryLink
{
public string Title {get; set;}
public string Description { get; set; }
public string Url { get; set; }
}
//生成結果
<StoryList>
<Story title="First example story" description="This is the first example story"
link="/Story/1" />
<Story title="Second example story" description="This is the second example story"
link="/Story/2" />
<Story title="Third example story" description="This is the third example story"
link="/Story/3" />
</StoryList>
返回JSON數據(Returning JSON Data)
最近幾年,在web程序中對XML的使用在慢慢減少,而是更加喜歡用JSON格式(JavaScript Object Notation)傳輸數據,JSON是一個輕量級的基於文本格式,用來描述分層數據結構的,JSON是有效的javascript代碼,這意味着被所有的主流瀏覽器支持,這也是讓JSON相對於XML更加有影響更加簡便。在MVC框架里面內置了JsonResult類,讓我們能夠序列化.net對象為json格式。通過Json方法可以創建返回JsonResult結果類型。如:
[HttpPost]
public JsonResult JsonData() {StoryLink[] stories = GetAllStories(); return Json(stories); }生成的格式數據像這樣,如下所示:

[{"Title":"First example story",
"Description":"This is the first example story","Url":"/Story/1"},
{"Title":"Second example story",
"Description":"This is the second example story","Url":"/Story/2"},
{"Title":"Third example story",
"Description":"This is the third example story","Url":"/Story/3"}]
注意:為了更加安全的緣故,JsonResult對象只對HTTP Post請求生成一個響應,這是為了阻止數據因為第三方法跨站點請求被暴露。推薦在返回JsonResult類型的action方法上面加上[HttpPost]作為一個提示,盡管這不是必須的,后面的章節會解釋這樣做的原因。
返回文件和二進制數據(Returning Files and Binary Data )
FileResult是一個抽象的基類,MVC框架自帶了三個實現的子類,如下:
1.FilePathResult:直接從服務器發送一個文件
2.FileContentResult:發送內存里面字節數組的內容
3.FileStreamResult:發送一個已經打開的System.IO.Stream對象的內容
我們不用擔心具體用哪一個,因為MVC提供了一個Controller.File輔助方法不同的重載版本來為我們自動創建。下面依次說明:
發送一個文件:

public FileResult AnnualReport()
{
string filename = @"c:\AnnualReport.pdf";
string contentType = "application/pdf";
string downloadName = "AnnualReport2011.pdf";
return File(filename, contentType, downloadName);
}
執行時,會彈出一個保存文件的對話框。對於File方法有三個參數:filename,contentType,fileDownloadName,前面兩個是必須的。
發送字節數組,發送Stream對象都是在File的第一個參數進行變化,所以就不在贅述了。
返回錯誤和HTTP代碼
最后一個MVC內置的ActionResult類就是我們用來發送具體錯誤信息和HTTP結果的代碼到客戶端。很多應用程序對這個功能不是必須使用的,因為MVC會自動創建。但是,它們能夠幫助我們更加直接的控制發送到客戶端的響應。
發送一個指定的HTTP結果代碼:使用HttpStatusCodeResult類,
如public HttpStatusCodeResult StatusCode() { return new HttpStatusCodeResult(404, "URL cannot be serviced"); }
發送一個404結果:public HttpStatusCodeResult StatusCode() {return HttpNotFound(); }
發送一個401結果:public HttpStatusCodeResult StatusCode() {return new HttpUnauthorizedResult(); }
創建一個自定義的ActionResult
內置的action result已經能夠充分滿足大多數情形和應用程序,但我們可以在特殊情況下自己定義action result。下面的部分通過生成一個RSS文檔來演示自定義action result

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Xml.Linq;
namespace ControllersAndActions.Infrastructure
{
public abstract class RssActionResult : ActionResult
{
}
public class RssActionResult<T> : RssActionResult
{
public RssActionResult(string title, IEnumerable<T> data,
Func<T, XElement> formatter)
{
Title = title;
DataItems = data;
Formatter = formatter;
}
public IEnumerable<T> DataItems { get; set; }
public Func<T, XElement> Formatter { get; set; }
public string Title { get; set; }
public override void ExecuteResult(ControllerContext context)
{
HttpResponseBase response = context.HttpContext.Response;
// set the content type of the response
response.ContentType = "application/rss+xml";
// get the RSS content
string rss = GenerateXML(response.ContentEncoding.WebName);
// write the content to the client
response.Write(rss);
}
private string GenerateXML(string encoding)
{
XDocument rss = new XDocument(new XDeclaration("1.0", encoding, "yes"),
new XElement("rss", new XAttribute("version", "2.0"),
new XElement("channel", new XElement("title", Title),
DataItems.Select(e => Formatter(e)))));
return rss.ToString();
}
}
}
實際上,我們定義了兩個類,一個是RssActionResult抽象類(ActionResult的子類),另一個是從RssActionResult派生的RssActionResult<T>。我們定義兩個類是為了能夠創建返回類型是RssActionResult的action方法。
好了,今天的筆記就到這里,關於Controllers and Actions 章的筆記到這里也結束了。
晚安!