ASP.NET MVC的Controller响应输出详解

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。如下面的代码:

 
  1. using System.Web.Mvc;
  2. using System.Web.Routing;
  3. namespace ControllersAndActions.Controllers {
  4. public class BasicController : IController {
  5. public void Execute(RequestContext requestContext) {
  6. string controller = (string)requestContext.RouteData.Values["controller"];
  7. string action = (string)requestContext.RouteData.Values["action"];
  8. if (action.ToLower() == "redirect") {
  9. requestContext.HttpContext.Response.Redirect("/Derived/Index");
  10. } else {
  11. requestContext.HttpContext.Response.Write(
  12. string.Format("Controller: {0}, Action: {1}",
  13. controller, action));
  14. }
  15. }
  16. }
  17. }
当然你也可以通过继承基类Controller实现跳转和输出html:
 
  1. using System.Web.Mvc;
  2. namespace ControllersAndActions.Controllers {
  3. public class DerivedController : Controller {
  4. public void ProduceOutput() {
  5. if (Server.MachineName == "TINY") {
  6. Response.Redirect("/Basic/Index");
  7. } else {
  8. Response.Write("Controller: Derived, Action: ProduceOutput");
  9. }
  10. }
  11. }
  12. }

上面是虽然能够实现输出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类型,如下代码所示:

 
  1. public class HomeController : Controller
  2. {
  3. public ActionResult Index()
  4. {
  5. return View();
  6. }
  7. public ActionResult About()
  8. {
  9. ViewBag.Message = "Your application description page.";
  10. return View();
  11. }
  12. public ActionResult Contact()
  13. {
  14. ViewBag.Message = "Your contact page.";
  15. return View();
  16. }
  17. }
我们选中ActionResult,按F12查看ActionResult的定义如下:
 
  1. // 封装一个操作方法的结果并用于代表该操作方法执行框架级操作。
  2. public abstract class ActionResult
  3. {
  4. protected ActionResult();
  5. // 通过从 System.Web.Mvc.ActionResult 类继承的自定义类型,启用对操作方法结果的处理。
  6. // 参数:
  7. // context:用于执行结果的上下文。上下文信息包括控制器、HTTP 内容、请求上下文和路由数据。
  8. public abstract void ExecuteResult(ControllerContext context);
  9. }
可以看到ActionResult是一个抽象类,最关键的方法就是ExecuteResult这个抽象方法,当一个具体类继承这个抽象类时实现这个ExecuteResult抽象方法,里面负责调用Response对象产生相应的输出。当MVC框架接收到从一个Action方法返回的ActionResult实例时,它会自动调用这个ExecuteResult方法,来响应输出。下面通过一个简单的写一个类来看看实现抽象类ActionResult的用法:
 
  1. using System;
  2. using System.Collections.Generic;
  3. using System.linq;
  4. using System.Web;
  5. using System.Web.Mvc;
  6. namespace ControllersAndActions.Infrastructure {
  7. public class CustomRedirectResult : ActionResult {
  8. public string Url { get; set; }
  9. public override void ExecuteResult(ControllerContext context) {
  10. string fullUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext);
  11. context.HttpContext.Response.Redirect(fullUrl);
  12. }
  13. }
  14. }
接下来我们看看怎么在Controller的Action里面使用这个自定义的CustomRedirectResult。
 
  1. using ControllersAndActions.Infrastructure;
  2. using System.Web.Mvc;
  3. namespace ControllersAndActions.Controllers {
  4. public class DerivedController : Controller {
  5. public ActionResult Index() {
  6. ViewBag.Message = "Hello from the DerivedController Index method";
  7. return View("MyView");
  8. }
  9. public ActionResult ProduceOutput() {
  10. if (Server.MachineName == "TINY") {
  11. return new CustomRedirectResult { Url = "/Basic/Index" };
  12. } else {
  13. Response.Write("Controller: Derived, Action: ProduceOutput");
  14. return null;
  15. }
  16. }
  17. }
  18. }
除了返回一个我们自定义的ActionResult具体类的实例之外,还可以返回MVC框架内置的一些ActionResult。当我们在Action方法的最后调用类似下面方法:

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复杂业务调用和准备数据,选择合适的视图。如下:

 
  1. using System.Web.Mvc;
  2. namespace ControllersAndActions.Controllers {
  3. public class ExampleController : Controller {
  4. public ViewResult Index() {
  5. return View("Homepage");
  6. }
  7. }
  8. }

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()方法,这个方法有很多重载的方法,提供种方式的调用。

未指定视图,使用默认视图

 
  1. using System.Web.Mvc;
  2. using System;
  3. namespace ControllersAndActions.Controllers {
  4. public class ExampleController : Controller {
  5. public ViewResult Index() {
  6. return View();
  7. }
  8. }
  9. }
指定试图和Layout文件:
 
  1. public ViewResult Index() {
  2. return View("Index", "_AlternateLayoutPage");
  3. }
视图文件存储的位置是按照Controller的名字的规则来存放的,这个约定比较简单和方便,但有时候想返回一个绝对的路径的视图文件,如下:
 
  1. using System.Web.Mvc;
  2. namespace ControllersAndActions.Controllers {
  3. public class ExampleController : Controller {
  4. public ViewResult Index() {
  5. return View("~/Views/Other/Index.cshtml");
  6. }
  7. }
  8. }
我们可以通过以“~”或者"/"开头,手动指定视图文件的完整位置,这样MVC框架不会去按约定规则那样去一个一个路径的去找满足匹配的默认的视图文件。

三、在Action中传递数据到View

3.1、弱类型视图

Action中可以把数据传递给View,让后把数据显示出来。

传递一个变量给View:

 
  1. public ViewResult Index() {
  2. DateTime date = DateTime.Now;
  3. return View(date);
  4. }
视图使用变量:
 
  1. @{
  2. ViewBag.Title = "Index";
  3. }
  4. <h2>Index</h2>
  5. The day is: @(((DateTime)Model).DayOfWeek)

3.2、强类型视图

上面采用的是弱类型的视图,视图把传过来的变量当成object处理,所以在使用的时候要转换成相应的类型。这样的变量类型转换难免有些性能的影响,下面介绍一下使用强类型的视图:

 
  1. @model DateTime
  2. @{
  3. ViewBag.Title = "Index";
  4. }
  5. <h2>Index</h2>
  6. The day is: @Model.DayOfWeek
使用强类型的视图除了性能优势之外,还有就是Model的属性在vs编辑器中能够智能感知。

3.3、使用ViewBag传递变量到视图

ViewBag可以看成是一个Action到View变量容器,它的属性可以添加多个属性并可以把任意类型的变量赋值给它们。ViewBag在使用方法如下:

在Controller中为ViewBag赋值:

 
  1. using System;
  2. using System.Web.Mvc;
  3. namespace ControllersAndActions.Controllers {
  4. public class ExampleController : Controller {
  5. public ViewResult Index() {
  6. ViewBag.Message = "Hello";
  7. ViewBag.Date = DateTime.Now;
  8. return View();
  9. }
  10. }
  11. }
上面我们给ViewBag定义了两个属性Message和Date。在视图中读取ViewBag的属性:
 
  1. @{
  2. ViewBag.Title = "Index";
  3. }
  4. <h2>Index</h2>
  5. The day is: @ViewBag.Date.DayOfWeek
  6. <p />
  7. The message is: @ViewBag.Message
使用ViewBag的好处有以下几点:

1、可以传递多个对象或变量给视图。

2、当视图需要新的字段时不用修改Model或者创建一个新的类。

我们在实际的编程中经常把强类型视图Model和ViewBag结合起来用。

四、在Action中使用跳转

4.1、跳转到特定的URL地址

 
  1. using System;
  2. using System.Web.Mvc;
  3. namespace ControllersAndActions.Controllers {
  4. public class ExampleController : Controller {
  5. public ViewResult Index() {
  6. ViewBag.Message = "Hello";
  7. ViewBag.Date = DateTime.Now;
  8. return View();
  9. }
  10. public RedirectResult Redirect() {
  11. return Redirect("/Example/Index");
  12. }
  13. }
  14. }
上面跳转到用文本表示的的URL地址:/Example/Index。

4.2、跳转到特定的路由系统的URL地址

采用文本的URL地址跳转,虽然简单,但是有一个不好的地方是当你一旦改变了路由规则,代码里写死的链接也会改变,因此你要找到你使用这些链接的所有代码进行改动。一个更好的方法是可以利路由系统使用RedirectToRoute方法返回一个RedirectToRouteResult的实例,给我们的程序增加伸缩性,以后发到路由基本上不用做其它改动。

 
  1. using System;
  2. using System.Web.Mvc;
  3. namespace ControllersAndActions.Controllers {
  4. public class ExampleController : Controller {
  5. public ViewResult Index() {
  6. ViewBag.Message = "Hello";
  7. ViewBag.Date = DateTime.Now;
  8. return View();
  9. }
  10. public RedirectToRouteResult Redirect() {
  11. return RedirectToRoute(new {
  12. controller = "Example",
  13. action = "Index",
  14. ID = "MyID"
  15. });
  16. }
  17. }
  18. }
注意: 使用RedirectToRoute产生的一个临时的重定向,要产生一个永久重定向要调用方法RedirectToRoutePermanent。这两个方法都可以接收一个匿名对象,这个对象的字段将被MVC路由系统使用。


4.3、跳转到特定的Action

也许你会觉得在Action中使用RedirectToRoute传一个匿名对象比较麻烦。好消息是MVC框架给我们提供了一种更加简洁的方式,对RedirectToRoute方法进行了封装,新的方法是RedirectToAction。使用方法如下:

 
  1. public RedirectToRouteResult RedirectToRoute() {
  2. return RedirectToAction("Index");
  3. }
当你只指定了Action的名字,RedirectToAction默认是指当前Controller,正如上面代码所示。如果你想跳转到其它的Controller的Action就要多传一个参数,如下:
 
  1. public RedirectToRouteResult Redirect() {
  2. return RedirectToAction("Index", "Basic");
  3. }
上面代码表示跳转到BasicController的Index。

五、在Action中返回错误和Http Codes

一般情况我们都不用关心请求返回的Http Code和错误,因为MVC框架都为我做了很多这样事情,比如你请求一个不存在的URL它会返回404错误。

有时我们要根据业务逻辑返回特定的错误,比如请求一篇不存在ID的文章是我们也需要返回404。要在MVC返回指定错误状态码可以这样写:

 
  1. public HttpStatusCodeResult StatusCode() {
  2. return new HttpStatusCodeResult(404, "URL cannot be serviced");
  3. }
ASP.NET MVC的Action返回404还有更加简洁的方式:
 
  1. public HttpStatusCodeResult StatusCode() {
  2. return HttpNotFound();
  3. }
ASP.NET MVC的Action返回401:
 
  1. public HttpStatusCodeResult StatusCode() {
  2. return new HttpUnauthorizedResult();
  3. }

六、总结

本文从开始介绍一下使用IController手动实现一个Controller的简单功能,然后介绍Contorller和ActionResult以及怎么在Action重定向跳转,最后介绍了一下在MVC程序中手动返回错误状态码方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值