Asp.Net MVC学习总结(二)——控制器与动做(Controller And Action)

1、理解控制器

1.一、什么是控制器

控制器是包含必要的处理请求的.NET类,控制器的角色封装了应用程序逻辑,控制器主要是负责处理请求,实行对模型的操做,选择视图呈现给用户。javascript

简单理解:实现了IController接口,修饰符必须是public,不能是抽象的,不能是泛型的,类名必须以Controller结尾。html

在MVC框架中,控制器类必须实现System.Web.Mvc命名空间下的IController接口,如上图所示,这是一个很是简单的接口,该接口仅有一个Execute方法,当请求该控制器时Execute方法被调用。经过实现IController接口,你能够建立控制器类。java

1.二、控制器的做用

a、每个针对应用程序的请求,都是经过控制器自由地选择合适的方式来处理的,只要它不偏离到视图(View)和模型(Model)所负责的区域。编程

b、不要把业务或数据存储的逻辑放到控制器里面,也不要建立用户接口。json

1.三、建立实现IController接口的控制器

示例: 建立一个实现Icontroller接口的类,读取路由数据,并生成数据写入响应。设计模式

在Controllers文件夹下建立一个名为MyFirstController的类,实现IController接口并添加以下代码浏览器

运行该应用程序并在地址栏导航到/MyFirst,即可以看到此控制器产生的输出。服务器

建立一个类经过实现IController接口,MVC框架会将其视为一个控制器,并将请求发送给它,并且在如何处理和响应请求上没有任何限制,这是一个很好的示例,由于它向你展现了MVC框架的可扩展性,但用这种方式编写一个复杂的应用程序是很是困难的。数据结构

1.四、建立继承于Controller类的控制器

经过System.Web.Mvc.Controller类你能够派生你的控制器,System.Web.Mvc.Controller类是大多数Web开发人员须要熟悉的,用来对请求处理提供支持的一个类,Controller提供了如下三个关键特性。并发

(1)、动做方法(Action Method):一个控制器的行为被分解成多个方法(而并不是只有惟一的Execute()方法)。每一个动做方法被暴露给不一样的URL,并经过从输入请求提取的参数进行调用。

(2)、动做结果(Action Result):你能够返回一个描述动做结果的对象(例如:渲染一个视图,或重定向到一个不一样的URL或动做方法),而后经过该对象实现你的目的。这种指定结果和执行之间的分离简化了单元测试。

(3)、过滤器(Filter):你能够把可重用的行为封装成过滤器,而后经过在代码中添加特性的的方式,把这种行为标注到一个过多个控制器或动做方法上。

 除非在头脑中有一个很是明确的需求,不然建立控制器最好的办法就是经过Controller类进行派生,这也正是你在Visual Studio中添加一个控制器,Visual Studio为你所作的事情。

在Controllers文件夹下建立一个名为MySecondController的类,继承与Controller类,而后添加一个动做方法TestAction并编写以下代码返回一个动做结果,最后再该动做方法内右键添加对应的视图。

运行应用程序并导航到/MySecond/TestAction浏览结果以下:

做为Controller类的一个派生类,所要作的工做是实现动做方法、获取所须要的各类输入,以对请求进行处理,并生成一个适当的响应。后面的内容将介绍数据的接收与响应。

2、控制器对数据的接收

2.一、数据来源

a、查询字符串值 b、表单数据 c、路由数据

控制器须要常常访问来自输入请求的数据,如查询字符串表单数据、以及由路由系统根据输入的URL解析获得的参数的值。访问这些数据有三种主要方式。

(1)、从上下文对象提取。

(2)、做为参数被传递给动做方法(Action Method)而造成的数据。

(3)、明确调用框架的模型绑定(Model Binding)功能。

注意:参数名称是忽略大小写的,如Request["Test"]与Request["test"]结果是同样的。以下图:

View部分

Controller部分

2.二、经过上下文对象获取数据

当建立一个从Controller基类派生的控制器时,就可以访问一组很是便利的属性,这些属性包括:Request、Response、RouteData、HttpContext、Server等,每个属性都包含了请求不一样方面的的信息。在Action方法里是可使用任何Context对象来访问这些属性。例如:

 1  public ActionResult Index()
 2         {
 3             string userName = User.Identity.Name;
 4             string serverName = Server.MachineName;
 5             string clientIP = Request.UserHostAddress;
 6             DateTime dateStamp = HttpContext.Timestamp;
 7 
 8             string oldProductName = Request.Form["OldName"];
 9             string newProductName = Request.Form["NewName"];
10 
11             ViewBag.Message = "本机的IP是:" + clientIP;
12             return View();
13         } 

能够利用VS智能感知,在动做方法中输入this.找到这些可用的上下文信息。

2.三、使用动做(Action)方法参数

2.3.一、使用Action方法参数

 

下面的方式是否是比上面的方法更优雅易读呢?不过须要注意的是:Action方法里面是不容许有ref或out参数的,虽然编译不会报错但运行时会抛出一个异常。以下所示。

MySecond控制器代码以下:

Index视图代码以下:

运行结果以下:

 

2.3.二、理解参数对象实例化

Controller基类使用叫作“值提供器(Value Provider)”和“模型绑定器(Model Binder)”的MVC框架组件来获取动做方法的参数值。值提供器将可用的数据项集合呈现给控制器。有一组内建的值提供器从Request.For

m、Request.QueryString、Request.Files、RouteData.Values获取数据项,而后这些值会被传递给模型绑定器,模型绑定器会尝试将这些数据映射成动做方法参数的数据类型。默认的模型绑定可以建立和填充任何.NET类型的对象,包括自定义的类型和集合。

2.3.三、理解可选参数与必须的参数

若是MVC框架找不到引用类型参数(如:string或object)的值,动做方法仍然会被调用,但对该参数会使用一个null值,若找不到值类型参数(如:int或double)的值则会抛出一个异常,而且不会调用动做方法。

a、值类型参数是必须被赋值的。若是想让此参数和引用类型参数同样,能够定义成int?如:public ActionResult Index(int? num),当依然没有值时,不会发生异常,而是会传递null值。

b、引用类型的参数是可选的。为了使它成为必须的(保证一个非空的值被传递),在动做(Action)方法上添加一些代码拒绝null。例如,在该值等于null时,抛出一个ArgumentNullException异常。

2.3.四、指定默认参数值

若是但愿处理不含动做方法参数的请求,但又不想在代码中检查null值或抛出异常,可使用C#的可选参数特性来代替。以下所示:

1  public ActionResult List(string query = "all", int page = 1)
2         {
3             //此处省略代码N行...
4             return View();
5         }

在定义参数时,经过对参数赋值的办法,能够将参数标记为可选的,如上诉代码中,给query和page参数提供了默认值。MVC框架会试图经过请求为这些参数获取值,但若是没有值可用,那么将用所指定的默认值代替。

对于string类型参数query,注意string类型是引用类型,这意味着不须要检查null值。若是请求为指定查询字符串,那么该动做(Action)方法将以字符串“all”进行调用。对于int类型参数,注意int类型是值类型,在没有page值时,请求不会致使错误,该方法将以默认值“1”进行调用。

3、控制器对数据的响应

3.一、理解动做结果(Action Result)

原理:

a、MVC框架接收从Action方法返回的ActionResult对象,并调用定义在ActionResult类里面ExecuteResult方法,对ActionResult进行实现,为咱们处理Response对象并生成相关的输出。

b、MVC框架内置了许多ActionResult类型,都是从ActionResult类派生的,可以方便咱们在Action方法里面选择具体的返回类型,好比要呈现到View,能够选择ViewResult做为Action方法的返回值。

MVC框架含有许多内置的动做结果类型以下图所示,全部这些类型都是派生于ActionResult,其中在Controller类中有便利的辅助器方法。下面将解释如何使用这些结果类型。

MVC框架支持的输出类型有: 1.视图 2.文本数据 3.XML数据 4.JSON数据 5.文件或者二进制数据 6.返回错误和HTTP Codes 7.定制的ActionResult 8.重定向

3.二、经过渲染视图(View)返回HTML

最多见的来自Action方法的响应就是生成HTML并发送给浏览器,为了使用动做结果(ActionResult)生成HTML,须要建立一个指定了要呈现的视图ViewResult类的实例。咱们在Home控制器中编写以下代码.代码中指定了HomePage的视图。

当MVC框架调用ViewResult对象的ExecuteResult时,就会开始对指定的View进行搜素。

使用了区域(Area),则搜索顺序以下:

一、/Area/<AreaName>/Views/<ControllerName>/<ViewName>.aspx

二、/Area/<AreaName>/Views/<ControllerName>/<ViewName>.ascx

三、/Area/<AreaName>/Views/Shared/<ControllerName>/<ViewName>.aspx

四、/Area/<AreaName>/Views/Shared/<ControllerName>/<ViewName>.ascx

五、/Area/<AreaName>/Views/<ControllerName>/<ViewName>.cshtml

六、/Area/<AreaName>/Views/<ControllerName>/<ViewName>.vbhtml

七、/Area/<AreaName>/Views/Shared/<ControllerName>/<ViewName>.chtml

八、/Area/<AreaName>/Views/Shared/<ControllerName>/<ViewName>.vbhtml

若是没有使用区域(Area)或者在前面找不到则会在下面搜索,搜索顺序以下:

一、/Views/<ControllerName>/<ViewName>.aspx

二、/Views/<ControllerName>/<ViewName>.ascx

三、/Views/Shared/<ControllerName>/<ViewName>.aspx

四、/Views/Shared/<ControllerName>/<ViewName>.ascx

五、/Views/<ControllerName>/<ViewName>.cshtml

六、/Views/<ControllerName>/<ViewName>.vbhtml

七、/Views/Shared/<ControllerName>/<ViewName>.chtml

八、/Views/Shared/<ControllerName>/<ViewName>.vbhtml

只要有一个视图找到,就中止搜索,并开始将找到的视图(View)呈现给客户端。

经过路径来指定呈现的视图(View)

这种命名约定的方法很是方便和简捷,可是它限制了咱们所能呈现的一些视图。若是要呈现一个具体的视图,能够提供一个明确的路径,下面是一个例子。

当咱们这样指定一个视图时,指定的路径必须以“/”或者“~/”而且包含扩展名(如:.aspx)。固然不推荐这样来使用,由于这不利于程序的扩展和维护,这是一种绑定或耦合,有违MVC设计思想,能够有其余的方法来达到一样的效果例如:使用RedirectToAction()方法。

View辅助方法

View():返回到Action同名的视图。

View(“viewName”):返回到此控制器的ViewName视图。

View(“~/views/othercontroller/viewname.cshtml”):这种方式必须以~/或者/开头,但并不建议这么作,由于能够调用RedirectToAction这样的方法。

View(“viewname”,”layout”):呈现这个视图的时候,换一个母版页。

3.三、将数据从动做(Action)方法传递给视图(View)

传输数据的几种方式:1.视图模型  2.ViewData  3.ViewBag  4.TempData

3.3.1、提供视图模型对象

将一个对象做为View方法的参数传递给视图。例如:

Controller部分

1 public ViewResult Index2()
2 {
3    DateTime date = DateTime.Now;
4    return View(date);
5 }

View部分,添加视图时,选择Razor视图引擎。

1 @{ 
2     ViewBag.Title = "Index2"; 
3 } 
4 <h2>Index</h2> 
5 The day is: @(((DateTime)Model).DayOfWeek)  

上面的视图是一个没有类型或者说是弱类型的视图,它不知道关于视图模型对象的任何信息,而且将它做为object对象的实例进行处理。为了获得DayOfWeek属性的值,须要将object对象的实例强转为DateTime,这样作可以实现效果,但却让视图变得杂乱。

咱们能够经过建立强类型的View来改进,即在View里指定视图模型对象的类型,只须要添加代码:@model DateTime,以下所示:

1 @model DateTime
2 @{ 
3     ViewBag.Title = "Index2"; 
4 } 
5 <h2>Index</h2> 
6 The day is: @Model.DayOfWeek 

运行结果以下:

能够发现,使用强类型的视图不只让视图变得整洁,并且方便咱们编码,由于对属性有智能感知。以下图所示:

 

3.3.2、使用ViewBag传递数据

在前面咱们已经使用过了ViewBag视图包这个特性,该特性容许你在一个动态对象上定义任意属性,并可以在视图里面访问。以下所示:

Controller部分

1  public ViewResult Index2()
2         {
3             ViewBag.Message = "Hello ViewBag!";
4             ViewBag.Date=DateTime.Now;
5             return View();
6         }

View 部分

 1 @{
 2     Layout = null;
 3 }
 4 
 5 <!DOCTYPE html>
 6 
 7 <html>
 8 <head>
 9     <meta name="viewport" content="width=device-width" />
10     <title>Index2</title>
11 </head>
12 <body>
13     <div>
14         the message is:@ViewBag.message<br/>
15          the day is:@ViewBag.Date.DayOfWeek
16     </div>
17 </body>
18 </html>

运行效果以下:

相对于视图模型对象方面,ViewBag有一个优势,即它可以很容易地发送多个对象到视图。假如咱们被限制只能使用视图模型,那么为了实现相同的效果须要建立一个新的类型具备stringDateTime两个类型的的成员。使用动态对象能够在视图中输入属性和方法调用的任意序列。

3.3.3、使用ViewData传递数据

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

Controller部分

1  public ViewResult Index2()
2         {
3             ViewData["message"] = "Hello ViewBag!";
4             ViewData["Date"]=DateTime.Now;
5             return View();
6         }

View部分 

 1 @{
 2     Layout = null;
 3 }
 4 
 5 <!DOCTYPE html>
 6 
 7 <html>
 8 <head>
 9     <meta name="viewport" content="width=device-width" />
10     <title>Index2</title>
11 </head>
12 <body>
13     <div>
14         the message is:@ViewData["message"]<br/>
15         the day is:@(((DateTime)ViewData["Date"]).DayOfWeek)
16     </div>
17 </body>
18 </html>

运行结果与VIewBag运行结果一致。

上面的代码中,咱们看到ViewData须要对object对象进行类型转换,如今有了ViewBag之后,推荐使用ViewBag,而且尽可能使用强类型视图和视图模型。

3.3.四、使用TempData传递数据

3.4.四、使用TempData保留重定向数据】节中会介绍使用TempData传递数据,这里不做介绍。

3.四、执行重定向

有一种动做(Action)方法的一般结果并非直接产生输出,而是把用户的浏览器重定向导另外一个URL。大多数状况下,这个URL是应用程序的另外一个动做(Action)方法,用来生成你但愿用户看到的输出。

重定向的Action方法不产生任何的输出,只是让浏览器从新请求一个其它的URL。在MVC程序中,通常会定向到其它的Action方法来产生输出。

在执行重定向时,发送了两个HTTP代码中的一个到浏览器。

(1)、发送HTTP 302状态编码,表明暂时重定向。(经常使用类型)

(2)、发送HTTP 301状态编码,表示永久重定向。(使用需谨慎)

重定向的类型有:1.重定向到文本URL   2.重定向到路由系统的URL  3.重定向到动做(Action)方法

3.4.一、重定向到文本URL

对浏览器重定向最基本的方式是调用Redirect方法,它返回RedirectResult类的一个实例。以下示例:

若是但愿重定的URL被表示成一个字符串,并做为参数传递给Redirect方法。Redirect方法发送的是一个临时重定向。能够用RedirectPermanent方法发送一个永久重定向。例如:

 

3.4.二、重定向到路由系统的URL

使用重定向到文本URL的缺点是:限定了URL,当路由发生改变后,必须更新URL。幸亏咱们可使用路由系统,用RedirectToRoute方法来生成有效的URL该方法会建立 RedirectToRouteResult的一个实例。以下所示:

3.4.三、重定向到动做(Action)方法

使用RedirectToAction方法可以很优雅的重定向到一个Action方法,这个方法仅仅是对RedirectToRoute方法的封装,让你指定Action方法和控制器的值,而不须要建立一个匿名类型。以下所示:

若是想重定向到其余的控制器,须要提供一个控制器的名称如:

注意:传入的Action参数或控制器参数在它们被传递给路由系统以前是不会被验证的,因此要确保目标控制器和Action方法是存在的。

3.4.四、使用TempData保留重定向数据

 重定向会引发浏览器提交整个新的HTTP请求,这意味着没法访问原始的请求的细节。若是想将数据从一个请求传递到下一个请求,可使用TempData()方法。

TempData相似于Session,不一样的是TempData在被读取之后会被标记删除,而且当请求被处理的时候被移除。这是一个针对想在整个重定向过程当中保持短时间数据的很是完美的安排。以下示例:

首先添加一个MySecond控制器,并添加对应的视图,视图引擎为Razor并编写以下代码:

 MySecond控制器代码

 1  public class MySecondController : Controller
 2     {
 3         public ActionResult Index()
 4         {
 5             TempData["Message"] = "Hello TempData";
 6             TempData["Data"] = "TempData的值只能读取一次";
 7             return View();
 8         }
 9         public ActionResult TempDataTest()
10         {
11             return View();
12         }
13     }
14     

Index视图代码

 1 @{
 2     Layout = null;
 3 }
 4 
 5 <!DOCTYPE html>
 6 
 7 <html>
 8 <head>
 9     <meta name="viewport" content="width=device-width" />
10     <title>Index</title>
11 </head>
12 <body>
13     <div>
14         the Data is:@TempData["Data"]
15     </div>
16 </body>
17 </html>

TempDataTest视图代码

 1 @{
 2     Layout = null;
 3 }
 4 
 5 <!DOCTYPE html>
 6 
 7 <html>
 8 <head>
 9     <meta name="viewport" content="width=device-width" />
10     <title>TempDataTest</title>
11 </head>
12 <body>
13     <div>
14         the Data is:@TempData["Data"]<br/>
15         the Message is:@TempData["Message"]
16     </div>
17 </body>
18 </html>

运行结果以下:

注意:在Index里面没有读取TempData["Message"]的值,可是Index和TempDataTest都读取了TempData["Data"]的值。这样作是为了验证TempData里面的值在不一样的视图只能读取一次。

TempData里面的值在一次请求里面只能读取一次。 若是咱们想读取TempData的值可是又不让它被删除,可使用TempData.Peek(“Data”)方法。 若是想在保持一次TempData里面的值,可使用TempData.Keep("Data")。

3.五、返回文本数据

 

Content方法参数说明: 发送的文本数据、响应的HTTP content-type header、编码格式。 能够忽略最后两个参数,在MVC框架假定数据是HTML(content type为text/html)状况下,它会查询一种浏览器声明支持的编码格式。

3.六、返回XML数据

 1 using LinqService;
 2 using System.Xml.Linq;
 3 public ContentResult GetXMLData()
 4 {
 5     List<Books> books = bll.GetListBooks();
 6     XElement data = new XElement("BooksList", books.Select(e =>
 7     {
 8        return new XElement("Books",
 9          new XAttribute("title", e.Title),
10          new XAttribute("author", e.Author),
11          new XAttribute("Price", e.Price.ToString()));
12     }));
13 }

3.七、返回JSON数据

 1   public ActionResult JSON()
 2         {
 3             JsonResult json = new JsonResult();
 4 
 5             UserInfo userinfo=new UserInfo{ ID=1, Name="张三", Age=11 };
 6 
 7             List<UserInfo> list=new List<UserInfo>{
 8                 new UserInfo{ ID=1, Name="张三", Age=11 },
 9                 new UserInfo{ ID=1, Name="张三", Age=11 },
10                 new UserInfo{ ID=1, Name="张三", Age=11 },
11                 new UserInfo{ ID=1, Name="张三", Age=11 }
12             };
13             //json.Data = list;
14             //json.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
15             //return json;
16             //return new JsonResult() { Data = list, JsonRequestBehavior=JsonRequestBehavior.AllowGet };
17 
18             return  Json(userinfo,JsonRequestBehavior.AllowGet);
19         }

JSON是一个轻量级的基于文本格式,用来描述分层数据结构的, JSON是有效的javascript代码,这意味着被全部的主流浏览器支持

在MVC框架里面内置了JsonResult类,让咱们可以序列化.NET对象为json格式。经过Json方法能够建立返回JsonResult结果类型

3.八、返回文件和二进制数据

1 public FileResult AnnualReport()
2 {
3    string filename = @"D:\C#高级编程.doc";
4    string contentType = "application/doc";
5    string downloadName = "C#高级编程2013.doc";
6    return File(filename, contentType, downloadName);
7 }

若是不知道具体的MIME类型,能够指定contentType 为application/octet-stream

lFileResult是一个抽象基类,其三个实现的子类 :

1.FilePathResult :直接把服务器的某个路径下的文件发给浏览器

2.FileContentResult :发送一个内存中的二进制数据给浏览器

3.FileStreamResult:发送已经打开的文件流内容给浏览器。

3.九、返回错误和HTTP Codes 

发送特定的HTTP结果码

可使用HttpStatusResult类将一个特定的HTTP状态码发送给浏览器。这个类没有对应的控制器辅助器方法,所以必须直接对这个类进行实例化,以下所示:

1 public HttpStatusCodeResult StatusCode()
2 {
3      return new HttpStatusCodeResult(404,"file not found");
4 }

发送404结果

可使用便利的HttpNotFoundResult类来获取上面相同的效果,这个类派生于HttpStatusResult,并且能够用控制器的便利方法HttpNotFound来建立。以下所示:

1  public HttpStatusCodeResult StatusCode()
2 {
3    return HttpNotFound();
4 }

发送401结果

另外一个特定的HTTP状态码的封装程序类是HttpUnauthorizeResult,它返回401代码,用来指示一个未受权请求。以下所示:

1  public HttpStatusCodeResult StatusCode()
2  {
3      return new HttpUnauthorizedResult();
4  }

总结:在本文章中,主要是对MVC设计模式中的控制器和动做方法作了详细介绍,主要从实现自定义的控制器类两种方式着手,而后介绍了控制器对数据的接收与响应系列内容。对数据接收主要来源查询字符串值、表单数据、路由数据作了分别介绍,对数据的响应包括视图 、文本数据、XML数据、JSON数据、文件或者二进制数据、返回错误和HTTP Codes、定制的ActionResult、重定向作了分别介绍。最后说明一下本文章的部分理论观点引用自《Pro ASP.Net MVC 5.pdf》这本书。

相关文章
相关标签/搜索