[ASP.NET
MVC Maverick Road] 09 – Controller und Aktion (1)
Wir wissen, dass in MVC jede Anfrage zur Verarbeitung an den Controller weitergeleitet wird. Der Controller steht in engem Zusammenhang mit der Anfrage. Er enthält die logische Verarbeitung der Anfrage, kann das Modell bedienen und die Ansicht auswählen, die dem Benutzer präsentiert werden soll. Der Logikcode für Geschäfts- und Daten-, Schnittstellen- und Hilfsklassenbibliotheken wird im Allgemeinen nicht platziert der Controller.
Controller und Action haben viel Inhalt, daher habe ich sie in zwei Artikel unterteilt, und sie können auch in drei Artikel unterteilt werden. In diesem Artikel werden die Implementierung des Controllers, die Erfassung von Statusdaten durch den Controller und die Datenübertragung von ActionResult und Action vorgestellt. Später werden die Controller-Factory, Action Invoker und einige weitere erweiterte Funktionen vorgestellt, an die noch nicht gedacht wurde werden gelernt.
Inhalt dieses Artikels
IController-Schnittstelle erben
In den vorherigen Artikeln dieser Serie wurde der von uns hinzugefügte Controller geerbt die abstrakte gemeinsame Klasse der Klasse System.Web.Mvc.Controller (bitte beachten Sie: Controller (oder Controller) und Controller-Klasse haben in diesem Artikel zwei unterschiedliche Bedeutungen. Bitte verstehen Sie beim Lesen dieses Artikels den Kontext). Die abstrakte Controller-Klasse kapselt viele sehr praktische Funktionen, sodass Entwickler nicht selbst repetitive und umständliche Verarbeitungscodes schreiben müssen.
Wenn Sie die gekapselte abstrakte Controller-Klasse nicht verwenden, können wir auch Ihren eigenen Controller erstellen, indem wir die IController-Schnittstelle implementieren. Es gibt nur eine Exctute-Methode in der IController-Schnittstelle:
public interface IController { void Execute(RequestContext requestContext); }
Die Controller-Schnittstelle befindet sich unter dem System.Web.Mvc-Namespace und ist eine Schnittstelle mit einer sehr einfachen Struktur.
Wenn eine Anfrage an eine Controller-Klasse gesendet wird, die die IController-Schnittstelle implementiert (das Routing-System kann den Controller über die angeforderte URL finden), wird die Execute-Methode aufgerufen.
Als Nächstes erstellen wir eine leere MVC-Anwendung, fügen im Ordner „Controller“ eine Klasse hinzu, die IController implementiert, und führen einige einfache Vorgänge wie folgt aus:
using System.Web.Mvc; using System.Web.Routing; namespace MvcApplication1.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)); } } }
Führen Sie die Anwendung aus, die URL lautet befindet sich unter /Basic/Index (Sie können Index in einen beliebigen anderen Fragmentnamen ändern). Das Ergebnis lautet wie folgt:
implementiert die IController-Klasse und MVC erkennt sie ist eine Controller-Klasse, und die entsprechende Anforderung wird entsprechend dem Controller-Namen zur Verarbeitung an diese Klasse übergeben.
Aber für eine etwas komplexe Anwendung erfordert es viel Arbeit, die IController-Schnittstelle selbst zu implementieren, und wir machen das selten. Dadurch verstehen wir die Funktionsweise des Controllers besser. Die gesamte Anforderungsverarbeitung im Controller beginnt mit der Execute-Methode.
Erben Sie die abstrakte Controller-Klasse
MVC ermöglicht es uns, sie frei anzupassen und zu erweitern. Wie oben erwähnt, können Sie beispielsweise die IController-Schnittstelle implementieren, um Antworten darauf zu erstellen Verschiedene Anfragen bearbeiten und Ergebnisse generieren. Wenn Ihnen die Action-Methode nicht gefällt oder Ihnen die View egal ist, können Sie selbst einen besseren, schnelleren und eleganteren Controller schreiben, der die Anfrage bearbeitet. Wie bereits erwähnt, erfordert die eigene Implementierung der IController-Schnittstelle jedoch viel Arbeit. Das Wichtigste ist, dass die Robustheit des Codes ohne langfristige praktische Tests nicht gewährleistet werden kann. Die System.Web.Mvc.Controller-Klasse des MVC-Frameworks bietet genügend praktische Funktionen, um uns die Verarbeitung von Anforderungen und die Rückgabe von Ergebnissen zu erleichtern.
Wir haben den Controller, der die Controller-Klasse erbt, viele Male verwendet und haben ein gewisses Verständnis davon. Er bietet die folgenden Hauptfunktionen:
Aktionsmethode: ein Controller, der das Verhalten unterteilt In mehrere Methoden wird normalerweise eine Methode einer Anforderung zugeordnet, und die von der Anforderung übergebenen Daten können über Methodenparameter abgerufen werden.
ActionResult: Sie können ein Objekt zurückgeben, das das Ausführungsergebnis der Action-Methode beschreibt. Der Vorteil besteht darin, dass Sie einfach das entsprechende Rückgabeobjekt angeben können, wenn Sie das Ergebnis zurückgeben möchten, und Sie müssen sich keine Sorgen machen darüber, wie man es ausführt und das Ergebnis generiert.
Filter: Durch C#-Funktionen wird die Verarbeitung eines bestimmten Verhaltens (z. B. Autorisierung und Überprüfung) gekapselt, was die Wiederverwendung zwischen mehreren Controller- und Aktionsmethoden erleichtert.
Wenn Sie also keine besonderen Anforderungen haben oder zu beschäftigt sind, können Sie am besten einen Controller erstellen, der Ihren Anforderungen entspricht, indem Sie die abstrakte Controller-Klasse erben. Da wir es schon oft verwendet haben, werden wir es hier nicht im Detail demonstrieren, aber werfen wir dennoch einen Blick auf die Codestruktur.
Fügen Sie einen Controller im Ordner „Controller“ hinzu. Wenn Sie Ordnung bevorzugen, löschen Sie normalerweise unnötige Referenzen:
using System.Web.Mvc; namespace MvcApplication1.Controllers { public class DerivedController : Controller { public ActionResult Index() { ViewBag.Message = "Hello from the DerivedController Index method"; return View("MyView"); } } }
我们可以查看 Controller 抽象类的定义,发现它是继承 ControllerBase 类的,在 ControllerBase 类中实现了 IController 接口的 Execute 方法,这个方法是MVC对请求进行处理的各个组件的入口,其中包括通过路由系统找到 Action 方法并调用。
Controller 类内部使用 Razor 视图系统来呈现 View,这里通过 View 方法,指定 View 的名称参数来告诉 MVC 选择 MyView 视图来返回给用户结果。
在 Controller 中获取状态数据
我们经常需要访问客户端提交过来的数据,比如 QueryString 值、表单值和通过路由系统来自 URL 的参数值,这些值都可称为状态数据。下面是 Controller 中获取状态数据的三个主要来源:
一系列的上下文对象。
传递给 Action 方法的参数。
显式的调用框架的模型绑定(Model Binding)特性。
从上下文对象中获取状态数据
获取状态数据最直接的方法就是从上下文对象中提取。当你创建了一个继承自 Controller 类的 Controller 时,可以通过一系列的属性可以方便的访问到和请求相关的数据,这些属性包括 Request、Response、RouteData、HttpContext 和 Server,每一个都提供了请求相关的不同类型的信息。下面列出了最常的上下文对象:
在 Action 方法中可以使用任意上下文对象来获取请求相关的信息,如下面在 Action 方法中所演示的:
... public ActionResult RenameProduct() { //访问不同的上下文对象 string userName = User.Identity.Name; string serverName = Server.MachineName; string clientIP = Request.UserHostAddress; DateTime dateStamp = HttpContext.Timestamp; AuditRequest(userName, serverName, clientIP, dateStamp, "Renaming product"); //从POST请求提交的表单中获取数据 string oldProductName = Request.Form["OldName"]; string newProductName = Request.Form["NewName"]; bool result = AttemptProductRename(oldProductName, newProductName); ViewData["RenameResult"] = result; return View("ProductRenamed"); } ...
这些上下对象不用特意去记,用的时候,你可以通过VS的智能提示来了解这些上下文对象。
使用 Action 方法参数获取状态数据
在本系列的前面的文章中,我们已经知识如何通过 Action 参数来接收数据,这种方法和上面的从上下文对象中获取相比,它更为简洁明了。比如,我们有下面这样一个使用上下文对象的 Action 方法:
public ActionResult ShowWeatherForecast() { string city = (string)RouteData.Values["city"]; DateTime forDate = DateTime.Parse(Request.Form["forDate"]); // do something ... return View(); }
我们可以像下面这样使用 Action 方法参数来重写它:
public ActionResult ShowWeatherForecast(string city, DateTime forDate) { // do something ... return View(); }
它不仅易读性强,也方便进行单元测试。
Action 方法的参数不允许使用 ref 和 out 参数,这是没有意义的。
MVC 框架通过检查上下文对象来为 Action 方法的参数提供值,它的名称是不区分大小写的,比如 Action 方法的 city 参数的值可以是通过 Request.Form["City"] 来获取的。
理解 Action 方法的参数是如何被赋值的
Controller 类通过 MVC 框架的 value provider 和 model binder 组件来为 Action 方法获取参数的值。
value provider 提供了一系列Controller中可以访问到的值,在内部它通过从 Request.Form、Request.QueryString、Request.Files 和 RouteData.Values 等上下文对象中提取数据(键值集合),然后把数据传递给 model binder,model binder 试图将这些数据与Action方法的参数进行匹配。默认的 model binder 可以创建和赋值给任何.NET类型对象参数(即 Action 方法的参数),包括集合和自定义的类型。
在这不对 model binder 进行介绍,我将在本系列的后续博文中对其进行专门的介绍。
理解 ActionResult
ActionResult 是描述 Action 方法执行结果的对象,它的好处是想返回什么结果就指定对应的返回对象就行,不用关心如何使用Response对象来组织和生成结果。ActionResult 是一个命令模式的例子,这种模式通过存储和传递对象来描述操作。
当 MVC 框架从 Action 方法中接收到一个 ActionResult 对象,它调用这个对象的 ExecuteResult 方法,其内部是通过 Response 对象来返回我们想要的输出结果。
为了更好的理解,我们通过继承 ActionResult 类来自定义一个 ActionResult。在MVC工程中添加一个Infrastructure文件夹,在里面创建一个名为 CustomRedirectResult 的类文件,代码如下:
using System.Web.Mvc; namespace MvcApplication1.Infrastructure { public class CustomRedirectResult : ActionResult { public string Url { get; set; } public override void ExecuteResult(ControllerContext context) { string fullUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext); context.HttpContext.Response.Redirect(fullUrl); } } }
当我们创建一个 CustomRedirectResult 类的实例时,我们可以传递想要跳转的 URL。当 Action 方法执行结束时,MVC 框架调用 ExecuteResult 方法,ExecuteResult 方法通过 ControllerContext 对象获得 Response 对象,然后调用 Redirect 方法。
下面我们在 Controller 中使用自定义的 CustomRedirectResult:
public class DerivedController : Controller { ... public ActionResult ProduceOutput() { if (Server.MachineName == "WL-PC") { return new CustomRedirectResult { Url = "/Basic/Index" }; } else { Response.Write("Controller: Derived, Action: ProduceOutput"); return null; } }
运行后我们看到如下结果:
当运行在本机(WL-PC)时直接重定向到了指定的/Basic/Index。
上面我们通过自定义 CustomRedirectResult 来实现重定向,我们可以用 MVC 框架提供的方法,如下:
... public ActionResult ProduceOutput() { return new RedirectResult("/Basic/Index"); }
为了使用方便,Controller 类中为大部分类型的 ActionResult 提供简便的方法,如上面的可像下面这样简写:
... public ActionResult ProduceOutput() { return Redirect("/Basic/Index"); }
MVC框架包含了许多 ActionResult 类型,这些类型都继承自 ActionResult 类,大部分在 Controller 类中都有简便的方法,下面列举了一些:
除了该表列出来的,还有ContentResult、FileResult、JsonResult 和 JavaScriptResult。具体每种ActionResult类型的用法这里就不讲了,大家可以看看蒋老师的了解ASP.NET
MVC几种ActionResult的本质系列的文章。
几种从 Action 传递数据到 View 的方式
我们经常需要在 Action 方法中传递数据到一个 View 中,MVC 框架为此提供了一些很方便的操作。下面简单简介几种常用的方式。
View Model 对象
通过 View Model 对象传递数据给View,这是最常用的一种,在 Acton 方法执行结束时通过 View 方法传递 View Model 对象给 View,如下代码所示:
... public ViewResult Index() { DateTime date = DateTime.Now; return View(date); }
在 View 中我们通过 Model 属性来使用传递过来的 View Model 对象,如下:
@model DateTime @{ ViewBag.Title = "Index"; } <h2>Index</h2> The day is: @Model.DayOfWeek
在 Razor 视图引擎中,@model 的作用是声明 odel 属性的类型,省去了类型转换的麻烦,而 @Model 是V iew Model 对象的引用。
ViewBag、ViewData 和 TempData 属性
ViewBag、ViewData 和 TempData 都是 Controller 和 View 中能访问到的属性,都是用来存储小量的数据,他们的区别如下:
ViewBag,是一个动态(dynamic)的弱类型,在程序运行的时候解析,是 MVC3 中新增的特性,只在当前View有效。
ViewData,是一个字典集合,也是只在当前View有效,性能比 ViewBag 高,但是使用的时候需要类型转换。
TempData,也是字典集合,一般用于两个请求之间临时缓存内容或页面间传递消息,保存在 Session 中,使用完以后则从 Session 中被清除。
下面是三者使用的例子,先在 Controller 中分别用三者存储小数据:
public class DerivedController : Controller { public ActionResult Index() { ViewBag.DayOfWeek = DateTime.Now.DayOfWeek; ViewData["DayOfMonth"] = DateTime.Now.Day; return View(); } public ActionResult ProduceOutput() { TempData["Message"] = "Warning message from Derived Controller."; return Redirect("/Home/Index"); } }
在 Views/Derived 目录下的 Index.cshtml 中,取出 ViewBag 和 ViewData 中的存储的数据:
... Day of week from ViewBag: @ViewBag.DayOfWeek <p /> Day of month from ViewData: @ViewData["DayOfMonth"]
在 Views/Home 目录下的 Index.cshtml 中,取 TempData 中的数据如下:
... @TempData["Message"]
当请求 /Derived/ProduceOutput 时,ProduceOutput 方法将一条消息存到 TempData 中,并跳转到 /Home/Index。
下面是分别是将URL定位到 /Derived/Index 和 /Derived/ProduceOutput 时的结果:
一般在当前 View 中使用 ViewBag 或 ViewData,在两个请求之间传递临时数据用 TempData。由于 TempData 被使用后即被释放,所以如果要二次使用 TempData 中的数据就需要将其存到其他变量中。
以上就是[ASP.NET MVC 小牛之路]09 - Controller 和 Action (1)的内容,更多相关内容请关注PHP中文网(www.php.cn)!