《Pro ASP.NET MVC 3 Framework》学习笔记之二十四【Controllers和Actions】-attach

重定向到文本URL(Redirecting to a Literal URL)

最基本的重定向浏览器方式就是调用Redirect方法,该方法会返回一个RedirectResult类的实例。

例如:public RedirectResult Redirect() {return RedirectPermanent(“/Example/Index”); }当然我们可以根据自己的喜好来选择Redirect方法的重载,可以添加一个bool类型的参数指定是否永久重定向(不指定则是暂时重定向)

进行单元测试:

View Code

[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的实例。

如下所示:

View Code

public RedirectToRouteResult Redirect() 
{
return RedirectToRoute(new {
controller = "Example",
action = "Index",
ID = "MyID"
});
}

RedirectToRoute方法发起的是一个暂时重定向(temporary redirection),如果要实现永久重定向可以使用RedirectToRoutePermanent方法。两个方法都接收一个匿名类型参数传递给路由系统生成一个URL。下面是单元测试:

View Code

[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部分


Index


The day is: @ViewBag.Date //只对当前的View有效,所以在About里面是null值
The day is:@TempData["Date"]//只读取TempData["Date"]的值


//About部分


Index


The day is: @ViewBag.Date
The day is:@TempData["Date"]


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。

单元测试如下:

View Code

[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时。如下所示:

View Code

        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; }
}

//生成结果

"First example story" description="This is the first example story"
link="/Story/1" />
"Second example story" description="This is the second example story"
link="/Story/2" />
"Third example story" description="This is the third example story"
link="/Story/3" />

返回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); }生成的格式数据像这样,如下所示:

View Code

[{"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辅助方法不同的重载版本来为我们自动创建。下面依次说明:
发送一个文件:

View Code

        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

View Code

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 : RssActionResult
{
public RssActionResult(string title, IEnumerable data,
Func formatter)
{

Title = title;
DataItems = data;
Formatter = formatter;
}

public IEnumerable DataItems { get; set; }
public Func 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。我们定义两个类是为了能够创建返回类型是RssActionResult的action方法。

好了,今天的笔记就到这里,关于Controllers and Actions 章的笔记到这里也结束了。
晚安!

发表评论

电子邮件地址不会被公开。 必填项已用*标注