zoukankan      html  css  js  c++  java
  • MVC笔记 Controller相关技术

    一、Controller的责任

        MVC的核心就是Controller(控制器),它负责处理浏览器传送过来的所有请求,并决定要将什么内容响应给浏览器。但Controller并不负责决定内容应该如何显示,而是将特定形态的内容响应给MVC架构,最后才由MVC架构依据响应的形态来决定如何将内容响应给浏览器。如何决定响应内容是View的责任。

    二、Controller的类与方法

        Controller本身就是一个类(Class),该类有许多方法(Method)。在这些方法中,只要是公开方法,该方法就会被视为是一种动作(Action);只要有动作存在,就可以通过该动作方法接收网页请求并决定响应视图。

        由上可知编写Controller的基本要求:

    • Controller必须为公开类。
    • Controller的名称必须以"Controller"结尾。
    • 必须继承自MVC内置的Controller类,或继承自实现IController接口的自定义类,或自行实现IController接口。
    • 所有方法必须为公开方法。改方法可以没有参数,也可以有多个参数。

    三、Controller的执行过程

        Controller被MvcHandler选中之后,下一步就是通过ActionInvoker选取适当的Action来执行。在Controller中,每一个Action可以定义0到多个参数。ActionInvoke会依据当前的RouteValue及客户端传过来的信息准备好可输入Action参数的依据,最后正式调用被Controller选中的那个Action方法。

         Action执行完后的返回值通常是ActionResult类的。事实上,ActionResult类是一个抽象类,因此,MVC本身就实现了许多不同ActionResult类的子类。Controller得到ActionResult类之后,就会开始执行ActionResult类的ExecuteResult()方法,并将执行的结果返回客户端。这时,Controller的任务就算完成了。

          Controller在执行时还有一个动作过滤器(Action Filter)机制,可以分成以下4中类型。

    • 授权过滤器(Authorization Filter);
    • 动作过滤器(Action Filter);
    • 结果过滤器(Result Filter);
    • 例外过滤器(Exception Filter)。

        此外,在Controller的执行过程中还必须考虑动作过滤器的执行顺序。除上述说明之外,在执行Action与ActionResult类时嗨会有一些事件被执行,这部分将在九中说明。

    四、动作名称选取器

        通过ActionInvoker选取Controller中的公开方法时,默认会用Reflection(映像)的方式取得Controller中具有相同名字的方法(不区分大小写)。如下程序范例表示得很清楚:当RouteValue表达式中的Action是"Index",默认会执行Index()方法。

    复制代码
     1   public class HomeController : Controller
     2     {
     3         /// <summary>
     4         /// 要求网址 http://localhost/Home/Index
     5         /// </summary>
     6         public ActionResult Index()
     7         {           
     8             return View();
     9         }
    10 
    11     }
    复制代码

        如果在以上的Action中加入ActionName属性,并将其指名为"Default",此时,若RouteValue表达式中的Action是"Index",就不会执行Index()方法,而必须使RouteValue表达式中的Action为"Default",Index()方法才能被正确执行,这就是动作名称选取器(Action Name Selector)的作用,示例如下。

    复制代码
     1   public class HomeController : Controller
     2     {
     3         /// <summary>
     4         /// 要求网址 http://localhost/Home/Index
     5         /// </summary>
     6         [ActionName("Default")]
     7         public ActionResult Index()
     8         {           
     9             return View();
    10         }
    11 
    12     }
    复制代码

        唯一需要特别注意的是,如果你使用默认的"return View()"方法返回ActionResult类,由于应用了[ActionName("Default")]属性,所以MVC会去寻找"/Views/Home/Default.aspx"页面而不是"/Views/Home/Index.aspx"页面来执行。

    五、动作方法选取器

       5.1  NonAction属性

        若将NonAction属性应用在Controller中的Action方法上,即便该Action方法是公开方法,也会告知ActionInvoke不要选取这个Action来执行。这个属性主要用来保护Controller中的特定公开方法不会被发布到Web上。或是当功能尚未开发完成就要进行部署时,若暂时不想将此方法删除,也可应用这个属性,表示"不要对外公开"。

    1        [NonAction]
    2         public ActionResult Index()
    3         {
    4             return View();
    5         }

        将Action方法中的"public"修改成"privare",也可以达到同样的目的,示例如下:

    1        private ActionResult Index()
    2         {
    3             return View();
    4         }

       5.2  HttpGet属性、HttpPost属性、HttpDelete属性和HttpPut属性

        HttpGet、HttpPost、HttpDelete和HttpPut属性是动作方法选取器的一部分,我们以下列程序为例进行介绍。若应用了[httpPost]属性,表示只有当客户端浏览器发送HTTP POST请求时才可以选取这个Action。

    1       [HttpPost]
    2         private ActionResult Index()
    3         {
    4             return View();
    5         }

        相反的,若果没有应用这些属性,客户端浏览器发送任何HTTP动词,都会自动选取对应的Action。

        这些属性常用在需要接受窗口数据的时候。你可以创建两个同名的Action,一个应用[HttpGet]属性来显示窗口HTML,另一个应用[HttpPost]属性来接收窗口送出的值,范例程序如下。

    复制代码
     1         [HttpGet]
     2         public ActionResult Create()
     3         {
     4             return View();
     5         }
     6         [HttpPost]
     7         private ActionResult Create(FormCollection c)
     8         {
     9             UpdateToDB(c);
    10             return RedirectToAction("Index");
    11         }
    复制代码

      NOTE   由于HTML窗口无法送出"Delete"这个Http动词,所以如果希望Action能提供像RESET协议那样的方式来处理删除动作,又能通过同一个窗口使用这个只允许"Delete"的动作的话,可以用Html.HttpMethodOverride()方法的HTML辅助方法来模拟Http Delete方法的行为,但实际上窗口还是以Http Post的方式送出去的。

    六、 ActionResult类

        ActionResult类是Action执行的结果,但ActionResult中并不包含执行结果,而是包含执行响应时所需的信息。当Action返回ActionResult类之后,会由MVC执行。先看看ActionResult抽象类的程序代码。在ActionResult抽象类中仅定义了一个ExecuteResult()方法来执行结果。

    复制代码
     1  namespace System.Web.Mvc
     2   {
     3     // 摘要:
     4     //     封装一个操作方法的结果并用于代表该操作方法执行框架级操作。
     5     public abstract class ActionResult
     6     {
     7         // 摘要:
     8         //     初始化 System.Web.Mvc.ActionResult 类的新实例。
     9         protected ActionResult();
    10 
    11         // 摘要:
    12         //     通过从 System.Web.Mvc.ActionResult 类继承的自定义类型,启用对操作方法结果的处理。
    13         //
    14         // 参数:
    15         //   context:
    16         //     用于执行结果的上下文。上下文信息包括控制器、HTTP 内容、请求上下文和路由数据。
    17         public abstract void ExecuteResult(ControllerContext context);
    18     }
    19   }
    复制代码

        MVC定义的ActionResult如表所示:

    Contro辅助方法

    用 途

    ContentResult

    Content

    返回一段用户自定义的文字内容

    EmptyResult

    不返回任何数据,即不响应任何数据

    JsonResult

    Json

    将数据序列化成JSON格式返回

    RedirectResult

    Redirect

    重定向到指向的URL

    RedirectToRouteResult

    RedirectToAction、RedirectToRoute

    与RedirectResult类似,但它将新定向到一个Action或Route

    ViewResult

    View

    使用IViewInstance接口和IViewEngine接口,实际输出数据的是IViewEngine接口和View

    PartialViewResult

    PartialView

    与ViewResult类相似,返回的是”部分显示”,即”UserControls”目录下的View

    FileResult

    File

    以二进制串流的方式返回一个文件数据

    JavaScriptResult

    JavaScript

    返回的是JavaScript指令码

        表中的Controller辅助方法在Controller类中为返回ActionResult类提供支持,如下程序可用于跳转到另一个页面。

    1      [HttpPost]
    2         public ActionResult Post(FormCollection c)
    3         {
    4             return new RedirectResult("/");
    5         }

        如果使用Controller辅助方法,就可以将以上程序改写如下:

    1      [HttpPost]
    2         public ActionResult Post(FormCollection c)
    3         {
    4             return Redirect("/");
    5         }

        以上两段程序代码其实差不多,但实际操作中则是以使用Controller辅助方法居多。

      6.1  ViewResult类

        ViewResult类是在MVC中最常用的ActionResult类,用于返回一个标准的视图。通过Controller辅助方法,我们能更方便地定义如何输出视图。指定要输出的View名称、指定该View要应用哪个MasterPage、指定要输入的View的Model。

      6.2  PartialViewResult类

        PartialViewResult类与ViewResult类非常相似,但它无法为View赋值MasterPage,通常用在前端为Ajax应用程序的情况下,并可以通过Ajax来取得网页中的部分内容。

        如下程序会执行"/Views/Home/About.ascx"页面,并将结果输出至客户端。

    1        public ActionResult About()
    2         {
    3             return PartialView();
    4         }

      6.3  EmptyResult类

        有些Action在执行后其实不需要返回任何数据,例如一个页面执行完后直接转到其他页面的情况。EmptyResult类不会执行任何响应客户端的程序,所以也不会返回任何数据,使用方法如下:

    1       public ActionResult Empty()
    2         {
    3             return new EmptyResult();
    4         }

        在MVC中,还有一种表达EmptyResult类的方式,即将上述语法写成如下:

    1        public void Empty()
    2         {
    3             return;
    4         }

        有一种情况是用EmptyResult类搭配Response.Redirect()方法进行HTTP 302暂时转向,示例如下:

    1       public void Redirect()
    2         {
    3             Response.Redirect("/Home/Index");
    4         }

        如果已经开始使用.NET 4.0,也可以考虑使用4.0新增的Respo.RedirectPermanent()方法建立HTTP 301永久转向,示例如下。

    1       public void Redirect()
    2         {
    3             Response.RedirectPermanent("/Home/Index");
    4         }

      6.4  ContentResult类

        ContentResult类可以响应文字内容的结果。可以让ContentResult类响应任意指定文字内容。Content-Type和文字编码(Encoding)。

        如下范例将会响应一段XML文字,并设定响应的Content-Type为text/xml(已指定响应的文字编码方式为Encoding.UTF8)。

    1       public ActionResult Content()
    2         {
    3             return Content("<ROOT><TEXT>123</TEXT></ROOT>","text/xml",Encoding.UTF8);
    4         }

        如果只响应一串HTML字符串,可以只使用第一个参数,如下:

    1       public ActionResult Content()
    2         {
    3             string strHTML = "........";   //省略 HTML的内容
    4             return Content(strHTML);
    5         }

        还有一种方法可以表达如上一样简单的返回类,直接将返回类设定成"string"即可。MVC会进行判断,只要Action返回的不是ActionResult类,就会将返回的类转换成字符串类输出。

      6.5  FileResult类

       FileResult类可以响应任意的文件内容,包括二进制格式的数据,例如图像文件。PDF文档或ZIP文件,可以输入byte数组、文件路径、stream数据、Content-Type、下载文件名等参数并将其返回客户端。事实上,FileResult是一个抽象类,在MVC中实现FIleResult类的子类共有3个,分别是:

    • FilePathResult:响应一个实体文件;
    • FileContentResult:响应一个byte数组的内容;
    • FileStreamResult:响应一个Stream数据。

        但通过Controller类中所提供的File辅助方法可以让你不用记忆这么多。File()辅助方法能自动选取不同的FileResult类进行响应。

        如果通过Action输出一个存储在"App_Data"目录栏下的PNG图像文件,可以参考以下代码:

    1        public ActionResult GetFile()
    2         {
    3             return File(Server.MapPath("~/App_Data/UserA/a.png"),"image/png");
    4         }

        若希望直接通过浏览器下载文件,而不是在浏览器打开文件,可以再第3个参数中输入要求下载的文件名。如:PDF文件来自于数据库,并希望让用户下载,可以先取得一个byte数组或Stream数据,并在File()辅助方法的第2个参数中指定正确的Content-Type,最后再指定要下载的文件名即可。

    1       public ActionResult GetFile()
    2         {
    3             byte[] fileContent = GetFileByteArrayFromDB();
    4 
    5             return File(fileContent,"application/pdf","Report.pdf");
    6         }


      6.6  强制下载文件时需注意中文文件名的问题

        由于MVC支持中文文件名文件下载的能力有限,若要开发出跨浏览器下载中文文件名文件的解决方案,就不能只靠FileResult类了,可以自定义ActionResult类来克服这个问题,也可直接用较为底层的方法来解决。一下将说明强制下载文件的原理及解决方法。

        强制下载文件功能时,通常是通过设定"Content-Disposition"这个HTTP响应标头(Response Header)的方式将强制下载文件的要求告知客户端,示例如下。

    1        string fileName = "ExportData.csv";
    2        string strContentDisposition = String.Format("{0};filename="{1}"", "attachment", fileName);
    3        Response.AddHeader("Content-Disposition", strContentDisposition);

        通过上述程序代码,就可以让客户端强制下载此页面的内容,也就是说,改页面的内容(可能是文件或二进制文件)不会直接在浏览器中打开,下载后也不会打开相关程序。

        Content-Disposition标头的第一组参数是"attachment",代表此文件是一个附件文件,也就是"要求下载"的意思。如果将"attachment"改成".inline"的话,就代表这是一个内嵌于其他网页的文件(如图像文件、CSS、JavaScript、Flash等),而这也是默认的设定,等同于不添加Content-Disposition标头的情况。

      6.7  JavaScriptResult类

        JavaScriptResult类的用途是将javaScript程序代码响应给浏览器。通过Ajax环境,可以利用JavaScriptResult类来响应适当的JavaScript程序代码并将其交给浏览器动态执行,由于Ajax功能属于View.

      6.8  JsonResult类

        JSON是Web在实现Ajax应用程序时经常使用的一种数据传输格式,JsonResult类可自动将任意对象的数据序列转换成JSON格式返回。JsonResult类默认的Content-Type为application/json,对于某些JavaScript Framework来说,这是必要的需求,如jQuery。 建议尽量避免使用HTTP GET方法获取JSON数据。但若只使用HTTP POST方法获取JSON数据也存在一个问题,那就是数据无法被浏览器缓存。如果数据敏感度不高且想实现缓存的话,还需让JsonResult类能够对HTTP GET请求进行响应,解决方案就是为JSON()辅助方法加上一个JsonResultBehavior列举参数,这样就可以通过GET方法取得JSON数据了。

    复制代码
    1       public ActionResult JSON()
    2         {
    3             return Json(new
    4             {
    5                 id = 1,
    6                 name = "will",
    7                 CreatedOn = DateTime.Now
    8             }, JsonRequestBehavior.AllowGet);
    9         }
    复制代码

      6.9  RedirectResult类

        RedirectResult类主要用途是执行指向其他页面的重定向。在RedirectResult类的内部,基本上还是用Response.Redirect()方法响应HTTP 302暂时定向的。

    1        public ActionResult Redirect()
    2         {
    3             return Redirect("/Home/Index");
    4         }

      6.10 RedirectToRoute类

        Controller类中有两个与RedirectToRoute类有关的辅助方法,分别是RedirectToAction()和RedirectToRoute().

        1. RedirectToAction()辅助方法

           RedirectToAction()方法比较简答,既可通过直接输入Action名称来设定让浏览器转向该Action网址,也可以输入新建的RouteValue值,如下:

    • 转到同一个Controller中的另一个Action。
    1       public ActionResult RedirectToActionSample()
    2         {
    3             return RedirectToAction("SamplePage");
    4         }
    • 转到指定Controller的特定Action。
    1       public ActionResult RedirectToActionSample()
    2         {
    3             return RedirectToAction("List", "Member");
    4         }
    • 转到MemberController的List Action,并且加上"page"这个RouteValue值。
    1       public ActionResult RedirectToActionSample()
    2         {
    3             return RedirectToAction("List", "Member", new { page = 3 });
    4         }

      2. RedirectToRoute()辅助方法

        RedirectToRoute()辅助方法较为复杂,可利用Global.asax文件中定义的网址路由表来指定不同的转向网址。

    • 转到同一个Controller中的另一个Action。
    1       public ActionResult RedirectToRouteSample()
    2         {
    3             return RedirectToRoute(new { action = "SamplePage" });
    4         }
    • 转到指定Controller的特定Action。
    1       public ActionResult RedirectToRouteSample()
    2         {
    3             return RedirectToRoute(new { controller = "Member", action = "list" });
    4         }
    • 转到MemberController的List Action,并且加上"page"这个RouteValue。
    1       public ActionResult RedirectToRouteSample()
    2         {
    3             return RedirectToRoute(new { controller = "Member", action = "list", page = 3 });
    4         }

    七、ViewData与TempData概述

      7.1  ViewData

        ViewData属性是一个ViewDataDictionary类,可用于存储任意对象的数据,但存储的键值必须为字符串。

        ViewData有一个特性,就是它只会存在于当前的HTTP请求中,而不像Session一样可以讲数据带到下一个HTTP请求。

      7.2  TempData

        TempData的数据结构与ViewData一样,都属于字典类,不过TempData属性的类是TempDataDictionary。TempData嗨有一点不一样的地方,就是它的内部是用Session来存储数据的。存储在TempData中的数据只会暂存:1次网页请求!

        1次网页请求的定义:当窗口数据被传送到以下Action中进行存储时,如果发生新建数据失败的情况,我们会希望这次传送的数据可以保留至下一个页面,此时,就会将这个只希望出现1此的信息保存到TempData中,并在下一个页面中执行。

    复制代码
     1        [HttpPost]
     2         public ActionResult Create(Message msg)
     3         {
     4             if (!UpdateMessageToDB(msg))
     5             {
     6                 TempData["PostedMessage"] = msg;
     7                 return RedirectToAction("Create");
     8             }
     9             return RedirectToAction("Index");
    10         }
    复制代码

        当新建数据失败的情况发生后,将会返回"Create"这个Action,并将原来的数据从TempData中读出,示例如下:

    1         [HttpGet]
    2         public ActionResult Create()
    3         {
    4             string data = TempData["PostedMessage"] as Message;
    5             return View(data);
    6         }

        此时,数据就会回到Create()方法,并在此被传送到Create视图。在这个Controller生命周期结束的前一刻,由于MVC会记录"TempData["PostedMessage"]"语句已经被读取了,因此,在网页请求结束之前会将"TempData["PostedMessage"]"语句删除。

    八、模型绑定

        8.1  简单模型绑定

        8.2  使用FormCollection类获取窗口数据

        除了可以通过简单模型绑定机制获取窗口传过来的单栏数据之外,我们也可以通过FormCollection类一次获取整个窗口传送过来的数据。只要设定一个FormCollection类的参数,就可以获取所有从窗口传送过来的数据。这种用法与使用Request.Form()方法一样,不过在MVC里还是建议尽量不要用Request.Form()方法来获取窗口数据。

    1       public ActionResult GetFormCollection(FormCollection form)
    2         {
    3             ViewData["uName"] = form["uName"];
    4             return View("ModelBinderDemo");
    5         }

        8.3  复杂模型绑定

        8.4  多个复杂模型的绑定

        好比一个HTML代码中只有一个Form(表单)窗口,但是该窗口内有两组字段,是因为在编写数据绑定类里面设定了两组参数,分别为form1和form2,而这两组参数的类都是GuestbookForm,如:

    1       public Action ComplexModelBinding(GuestbookForm form1, GuestbookForm form2)
    2         {
    3             InsertIntoDB(form1);
    4             InsertIntoDB(form2);
    5             return Redi("/");
    6         }

        只需要做好模型绑定即可。

       8.5  判断模型绑定的结果

        验证数据,用自身的方法如Html.ValidationMessageFor()方法用来显示特定字段的错误信息。也可在Controller中判断业务逻辑。

    九、动作过滤器(略)

  • 相关阅读:
    BroadcastReceiver总结
    一二三(The Seventh Hunan Collegiate Programming Contest)
    使用 JQueryMobile 点击超链接提示“error loading page” 错误
    盒子游戏(The Seventh Hunan Collegiate Programming Contest)
    遗留系统升级改造方案思路
    根据外接鼠标控制笔记本触摸板禁用或启用
    设计模式之策略模式
    android4.3环境搭建
    清晰明亮的白色lua协程(coroutine)
    基于JUnit和Ant测试程序正在运行使用Kieker(AspectJ)监测方法
  • 原文地址:https://www.cnblogs.com/Alex80/p/6429135.html
Copyright © 2011-2022 走看看