一、 学习MVC注意事项
1. 了解不同的项目类型
从ASP.NET 2.0开始,vs针对网站开发区分了两种项目类型,一种是'项目'(Website Project),另一种是'网址'(Web Application Project)。两者最大的差别在于'项目'采用动态编译的架构运作。如:中只要编辑并存储了'App_Code'目录下的类或强类型数据集,就会使整个网站项目在后台重新编译,有时甚至会阻碍在vs中进行操作,若项目规模变大,就很容易拖慢开发速度。而MVC要求项目类型最好是'网址',可以利用自动化辅助功能开发。
2. 初学者常犯的错误
对于习惯'项目'的人来说,当程序被修改保存后,即可直接切换至浏览器进行测试。但由于MVC需以'网址'的项目类型来开发,所以当程序被修改之后,都要先手动生成项目,才能将修改的结果编译进'bin'目录下组建中,这样才可以切换到浏览器测试,否则所做的修改都不会反映到页面上。
3. 小心使用Request对象和Response对象
虽然在Controller中可以访问Request对象,但对于从客户端传来的QueryString或Form数据,建议不要通过Request对象取得,以免MVC项目难以维护。
Response对象也是早期ASP和ASP.NET Web Forms中常用的对象,在controller中建议尽量不要使用。当然,使用它来响应页面内容更要不得。
不过有规定就例外,只是提醒尽量不要用,并非不能用。如自行开发CAPTCHA图片验证功能时,可能需要通过Response对象响应经过动态运算的图片。
4. 不要在视图中编写过多的程序逻辑
在开发MVC视图时,一定要尽可能的简化逻辑。不要将过多的商业逻辑写在代码里。
二、改良MVC项目
1. 使用视图数据模型
上一篇中的留言板中,因为先前在 MvcGuestbook.edmx文件创建的'留言板'数据模型包含了5个字段,但是'id'与'建立时间'这两个字段在存储留言中用不到。这样,通过动作窗口接收数据时会有一定的安全风险。所以一些开发,针对一些有特殊目的的窗口或字段,会采用自定义模型的方式来定义。
TIP : 通过'Entity Framework'生成的'Model.留言板'类当成ViewModel使用,若save动作设定的参数类型为'Model.留言板',而且直接将接收到的对象保存到数据库中的话,原本预期只会收到3个参数,但黑客如果知道其他字段的名称(例如创建时间),就可能会多输入一个Form数据,进而复制进你的数据库,并调过字段的默认值。这样,数据库就被注入数据了。
此类数据模型大多专用于视图中,也称为视图数据模型,即ViewModel。下面是ViewModel的创建过程。
首先,在'Models'目录下新建一个数据类型(也就是一般的C#类),并将其命名为'GuestbookForm.cs',如图:
由于留下足迹窗口中只有3个字段,所以在此类中要定义3个属性。具体如下:
1 namespace MvcApplication1.Models 2 { 3 public class GuestbookForm 4 { 5 public string name { get; set; } 6 public string Email { get; set; } 7 public string content { get; set; } 8 } 9 }
修改'/Views/Guestbook/Write.aspx'页面的"<% @page...%>"语句,将Inherits属性修改成强制类型,并将ViewPage的属性改为输入MvcApplication1.Models.GuestbookForml类,示例如下。注意,控制器的"return View();"语句不需要修改。
Inherits="System.Web.Mvc.ViewPage"
接下来修改原程序中 '<%=Html.Label("name") %>'或'<%=Html.TextBox("name") %>'为MVC2.0新建的强制类型HTML辅助方法,如图,在此过程中还可以利用IntelliSense来编写程序,而且不必担心把字段名称写错。
NOTE: "Html.LabelFor(x=>x.name)"语句中的"x=>x.name"又称为Lambda表达式,是C#3.0/4.0提供的一种机制。
至于最后输出到浏览器的页面,还是跟之前一模一样。
接下来我们修改Save()动作。先回忆一下原来的声明,具体如下:
public ActionResult Save(string name,string Email,string content)
将以上代码修改如下:
public ActionResult Save(Models.GuestbookForm data)
我们将原来的3个参数改成只剩下一个,使用的一样是之前提到过的Model Binder机制。使用这个机制,将自动通过QueryString()方法或Form()方法传来的参数找到对应的强制类型中的属性。只要名称对上了,该机制就会把数据放入该对象的属性中。
此时,Write视图和Save动作已经在无形中联系起来了,而且使用的是同一个数据模型。这种为了视图而另外定义的数据模型称为视图数据模型(ViewModel),修改完成的程序代码如下:
1 [HttpPost] 2 public ActionResult Save(Models.GuestbookForm data) 3 { 4 MvcApplication1.Models.GuestbookEntities db = new Models.GuestbookEntities(); 5 6 db.AddTo 留言板(new Models.留言板() 7 { 8 name =data.name, 9 Email=data.Email, 10 content=data.content, 11 dtime=DateTime.Now 12 13 }); 14 15 db.SaveChanges(); 16 ViewData["name"] = data.name; 17 ViewData["Email"] = data.Email; 18 ViewData["content"] = data.content; 19 20 return View(); 21 }
这段程序代码跟之前差不多,只是参数变少了。但Write视图与Save地址共享用一个ViewModel及用强制类型的方式开发项目有着非常高的价值。如果日后数据库字段名称被修改了,通过VS2010内置的重构机制,将能更有效的针对整个项目进行字段名称的替换。
三、使用窗口验证功能
好比避免之前留言为空就直接提交到数据库的情况。MVC内置了数据验证机制,只要搭配ViewModel及NET 5 SP1,或者.NET 4.0提供的Data Annotations的函数库,就能定义数据验证规则,同时实现客户端的验证与服务器端的验证了。
要让GuestbookForm这个ViewModel在使用时能验证'姓名'与'内容'字段为必填项,而且希望当用户输入E-mail数据时能验证其格式,可以通过Data Annotations函数库的声明设定将这些功能应用在ViewModel上。以下是应用Data Annotations函数库的程序代码,可以看出,这里只是多加载了System.ComponentModel.DataAnnotations命名空间和新建了几个属性(Attribute)而已。暂时看不懂,没关系,先看看名字。猜猜用途。
1 using System.ComponentModel.DataAnnotations; 2 3 namespace MvcApplication1.Models 4 { 5 public class GuestbookForm 6 { 7 [Required] 8 public string name { get; set; } 9 [DataType(DataType.EmailAddress)] 10 [RegularExpression(@"^[a-z0-9\._%+-]+@[a-z0-9\.-]+\.([a-z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|asia|jobs|museum)$")] 11 public string Email { get; set; } 12 [Required] 13 public string content { get; set; } 14 } 15 }
利用ModelState.IsValid 验证,可以通过数据模型绑定进来的数据是否符合ViewModel字段验证的要求,如果验证失败,ModelState.IsValid验证就会返回"false"值,并转到"留下足迹"页面,如下:
1 [HttpPost] 2 public ActionResult Save(Models.GuestbookForm data) 3 { 4 5 if (!ModelState.IsValid) 6 { 7 return RedirectToAction("Write"); 8 } 9 MvcApplication1.Models.GuestbookEntities db = new Models.GuestbookEntities(); 10 11 db.AddTo 留言板(new Models.留言板()
至此,我们已经完成了服务器验证的设置工作,接着来看MVC是如何让实现"客户端JavaScript验证"机制的,一共有一下3个步骤。
Step01:打开"/View/Shared/Site.Master"文件,在"…"标记之间添加以下3个项目内置的javascript文件。
Step02:在"<%using (Html.BeginForm("save",RouteData.Values["controller"].ToString())) {%>"语句之前加入以下声明,以启用客户端验证功能。
<%=Html.EnableClientValidation(); %>
TIP:若在一个页面中有两个或者两个以上的窗口需要进行验证," <%=Html.EnableClientValidation(); %>"语句必须出现在第一个Html.BeginForm声明之前,否则验证将无法执行。
若要使用" <%=Html.EnableClientValidation(); %>"语句验证功能,必须使用Html.BeginForm声明窗口,用户自行编写的