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

生成输出(Producing Output)

在controller完成处理请求之后,通常需要生成一个响应。当我们通过直接实现IController接口创建一个简单的controller时,我们需要对处理请求的每一个方面负责,包括创建对客户端的响应。如果我们想发送一个HTML响应,那我们必须创建并且集合HTML数据,然后使用Response.Write方法将数据发送到客户端。类似地,如果我们想重定向用户到其他的URL,可以使用Response.Redirect方法。

下面是具体的实例:

View Code

using System.Web.Mvc;using System.Web.Routing;

namespace ControllersAndActions.Controllers{public class BasicController : IController    {public void Execute(RequestContext requestContext)        {string controller = (string)requestContext.RouteData.Values["controller"];string action = (string)requestContext.RouteData.Values["action"];            requestContext.HttpContext.Response.Write(string.Format("Controller:{0},Action:{1}", controller, action));//..or..            requestContext.HttpContext.Response.Redirect("/Some/Other/Url");        }    }}

 当然在DerivedController里面也可以使用Response属性,但是这样使用会带来一些问题:

①controller类必须包含HTML或URL架构的详细信息,这会让维护和可读性非常差
②对于这种直接响应输出的controller进行单元测试也很困难
③处理好每一个这种方法响应的细节是非常乏味和易出错的

幸运的是,MVC框架有一个解决这些问题非常好的功能——Action Results。下面会介绍Action Result的概念并展示它用来生成controller的响应的不同方式:

理解Action Results

MVC框架使用Action Results将"说明我们意图(stating our intentions)"和"执行我们的意图(executing our intentions)"分开(我也不是很明白这两个概念,如果路过的朋友清楚,请留言指正,谢谢)

不直接使用Response对象,而是返回一个从ActionResult类派生的对象,用这个对象来描述我们想要controller响应什么,比如呈现一个视图或重定向到另外的URL,再或者action方法等等。ps:MVC里面的Action Result系统采用了设计模式里面的命令模式

MVC框架接收从action方法返回的ActionResult对象,并调用定义在ActionResult类里面ExecuteResult方法,接着,对ActionResult(这是一个抽象类)的实现为我们处理Response对象并生成符合我们意图的输出。下面是一段关于RedirectResult类的源码:

View Code

    public class RedirectResult : ActionResult {

[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification = "Response.Redirect() takes its URI as a string parameter.")]public RedirectResult(string url)            : this(url, permanent: false) {        }

[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification = "Response.Redirect() takes its URI as a string parameter.")]public RedirectResult(string url, bool permanent) {if (String.IsNullOrEmpty(url)) {throw new ArgumentException(MvcResources.Common_NullOrEmpty, "url");            }

Permanent = permanent;            Url = url;        }

public bool Permanent {get;private set;        }

[SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Response.Redirect() takes its URI as a string parameter.")]public string Url {get;private set;        }

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();

if (Permanent) {                context.HttpContext.Response.RedirectPermanent(destinationUrl, endResponse: false);            }else {                context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);            }        }

}

当我们创建一个RedirectResult类的实例,我们传入了一个想要跳转的URL参数,还可以传入一个可选的参数:是否永久重定向(默认值是false)。ExecuteResult将会在Action方法执行完时被MVC调用,通过ControllerContext对象获取用于查询的Response对象,然后调用RedirectPermanent或Redirect方法。

我们在DerivedController类里面添加一个方法如下:

View Code

    public class DerivedController : Controller    {public ActionResult Index()        {            ViewBag.Message = "Hello from DerivedController Index Method";return View("Index");        }

public ActionResult Redirect()        {return new RedirectResult("/Derived/Index");        }    }

这时运行程序输入/Derived/Redirect会跳转到Index页面。当然可以简便的写成return Redirect("/Derived/Redirect")。

此外,MVC框架内置了许多action result类型,都是从ActionResult类派生的,这样能够方便我们在action方法里面选择具体的返回类型,比如我们要呈现到View,可以选择ViewResult作为action方法的返回值。更多关于从ActionResult派生的类型请参考这里。下面会展示怎样使用这些返回值类型以及怎样创建和使用自定义的ActionResult。

通过呈现View返回HTML

最常见的来自action方法的响应就是生成HTML并发送给浏览器。当使用了ActionResult这套体系,为了生成HTML,我们要创建一个指定了要呈现视图的ViewResult类的实例。如下所示:例子指定了Homepage的视图

View Code

    public class ExampleController : Controller    {public ViewResult Index()        {return View("Homepage");        }    }

ps:我们可以显示的创建ViewResult对象(return new ViewResult { ViewName = "Homepage" };)这是一种完美的,可接受的方式。但是我们偏向于使用Controller类提供的的帮助方法来简化代码。

当MVC框架调用ViewResult对象的ExecuteResult时,就会开始对我们指定的View进行搜索,如果我们使用了Area,则搜索的顺序如下:

•  /Areas/<AreaName>/Views/<ControllerName>/<ViewName>.aspx
•  /Areas/<AreaName>/Views/<ControllerName>/<ViewName>.ascx
•  /Areas/<AreaName>/Views/Shared/<ViewName>.aspx
•  /Areas/<AreaName>/Views/Shared/<ViewName>.ascx
•  /Areas/<AreaName>/Views/<ControllerName>/<ViewName>.cshtml
•  /Areas/<AreaName>/Views/<ControllerName>/<ViewName>.vbhtml
•  /Areas/<AreaName>/Views/Shared/<ViewName>.cshtml
•  /Areas/<AreaName>/Views/Shared/<ViewName>.vbhtml

只要有一个View找到,整个搜索就会停止,并将找的View呈现给客户端。如果没有用Area,则没有前面的/Areas/<AreaName>.

ps:MVC按照这种顺序搜索Viiew也体现了"约定胜于配置(convention over configuration)"的思想

下面可以进行如下单元测试:

View Code

[TestMethod] public void ViewSelectionTest() { // Arrange -创建一个Controller    ExampleController target = new ExampleController();  

// Act - 调用Action方法ActionResult result = target.Index(); 

// Assert -检查结果    Assert.AreEqual("Homepage", result.ViewName); } //如果测试一个Action方法选择的是默认的View,如下所示:public ViewResult Index() { return View(); } //这时可以做这样的判断Assert.AreEqual("", result.ViewName); 

当我们调用View方法不指定具体的视图的名字时,会呈现默认的View,这个默认的View名就是该Action方法名,如public ViewResult Index(){return View();},默认的视图就是Index。MVC就会按照约定的顺序搜寻Index视图,搜寻哪一个视图是由RouteData.Values["action"]的值决定的。View方法很多重载,大家可以自己看看智能提示。

通过路径来指定呈现的View

这种命名约定的方法非常方便和简捷的,但是它限制了我们所能呈现的一些View。如果我们要呈现一个具体的view,可以提供一个显示的路径来绕开搜索的阶段。下面是一个这样的例子:

View Code

using System.Web.Mvc; 

namespace ControllersAndActions.Controllers { public class ExampleController : Controller        { public ViewResult Index() { return View("~/Views/Other/Index.cshtml");         }     } } 

当我们像这样指定一个view时,指定的路径必须以"/"或"~/"并且包含扩展名(如.cshtml)。当然我推荐这样来使用,因为我们可以有其他的方法来达到同样的效果。

从Action方法向View传递数据

我们常常需要从action方法向view传递数据,MVC框架提供了多种方式来实现。

1.提供一个View Model对象
我们能够将一个对象作为View方法的参数传递给view,例如:

View Code

//Controller部分namespace ControllersAndActions.Controllers{public class ExampleController : Controller    {public ViewResult Index()        {            DateTime date = DateTime.Now;return View(date);        }    }}//View部分@{     ViewBag.Title = "Index"; } 

<h2>Index</h2> 

The day is: @(((DateTime)Model).DayOfWeek) 

上面的View是一个没有类型或者说若类型的view。这个view不知道关于view model对象的任何信息,并且将它作为了object对象的实例进行处理。为了得到DayofWeek属性的值,我们需要将object对象的实例强转为DateTime.这样做能够实现我们效果,但是却让view变得凌乱。

我们可以通过创建强类型的view来改进,即在view里面指定view model对象的类型,只需要添加一句代码:@model Datetime,如下所示:

@model DateTime @{     ViewBag.Title = "Index"; } <h2>Index</h2> The day is: @Model.DayOfWeek 

可以发现,指定view model的类型通过@model关键字(注意这里的"m"是小写),当我们读取view model对象属性值时,使用@Model关键字(这里的"M"是大写哦),使用强类型的视图不仅让我们的view变得整洁,而且方便我们编码,因为会对属性的智能提示。
下面进行单元测试:

View Code

[TestMethod] public void ViewSelectionTest() { // Arrange - create the controller     ExampleController target = new ExampleController();  

// Act - call the action method     ActionResult result = target.Index(); 

// Assert - check the result     Assert.AreEqual("Hello, World", result.ViewData.Model); } 

使用ViewBag传递数据

在前面就已经使用过ViewBag,这个功能让我们能够随意地定义一个动态对象的属性,并能够在view里面访问,如下所示:

View Code

//Controller部分public ViewResult Index() {     ViewBag.Message = "Hello";     ViewBag.Date = DateTime.Now; return View(); } 

//View部分@{     ViewBag.Title = "Index"; } 

<h2>Index</h2> 

The day is: @ViewBag.Date.DayOfWeek <p /> The message is: @ViewBag.Message 

相对于使用view model对象,ViewBag能够很容易地发送多个对象到view。如果我们被限制只能使用view models,那么为实现相同的效果,我们需要创建一个新的类型具有string和DateTime两个类型的成员。使用动态对象,我们可以输入在视图中调用的方法和属性序列。

像这样的:The day is: @ViewBag.Date.DayOfWeek.Blah.Blah。VS不会为动态对象提供任何智能提示包括ViewBag,所以如果有什么错误,只有等到view呈现才知道。当然我们完全可以同时使用view model对象和ViewBag,各尽其用。

使用ViewData传递数据

ViewData是在MVC3之前的版本中出现的,主要的功能类似于ViewBag,但ViewData是使用ViewDataDictionary类实现的而不是一个动态类型,ViewDataDictionary类像一个常规的键/值对的集合,并通过Controller类的ViewData属性访问。如下所示:

View Code

//Controller部分public ViewResult Index() { 

ViewData["Message"] = "Hello";     ViewData["Date"] = DateTime.Now; 

return View(); } 

//View部分@{     ViewBag.Title = "Index"; } 

<h2>Index</h2> 

The day is: @(((DateTime)ViewData["Date"]).DayOfWeek) <p /> The message is: @ViewData["Message"] 

ps:我们能看到需要对object对象进行类型转换,现在有了ViewBag以后,推荐使用ViewBag,但是尽量使用强类型view和view models是非常明智的。

执行重定向(Performing Redirections)

一个来自action方法常见的结果不是直接生成任何输出,而是重定向用户到其他的URL。大多数时候,这个重定向的URL是应用程序里面另外一个action方法,用来生成我们想给用户看到的输出。

POST/REDIRECT/GET模式:最频繁使用的重定向是在处理POST请求的action方法里面,正如我们前面提到的,POST请求是要改变应用程序状态的,如果我们处理一个请求后接着仅仅是返回HTML,我们就冒了用户第二次点击重载按钮和重提交按钮所导致的不可预期的异常的风险。为了避免这个问题,我们可以遵循POST/Redirect/GET模式。在这个模式中,接收一个请求,处理它并重定向到浏览器以至于浏览器为其他的URL制造一个GET请求。GET请求不应该修改我们应用程序的状态,所以任何无意的关于请求的重提交不会引起任何问题。

当我们执行一个重定向,我们就发送了两个HTTP编码中的一个到浏览器:

①发送HTTP 302状态编码,代表暂时重定向。这是最频繁使用的重定向类型,MVC3里面也是这样,当我们遵循Post/Redirect/Get模式时,这时发送的代码就是我们想要的。
②发生HTTP 301状态编码,表示永久重定向。这要慎重使用,因为它指示HTTP编码的接收者不会再次请求原始的URL并使用包含了重定向编码的新URL。如果你有疑虑,那么最好使用暂时重定向,发送302编码。

不早了,今天的笔记就到这里!笔记里面不对的地方还请路过的朋友指正,谢谢!
晚安

时间: 2024-07-29 00:00:52

《Pro ASP.NET MVC 3 Framework》学习笔记之二十三【Controllers和Actions】的相关文章

ASP.NET MVC 3 Framework学习笔记之Model Templates

.使用模板化的视图Helpers(Using Templated View Helpers) 模版化视图helpers的创意就是它们更加灵活.我们不用自己去指定应该用什么HTML元素来呈现一个模型的属性,MVC自己会搞定,在我们更新了视图模型时,也不用手动的更新视图.下面是一个例子:  代码如下 复制代码 //在Models里面添加Persons.cs using System; using System.Collections.Generic; using System.Linq; using

《Pro ASP.NET MVC 3 Framework》学习笔记目录

<Pro ASP.NET MVC 3 Framework>简介: 作者: Adam Freeman 和 Steven Sanderson 出版社: Apress; New 平装: 820页 语种: 英语 ISBN: 1430234040 声明:笔记里面按我自己的理解翻译了大部分内容,写这个笔记的目的:为了方便自己查阅,也为园友提供学习的方便. 我无意侵犯作者的任何权利,仅仅为了自己学习.也希望路过的朋友不要用于任何商业目的. 第一部分 ASP.NET MVC3介绍   <Pro ASP.

《Pro ASP.NET MVC 3 Framework》学习笔记之一【MVC的历程,优点,HelloWorld】

序论:asp.net mvc出现已经有两三年的时间了(2009开始1.0版本),但是这么方面的中文学习资料仍然非常少,特别是asp.net mvc3,几乎就没有中文的学习书籍.在英文的书籍中有两本是非常经典的mvc3教程:<Professional ASP.NET MVC 3>--作者:Jon Galloway , Phil Haack, Brad Wilson , K. Scott Allen和<Pro ASP.NET MVC 3 Framework>--作者:Steven Sa

ASP.NET MVC Web API 学习笔记----HttpClient简介

  1. HttpClient简单介绍  依稀还记得那个时候用WebClient,HttpWebRequest来发送一个请求,现在ASP.NET MVC4中自带了一个类HttpClient,用于接收HttpResponseMessage和发送HttpRequestMesssage. 问题在于既然WebClient,HttpWebRequest可以完成相应的功能,为什么还要使用HttpClient类,.NET Framework中既然提出了这样一个类肯定是有其特别之处的,这里罗列几个不同之处: (

ASP.NET MVC Web API 学习笔记---联系人增删改查

本章节简单介绍一下使用ASP.NET MVC Web API 做增删改查.目前很多Http服务还是通过REST或者类似RESP的模型来进行数据操作的.下面我们通过创建一个简单的Web API来管理联系人          说明:为了方便数据不使用真正的数据库,而是通过内存数据模拟    1.       Web API中包含的方法 Action HTTP method Relative URI GetAllContact GET /api/contact GetContact GET /api/

《Pro ASP.NET MVC 3 Framework》学习笔记之九【Ninject的使用-下】

接着上次的Ninject的笔记,如果你是初次路过,可以先看看我前面的笔记. 一,创建依赖链(Chains of Dependency) 当我们向Ninject请求创建一个类型时,Ninject会去检查该类型和其他类型之间的耦合关系.如果有额外的依赖,Ninject也会解析它们并创建我们需要的所有类的实例.为了进一步说明,我们创建一个新的接口和一个实现该接口的类.请注意我们的例子是跟前面的笔记衔接的,所以如果你打算跟着一起操作的话,最好能够去看看前面的笔记. 创建一个IDiscountHelper

《Pro ASP.NET MVC 3 Framework》学习笔记之四【领域模型介绍】

主题:应用领域驱动开发(Applying Domain-Driven Development) Domain Model是MVC程序的"心脏",其他的一切,包括Controllers和Views仅仅是用来跟Domain Model交互的一种方式,ASP.NET MVC并没有限制使用在Domain Model上面的技术,我们可以自由的选择跟.net framework交互的技术,并且这样的选择是非常多的.不仅如此,ASP.NET MVC为我们提供了基础的架构和约定来帮助Domain Mo

《Pro ASP.NET MVC 3 Framework》学习笔记之五【依赖注入及ninject工具使用】

一,创建松耦合的组件 1."分解关注点"是MVC模式里面一个非常重要的特性.我们想要在应用程序里面创建的组件尽可能的独立,这样我们就能管理比较少的依赖关系.理想情况下,每个组件都是孤立的,不知道其他组件的存在,处理应用程序的其他领域仅仅通过抽象接口,这就是所谓的松耦合,它让我们的应用程序更加容易测试和修改.通过一个简单的例子可以帮助我们理解,假如我们想写一个发邮件的组件,暂且就把这个组件命名为MyEmailSender,接着我们实现一个接口,这个接口定义了所有需要发送邮件的功能,也暂且

《Pro ASP.NET MVC 3 Framework》学习笔记之三十五 【部署】

准备要部署的应用程序 在正式进入部署MVC程序到IIS之前,会介绍一些关于应用程序迁移到生产环境之前探测错误以及一旦进入生产环境最大化性能的技术.同时也会展示关于流线型部署过程的有用的功能.   检测视图错误 Razor视图会在服务器需要的时候编译而不是在VS里面生成项目时编译,正常情况下,探测视图编译错误的方式是系统的访问每一个action,从而让每一个view都能够呈现.这显然是非常乏味而且不会一直成功的技术,特别是在基于不同的model状态呈现不同的view的时候.我们可以启用一个特别的项

《Pro ASP.NET MVC 3 Framework》学习笔记之十五【示例项目SportsStore】

绑定Shopping Cart 定义购物车Cart的实体,购物车是我们程序业务领域的一个部分,所以在我们领域模型(Domain Model)里面添加一个cart的实体是合理的.在SportsStore.Domain的Entities文件夹下添加一个Cart的实体类,如下所示: View Code public class Cart {private List<CartLine> lineCollection = new List<CartLine>();//添加 public vo