快捷搜索:  as  test  1111  test aNd 8=8  test++aNd+8=8  as++aNd+8=8  as aNd 8=8

黄金城vip线路检测:【ASP.NET MVC4 IN ACTION】学习笔记(五)Action的控制器(Actionspacked Controllers)



本次覆盖常识点:

1. 如何才算一个节制器(What makes a controller)

2. 节制器中的成员有什么 (What belongs in a controller)

3. 手动映射视图模型 (Manually mapping view models )

4. 验证该用户的输入 (Validating user input )

5. 应用默认的单元测试模版(Using the default unit test project )

在近来两章里,我们看了一下创建一个Guestbook利用法度榜样根基常识,也看了几种不合的把数据通报给视图的可以实现的要领。在本章里,我们将会完成Guestbook例子,我们只添加一点点细节部分。我们将钻研什么应该是controller中的一部分,什么不应该是,然后看一下如何手动构造一个视图models,验证用户的输入,写几个不会应用到视图的节制器操作(action)。这个将会让我们在controller中建造一个很常用action块(block),也便是不返回view类型action代码,我们曩昔学的都是返回一个view(),这里就不是的了。

我们也将向你简单地先容一下单元测试controller中的action,这样可以确保这些action可以精确的事情。我们将会应用默认的单元测试项目,然后为GuestbookController创建单元测试,测试前面几章的事情环境。

在我们进修这些新常识之前,我们先快速地概括一下controllers和actions

4.1 探究Controllers和actions

备注:controller action指controller中的action(也便是你们常常写的措施而已)

controller actions指controller中整个actions

action名字加action表示 某某名字的action,例如有个叫Index的action,我就会这样表示

Index action.知道了吗?这方便我写博客,也更方便理解

view model指页面(view)顶用到model,我上篇博客说的视图模型便是view model

action method指 返回值是ActionResult那个措施,比如Index那个action措施

user interface,用户接口,你可以领悟理解成页面,也便是要跟用户交互的器械,比如我们给用供给一个页面,让用户录入数据,这便是一个interface,一个接口,你暂且在这里便是view,便是页面

business logic,商业逻辑,类似于数据造访层中营业实现的逻辑

domain,你可以理解数据造访层

domain model,数据造访层要应用到的model

今后我想直接写英文了,好方便理解,在上一波博客我是吃过苦了,文章读起来好拗口

在第一波里面我们看出来了Controller的紧张性,它里面有很多措施(action),这些action都可以经由过程url实现触发调用.而这些action便是把model中的数据传到页面上的中心者,也可以理解为搬运工吧,以是理解action是如何事情的是很紧张的.鄙人一节,我们主要更具体地懂得下controller actions是如何事情的.

下面我们看下action的样子(GuestbookController中的Index action)

它承袭了Controller,也含有默认的action,我们也知道所有的节制器必须承袭Controller,着实框架容许我们只要至少实现IController否则,它是不能处置惩罚Web哀求的

下面我们看看框架是怎么知道哪个class是个被当做controller来处置惩罚,由于controller说白了也是一个类而已.下面让我们看看

IController

4.1.1IController和Controller 父类(base class)

IController定义了一个controller最根本的东东---Execute措施吸收一个RequestContext类型的工具

下面是一个简单的controller,我们写一些html放到相应流里面(response stream)

我们手动实现一下:

这是一个实现了IController的黄金城vip线路检测节制器,我们实现了Execute措施,我们就能直接造访HttpContext,Request还有Response工具了.这种要领很轻易定义然则没什么用.这样做,我们就无法直接出现view.而且我们这样做---在controller中直接就写HTML,把逻辑和出现的内容混在一路了,很难熬惆怅.

好吧,我们先不看框架那些有用的特性,比如说安然性(第8章会讲),model binding(第10章),action results(第16章).

这样做,我们也丢掉了定义action措施的能力----------由于所有的哀求都被Execute处置惩罚了

实际上,你必要去实现IController也不太可能那样去做,由于它本身没什么用(避开框架的主要部分,有一些来由能让你觉的照样有用的).平日我们会承袭父类----------ControllerBase和Controller

ControllerBase

ControllerBase类除了包孕了我们已经看过的一些基础特性以外,它本身就直接实现了IController.举个例子:ControllerBase也包孕ViewData属性(把数据出给view的一个手段).然则ControllerBase仍旧照样不怎么有用-----它仍旧照样不能经由过程应用action来出现view.以是Controller来了

Controller

Controller承袭ControllerBase,以是除了包孕一些故意义的器械以外,它还包孕了ControllerBase定义的东东(比如ViewData).它包孕了ControllerActionInvoker(这是一个知道如何根据url找到对应的措施(method),并履行了那个措施,它定义了一些措施,比如View(可以经由过程controller action来出现一个view))

这是一个当你开初创建好你的controller的时刻会自动承袭的类,以是说直接承袭ControllerBase或者IControlle黄金城vip线路检测r都不会有什么赞助.然则我们知道了在MVC 管道(pipeline)模型中扮演很紧张的角色,知道它们的存在照样很有用的.

好了,现在我们已经知道了如何把一个类变成一个Controller.下面我们看看action

4.1.2如何才可以成为一个action method

在第二章(我的第一波博客,我从第二章开始讲的,第一章是先容,今后我会按黄金城vip线路检测书里面的章节顺序讲,还盼望谅解)我们在controller中看到了一些public的action措施(事实上,抉择一个措施能否成为一个action,是有条规律规则的,讲起来照样蛮繁杂的,我们将会在第16章开始解说).

寻常呢,action method都是返回一个ActionResult的一个实例.举个例子,一个action返回值可所以void类型的,也可以直接输出HTML(很像我们刚刚的SimpleController)

我们也可以这样写,它们结果是一样的,返回一个HTML片段(snippet)

这个没有问题,由于ControllerActionInvoker它包管了action的返回值老是ActionResult.假如action返回的是一个ActionResult(例如ViewResult),ControllerActionInvoker就会被调用.然则假如action返回的一个不合的类型(就像这里,一个string),它的返回值是ContentObject工具(也是一个把要写入的器械放入相应流里面的东东),直接应用ContentResult也是一样的

这便是一个很简单的action了,不必要view就可以直接在浏览器中出现HTML标签了。然则在真实天下的利用法度榜样中平日不会这样用的,照样最好经由过程view把要出现的页面和controller分开,这样做当我们改变user interface的时刻,就不用动controller中的代码了

除了出现标签或者返回一个view,还有其他几种类型,举个例子,我们可以应用RedirectToRouteResult,让用户在浏览的时刻跳到其他页面(我们在第二章(我的第一波博客)中的RedirectToAction措施你已经应用过了,还记得吗?),我们也可以返回JSON(在第七章的AJAX中我们将会学到)

我们可以应用NonActionAttribute让一个controller中的public的action不是一个action。

NonActionAttribute是一个action措施选择器(action method selector),它可以重写 匹配一个措施名到一个action名的默认行径(behavior).NonActionAttribute是最简单的一个选择器,它可以使那些可以经由过程URL造访的措施弗成以经由过程这种手段来造访,其其实第二章,我们也已经看到了一些选择器,比如HttpPostAttribute(可以确保这个action可以相应HTTP post形式的哀求,其他要领都是弗成以造访的)

提醒

NonActionAttribute很少应用的,在一个controller中一个public的措施你不想它成为action,你最好想一下controller中是不是它最好的地方,假如这个措施有用,你可能会写成private造访修饰符级其余。假如这个措施必须public,由于必要测试的缘故原由,我感觉应该提取成一个零丁的类

现在让我简单地看一下什么构成了action,你将会看到一些不合的要领,把内容放到浏览器中显示。除了view,你也可以直接发送content(内容),或者直接履行其他的action(重定向),这些技巧在你的利用法度榜样中都邑很有用。

现在让我们去看一下action中的逻辑

4.2在action method中有什么

MVC中最大年夜的两点便是将各自的职责分离,user interface 和 逻辑(比如页面提交数据怎么处置惩罚)分开,是以全部法度榜样就更轻易掩护(maintain)了。假如你没有让你的controller更精美(lightweight实际是轻量级的意思),把重点都放在controller里,你就不会体会到它的好处了。

Controller应该扮演中心者(coordinator)--它不应该包孕business logic。反之亦然(vice versa),它可以把view上的表单,用户的输入的数据(user input),来自view的,封装成一个工具,然后我们把这个工具在domain(实际business logic代码写

在的地方)层中应用,这样我们数据的存储都可以完成了。

让我们看一下经由过程controller若何把一个义务完成--手动的映射view models,吸收用户的输入的数据。首先,展现的是若何匹配view models,现在我们打开我们的guestbook例子,添加一个新的页面, 用不合的要领显示数据,存数据就先放着。接下来我们在这个页面上,把数据添加到数据库之前先添加一些验证,由于我们的数据库不必要存储一些没有用的(invalid)数据。在本节停止之后,你应该在知道如何构建一个view model了,如何完成基础的input验证,应该有个大年夜致的懂得了

4.2.1 手动地去匹配viewmodels(view models 指view应用到的model)

在上一篇,我们已经有了强类型视图的印象,还有一个view model--为了能够把数据更好地显示在屏幕上,创建的而一个零丁的model工具。

到今朝为止,我们做的例子中,我们应用到了同样的类(GuestbookEntry)作为我们的domain model和view model--它展现了我们数据库中的信息,也是在user interface上要表示的字段,信息

在异常小的项目中应用,比如我们的guestbook,这还可以,然则跟着法度榜样越来越繁杂,用户界面也繁杂了,model中的数据已经不能直接匹配了,就有需要要分成两部分(题外话:我现在自己公司做的项目便是两部分,一个EF天生的实体,一个扩充的实体,都已Dto结尾,只是为了更方便的显示数据),恰是因为这个,我们应该有能力可以把domain model转换成view model,让数据更轻易在user interface显示

我们就写个例子吧,向我们的Guestbook项目上添加一个页面:做一个总结页面,知道每一小我发了若干个comment,首先我们创建一个view model(view model专门让页面好展现数据,就看你怎么设计了)

在Models文件下建立CommentSummary.cs

我们现在必要创建一个controller action----返回一个CommentSummary聚拢,我们在GuestbookController中写action

1:public ActionResult CommentSummary()

2:{3:var entries = from entry in _db.Entries

4:group entry by entry.name into groupByName5:orderby groupByName.Count() descending

6:select new CommentSummary7:{

8:NumberOfComments = groupByName.Count(),9:UserName = groupByName.Key

10:};11:return View(entries.ToList());

12:13:}

我们应用linq查询,不懂的可以看一下 http://www.fengfly.com/plus/view-212474-1.html

这个映射(mapping)逻辑相称简单,把这些代码写在节制器你照样可以讲得通的,然则假如这个mapping变得加倍繁杂(举个例子,假如它取了很多来自不合数据源的信息,只是为了构造view model),我们应该把这些逻辑从controller action中移出,分离(就像曩昔的三层框架),让我们的controller加倍简单,轻量级的

在view中,我们轮回输出这个列表,用table展现

右键该action名称,维持默认,添加一个视图

1:@model IEnumerable

2:3:

4:

5:Number of comments

6:User name7:

8:@foreach(var summaryRow in Model) {9:

10:

@summaryRow.NumberOfComments11:

@summaryRow.UserName

12:13:}

14:

自动匹配View models

除了手动地把domain object和view models进行映射(mapping)以外,你也可以应用对象,比如开源的AutoMapper,像这个目的,更少的代码就可以完成了,我们将在第11章中MVC项目中教你怎么应用AutoMapper

在这一节,我们已经很简短地看了一点view model,然则鄙人一章我们将要更仔细地看,我们当然也会钻研view models和input models的不合点

除了mapping操作,controller中还有个常常用到的义务,验证用户输入

4.2.2 验证用户输入(input validation)

回到第二章,我们在GuestbookController.cs中的Create action吸收了用户的input(输入)。

这个action,吸收了Create.cshtmlpost过来的input的数据(一个GuestbookEntry工具(已经被MVC模型绑定(model-binding)机制处置惩罚了)的表单),设置一下日期,然后我们Insert一条数据到数据库,虽然已经完成了,但还没有真的完成--我们还没有任何的验证。此时,用户是可以不输入他们的name或者comment就可以提交(submit)数据的。现在让我们添加一些验证。

首先让我们在GuestbookEntry类顶用required特点 评释一下 Name和Message,应用using System.ComponentModel.DataAnnot黄金城vip线路检测ations;导入命名空间

1:using System;

2:using System.Collections.Generic;3:using System.ComponentModel.DataAnnotations;

4:using System.Linq;5:using System.Web;

6:7:namespace GuestInfo.Models

8:{9:public class GuestbookEntry

10:{11:public int Id { get; set; }

12:[Required]13:public string name { get; set; }

14:[Required]15:public string Message { get; set; }

16:public DateTime DateAdded { get; set; }17:

18:}19:}

应用评释(Annotate),也是一种验证工具属性的措施,Required表示这个filed不能为空,还有一些其他评释,比如StringLengthAttribute:验证字符串长度的。(我们将会在第6章更深入进修一下)

一旦表清楚明了,当Create action被调用的时刻,MVC将会自动地验证这些属性,看是否验证经由过程,我们可以应用ModelState.IsValid属性,然后就可以做抉择了,怎么处置惩罚。改动一下Create

1:

2:[HttpPost]//限定只能是HTTP Post 能够造访这个措施3:public ActionResult Create(GuestbookEntry entry)

4:{5:if (ModelState.IsValid)

6:{7:entry.DateAdded = DateTime.Now;

8:_db.Entries.Add(entry);9:_db.SaveChanges();

10:return RedirectToAction("Index");11:}

12:return View(entry);13:}

留意

调用ModelState.IsValid实际上不会进行表单验证,它只是检考验证是掉败照样成功。验证发生在Controller action被调用之前

我们可以调用Html.ValidationSummary措施,当验证掉败的时刻,显示差错信息

运行页面,还没输入任何内容点击提交:

应用这些助手,MVC将会自动检测验证差错的信息,还利用了一个css样式,由于我们的法度榜样是基于默认的MVC项目模版上写的,这个无效的信息,将会显示浅血色背景致

这个差错的信息内容你可以这样改,在Required加参数

同样地假如你不想 硬编码消息,想要依附于经由过程资本文件,支持本地化,你可以指定ResourceName和Resource type

例如:

[Required(ErrorMessageResourceType=typeOf(MyResources),ErrorMessageResourceName="RequiredMessageError")]

public string Message { get; set; }

4.3 单元测试先容

在这一节,我们将要简短地看一下测试controllers。有很多测试的措施,这里我们主要讲:unit testing

单元测试很小,脚本测试,应用的说话和你项目中的说话一样。为了验证某个措施对纰谬,能不能达到想要的效果就去零丁隔离式地测试某个组件的措施或函数。跟着法度榜样的变大年夜,单元测试也会变多,看到一个利用法度榜样有成百以致上千个单元测试都是很正常的,它们可以在任何光阴履行,去验证bug,让bug不会再发生

为了让单元测试运行地更快,我们不能调用进程外貌的器械,这点很紧张。当测试一个controller中的代码,任何自力的部分都是模拟的,以是主要的产品代码照样controller它本身。针对这个有一种可能,节制器被设计成自力的部分,很轻易就被调用(比如说database或者web办事)

为了更高效地测试我们的GuestbookController,为了容许测试,我们要做一些改动,然则在我们做这些工作之前,让我们看一下ASP.NET MVC中默认的单元测试模版

4.3.1 应用默认的单元测试模版

默认地,当你创建一个新的ASP.NET MVC项目的时刻,VS会供给一个创建单元测试项目的选项(在第二章我们已经见过了)

假如你选择一个创建单元测试项目,vs将会天生一个using Visual Studio Unit Testing FrameWork。这个单元测试项目包括了一些示例的测试,比如在HomeControllerTest类可以望见

1:using System;

2:using System.Collections.Generic;3:using System.Linq;

4:using System.Text;5:using System.Web.Mvc;

6:using Microsoft.VisualStudio.TestTools.UnitTesting;7:using GuestBook;

8:using GuestBook.Controllers;9:

10:namespace Guestbook.Tests.Controllers11:{

12:[TestClass]13:public class HomeControllerTest

14:{15:[TestMethod]

16:public void Index()17:{

18:// 排列19:HomeController controller = new HomeController();

20:21:// 操作

22:ViewResult result = controller.Index() as ViewResult;23:

24:// 断言25:Assert.AreEqual("改动此模板以快速启动你的 ASP.NET MVC 利用法度榜样。", result.ViewBag.Message);

26:}27:

28:[TestMethod]29:public void About()

30:{31:// 排列

32:HomeController controller = new HomeController();33:

34:// 操作35:ViewResult result = controller.About() as ViewResult;

36:37:// 断言

38:Assert.IsNotNu黄金城vip线路检测ll(result);39:}

40:41:[TestMethod]

42:public void Contact()43:{

44:// 排列45:HomeController controller = new HomeController();

46:47:// 操作

48:ViewResult result = controller.Contact() as ViewResult;49:

50:// 断言51:Assert.IsNotNull(result);

52:}53:}

54:}

看代码,跟曩昔其他 客户端形式的单元测试照样很像的,很好就学会了,这里我就不多说了

4.3.2 测试GuestbookController

有一些工作,GuestbookController的实现直接初始化,应用了GuestContext工具,这个工具造访数据库。这便是说没稀有据库的条件下,要想精确的得到数据,这个测试弗成能进行,这是一个集成测试(Integration Test),而不是单元测试了

只管集成测试很紧张,它能确保一个利用法度榜样的组件是否精确的在共同,也便是说假如我们对在controller中测试这个逻辑感兴趣,我们不得不先测试数据库连接。在小数量的测试,这个可能,然则假如一个项目有成百上千个测试,履行光阴显着下降,由于每一个都要连接数据库。这个问题的办理规划是减弱节制器中的GuestbookContext工具

不直接应用GuestbookContext,我们先容一个repository,它供给了一个进口,在处置惩罚数据造访操作,操作GuestbookEntry工具,我们为我们的repository写个接口

这个接口定义了4个措施,匹配4个查询,对应GuestbookController

我们定义了包孕查询逻辑的观点上的实现

1:using GuestInfo.Models;

2:using System;3:using System.Collections.Generic;

4:using System.Linq;5:using System.Text;

6:using System.Threading.Tasks;7:

8:namespace Guestbook.Contract9:{

10:public class GuestbookRepository : IGuestbookRepository11:{

12:private GuestbookContext _db = new GuestbookContext();13:

14:public IList GetMostRecentEntries()15:{

16:return (from entry in _db.Entries17:orderby entry.DateAdded descending

18:select entry).Take(20).ToList();19:}

20:21:public void AddEntry(GuestbookEntry entry)

22:{23:entry.DateAdded = DateTime.Now;

24:_db.Entries.Add(entry);25:_db.SaveChanges();

26:27:}

28:29:public GuestbookEntry FindById(int id)

30:{31:var entry = _db.Entries.Find(id);

32:return entry;33:}

34:35:public IList GetCommentSummary()

36:{37:var entries = from entry in _db.Entries

38:group entry by entry.Name into groupedByName39:orderby groupedByName.Count() descending

40:select new CommentSummary41:{

42:NumberOfComments = groupedByName.Count(),43:UserName = groupedByName.Key

44:};45:return entries.ToList();

46:}47:}

48:}

接下来我们在GuestbookController中添加一些代码,把接口引进来

改动代码:

using Guestbook.Contract;

using GuestInfo.Models;using System;

using System.Collections.Generic;using System.Linq;

using System.Web;using System.Web.Mvc;

namespace GuestInfo.Controllers

{public class GuestbookController : Controller

{private GuestbookContext _db = new GuestbookContext();

IGuestbookRepository _respository;public GuestbookController() {

_respository = new GuestbookRepository();}

public GuestbookController(IGuestbookRepository gu){

_respository = gu;}

//// GET: /Guestbook/

public ActionResult Index()

{////Shift+Tab削减缩进,Tab增添缩进

//var mostRecentEntries = (from o in _db.Entries orderby o.DateAdded descending select o).Take(20);//// ViewBag.Entries = mostRecentEntries.ToList();

//var model = mostRecentEntries.ToList();

var mostRecent = _respository.GetMostRecentEntries();return View(mostRecent);

}

public ActionResult Create(){

return View();}

[HttpPost]//限定只能是HTTP Post 能够造访这个措施

public ActionResult Create(GuestbookEntry entry){

if (ModelState.IsValid){

//entry.DateAdded = DateTime.Now;//_db.Entries.Add(entry);

//_db.SaveChanges();_respository.AddEntry(entry);

return RedirectToAction("Index");}

return View(entry);}

public ActionResult Show(int id)

{//var entry = _db.Entries.Find(id);//找到该Id的Entries

var entry = _respository.FindById(id);bool hasPermission = User.Identity.Name == entry.name; //假如登岸人的姓名即是entry录入人的姓名,就显示Edit按钮

ViewData["hasPermission"] = hasPermission;ViewBag.hasPermission = hasPermission;

return View(entry);}

public ActionResult CommentSummary()

{//var entries = from entry in _db.Entries

//group entry by entry.name into groupByName//orderby groupByName.Count() descending

//select new CommentSummary//{

//NumberOfComments = groupByName.Count(),//UserName = groupByName.Key

//};

var entries = _respository.GetCommentSummary();

return View(entries.ToList());

}

}

}

虽然我们已经把逻辑查询部分移出了controller,然则仍旧必要查询和测试。它不再是单元测试的一部分,而是一个集成测试,熬炼观点上的respository实例造访数据库。

依附注入(Dependence Injection)

调把依附性传进工具的构造函中的技巧被称为依附注入,我们已经手动地完成了依附注入,经由过程在我们的类中添加很多构造函数。在第18章,我们将进修如何应用依附注入容器去避免这个多构造函数的需求。更多关于依附注入的信息 http://manning.com/seeman

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

您可能还会对下面的文章感兴趣: