ASP.NET mvc的Controller可以说是MVC的核心,是就像电脑的CPU。今天我就来谈谈ASP.NET MVC的Controller是怎样响应输出的。
ASP.NET MVC的Controller处理完请求之后,通常都会输出一些内容,可能是Html,也可能是跳转到其它的URL。我们用接口IController接口自己实现一个Controller类,也可以直接继承基类Controller。下面我们会分别介绍这两种方式的实现。我们先来看看使用接口IController,当我们通过接口IController来创建Controller时虽然灵活和可按性高,但是我们就要关心、负责处理一个web请求的各个方面,其中包括生成输出到客户端。如果我们想输出Html到客户端,要用Response.Write,如果我们想要重定向用户的到其它页面要用Response.Redirect。如下面的代码:
- 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"];
- if (action.ToLower() == "redirect") {
- requestContext.HttpContext.Response.Redirect("/Derived/Index");
- } else {
- requestContext.HttpContext.Response.Write(
- string.Format("Controller: {0}, Action: {1}",
- controller, action));
- }
- }
- }
- }
- using System.Web.Mvc;
- namespace ControllersAndActions.Controllers {
- public class DerivedController : Controller {
- public void ProduceOutput() {
- if (Server.MachineName == "TINY") {
- Response.Redirect("/Basic/Index");
- } else {
- Response.Write("Controller: Derived, Action: ProduceOutput");
- }
- }
- }
- }
上面是虽然能够实现输出html和跳转,但是有以下三个问题。
1、Controller类里面会有原始的Html代码和URL,这让这个类的可读性和可维护性大大了降低。
2、这种直接输出html的形式要单元测试你需要自己Mock一个Controller并从这个Controller输出结果中通过解析Html字符串来判断是否得到相应的结果,这个过程是比较难实现和繁琐的。
3、一个Controller里面像上面一样用代码的形式在手动控制处理请求并响应,是一个乏味且容易出错的过程。
幸运的是MVC框架为我们提供了一个叫做Action Result的东西可以全部解决上面的问题。下面我们就来讲讲什么是Action Result和在Controller里利用Action Result输出内容的几种方式。
一、理解Action Results
可能你平时都注意到了,当我们新建的Controller时,Action默认返回值都是ActionResult类型,如下代码所示:
- public class HomeController : Controller
- {
- public ActionResult Index()
- {
- return View();
- }
- public ActionResult About()
- {
- ViewBag.Message = "Your application description page.";
- return View();
- }
- public ActionResult Contact()
- {
- ViewBag.Message = "Your contact page.";
- return View();
- }
- }
- // 封装一个操作方法的结果并用于代表该操作方法执行框架级操作。
- public abstract class ActionResult
- {
- protected ActionResult();
- // 通过从 System.Web.Mvc.ActionResult 类继承的自定义类型,启用对操作方法结果的处理。
- // 参数:
- // context:用于执行结果的上下文。上下文信息包括控制器、HTTP 内容、请求上下文和路由数据。
- public abstract void ExecuteResult(ControllerContext context);
- }
- using System;
- using System.Collections.Generic;
- using System.linq;
- using System.Web;
- using System.Web.Mvc;
- namespace ControllersAndActions.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);
- }
- }
- }
- using ControllersAndActions.Infrastructure;
- using System.Web.Mvc;
- namespace ControllersAndActions.Controllers {
- public class DerivedController : Controller {
- public ActionResult Index() {
- ViewBag.Message = "Hello from the DerivedController Index method";
- return View("MyView");
- }
- public ActionResult ProduceOutput() {
- if (Server.MachineName == "TINY") {
- return new CustomRedirectResult { Url = "/Basic/Index" };
- } else {
- Response.Write("Controller: Derived, Action: ProduceOutput");
- return null;
- }
- }
- }
- }
return View()
return View(m)
return Redirect("/Basic/Index")
return new RedirectResult("/Basic/Index")
这些其实都是返回的ActionResult的实例。
下面是例举一些系统内置的ActionResult类型。
类型 | 描述 |
ViewResult | 返回默认视图或者指定视图 |
PartialViewResult | 返回默认视图或者指定局部视图 |
RedirectToRouteResult | 在浏览器端会产生301或302的Http状态码,重定向到一个action或者一个route结点,生成的url是以路由配置为依据。 |
RedirectResult | 在浏览器端会产生301或302的Http状态码,重定向到一个指定的url。 |
HttpUnauthorizedResult | 设置Http状态码为401,表示请求的Url未获得授权,要求用户要先登录才能访问。 |
HttpNotFoundResult | 返回404,未找到请求的资源 |
HttpStatusCodeResult | 返回指定的Http状态码 |
EmptyResult | 返回空的Result,什么也不做 |
二、通过View视图返回Html
ASP.NET MVC中最常用的方式就在Action对应的视图中定义要返回的html,视图其实起到一个模板的作用。Controller的Action复杂业务调用和准备数据,选择合适的视图。如下:
- using System.Web.Mvc;
- namespace ControllersAndActions.Controllers {
- public class ExampleController : Controller {
- public ViewResult Index() {
- return View("Homepage");
- }
- }
- }
View("ViewName")的返回值是一个ViewResult类的实例。这里我们们可以指定返回的视图名字,如上面视图名为“HomePage”,我们也可以不指定视图名字,让MVC框架查找Action默认的视图。
当没有指定返回视图的名字时,MVC框架调用ActionResult的ExecuteResult方法时,返回视图的时候会按照你指定的位置搜索视图。如果你使用了Area就会按照以下顺序搜索默认视图:
• /Areas/<AreaName>/Views/<ControllerName>/<ViewName
• /Areas/<AreaName>/Views/<ControllerName>/<ViewName
• /Areas/<AreaName>/Views/Shared/<ViewName>.aspx
• /Areas/<AreaName>/Views/Shared/<ViewName>.ascx
• /Areas/<AreaName>/Views/<ControllerName>/<ViewName
• /Areas/<AreaName>/Views/<ControllerName>/<ViewName
• /Areas/<AreaName>/Views/Shared/<ViewName>.cshtml
• /Areas/<AreaName>/Views/Shared/<ViewName>.vbhtml
可以从上面看到MVC框架先是在找对应Controller的视图,然后再找公共的视图。这里要注意的是:即使你的项目使用的是razor视图引擎,它也会去找老的视图格式,.aspx和ascx,一旦找到匹配的视图文件,就使用这个视图文件并停止向下查找,最后响应输出结果。
在不使用Area的情况按下面的顺序搜索视图:
• /Views/<ControllerName>/<ViewName>.a
• /Views/<ControllerName>/<ViewName>.a
• /Views/Shared/<ViewName>.aspx
• /Views/Shared/<ViewName>.ascx
• /Views/<ControllerName>/<ViewName>.c
• /Views/<ControllerName>/<ViewName>.v
• /Views/Shared/<ViewName>.cshtml
• /Views/Shared/<ViewName>.vbhtml
在Action返回视图,用得最多的就是使用View()方法,这个方法有很多重载的方法,提供种方式的调用。
未指定视图,使用默认视图
- using System.Web.Mvc;
- using System;
- namespace ControllersAndActions.Controllers {
- public class ExampleController : Controller {
- public ViewResult Index() {
- return View();
- }
- }
- }
- public ViewResult Index() {
- return View("Index", "_AlternateLayoutPage");
- }
- using System.Web.Mvc;
- namespace ControllersAndActions.Controllers {
- public class ExampleController : Controller {
- public ViewResult Index() {
- return View("~/Views/Other/Index.cshtml");
- }
- }
- }
三、在Action中传递数据到View
3.1、弱类型视图
Action中可以把数据传递给View,让后把数据显示出来。
传递一个变量给View:
- public ViewResult Index() {
- DateTime date = DateTime.Now;
- return View(date);
- }
- @{
- ViewBag.Title = "Index";
- }
- <h2>Index</h2>
- The day is: @(((DateTime)Model).DayOfWeek)
3.2、强类型视图
上面采用的是弱类型的视图,视图把传过来的变量当成object处理,所以在使用的时候要转换成相应的类型。这样的变量类型转换难免有些性能的影响,下面介绍一下使用强类型的视图:
- @model DateTime
- @{
- ViewBag.Title = "Index";
- }
- <h2>Index</h2>
- The day is: @Model.DayOfWeek
3.3、使用ViewBag传递变量到视图
ViewBag可以看成是一个Action到View变量容器,它的属性可以添加多个属性并可以把任意类型的变量赋值给它们。ViewBag在使用方法如下:
在Controller中为ViewBag赋值:
- using System;
- using System.Web.Mvc;
- namespace ControllersAndActions.Controllers {
- public class ExampleController : Controller {
- public ViewResult Index() {
- ViewBag.Message = "Hello";
- ViewBag.Date = DateTime.Now;
- return View();
- }
- }
- }
- @{
- ViewBag.Title = "Index";
- }
- <h2>Index</h2>
- The day is: @ViewBag.Date.DayOfWeek
- <p />
- The message is: @ViewBag.Message
1、可以传递多个对象或变量给视图。
2、当视图需要新的字段时不用修改Model或者创建一个新的类。
我们在实际的编程中经常把强类型视图Model和ViewBag结合起来用。
四、在Action中使用跳转
4.1、跳转到特定的URL地址
- using System;
- using System.Web.Mvc;
- namespace ControllersAndActions.Controllers {
- public class ExampleController : Controller {
- public ViewResult Index() {
- ViewBag.Message = "Hello";
- ViewBag.Date = DateTime.Now;
- return View();
- }
- public RedirectResult Redirect() {
- return Redirect("/Example/Index");
- }
- }
- }
4.2、跳转到特定的路由系统的URL地址
采用文本的URL地址跳转,虽然简单,但是有一个不好的地方是当你一旦改变了路由规则,代码里写死的链接也会改变,因此你要找到你使用这些链接的所有代码进行改动。一个更好的方法是可以利路由系统使用RedirectToRoute方法返回一个RedirectToRouteResult的实例,给我们的程序增加伸缩性,以后发到路由基本上不用做其它改动。
- using System;
- using System.Web.Mvc;
- namespace ControllersAndActions.Controllers {
- public class ExampleController : Controller {
- public ViewResult Index() {
- ViewBag.Message = "Hello";
- ViewBag.Date = DateTime.Now;
- return View();
- }
- public RedirectToRouteResult Redirect() {
- return RedirectToRoute(new {
- controller = "Example",
- action = "Index",
- ID = "MyID"
- });
- }
- }
- }
4.3、跳转到特定的Action
也许你会觉得在Action中使用RedirectToRoute传一个匿名对象比较麻烦。好消息是MVC框架给我们提供了一种更加简洁的方式,对RedirectToRoute方法进行了封装,新的方法是RedirectToAction。使用方法如下:
- public RedirectToRouteResult RedirectToRoute() {
- return RedirectToAction("Index");
- }
- public RedirectToRouteResult Redirect() {
- return RedirectToAction("Index", "Basic");
- }
五、在Action中返回错误和Http Codes
一般情况我们都不用关心请求返回的Http Code和错误,因为MVC框架都为我做了很多这样事情,比如你请求一个不存在的URL它会返回404错误。
有时我们要根据业务逻辑返回特定的错误,比如请求一篇不存在ID的文章是我们也需要返回404。要在MVC返回指定错误状态码可以这样写:
- public HttpStatusCodeResult StatusCode() {
- return new HttpStatusCodeResult(404, "URL cannot be serviced");
- }
- public HttpStatusCodeResult StatusCode() {
- return HttpNotFound();
- }
- public HttpStatusCodeResult StatusCode() {
- return new HttpUnauthorizedResult();
- }
六、总结
本文从开始介绍一下使用IController手动实现一个Controller的简单功能,然后介绍Contorller和ActionResult以及怎么在Action重定向跳转,最后介绍了一下在MVC程序中手动返回错误状态码方法。