控制器是ASP.NET MVC应用程序的核心组件之一。它是一个含有一个或多个public方法(动作)的类,这些方法对应于特定的URL。这些动作充当着应用程序的“胶水”,将模型数据与应用程序的用户界面(视图)集合在一起。
控制器应充当一种协调程序,它不应该真正包含任何业务逻辑,而是充当一种转换层的形式,将视图中的用户输入转换成业务逻辑对象,反之亦然。
1.手动映射视图模型
在留言本这个例子中,GuestbookEntry类既作为域模型,也作为视图模型。它既表现了数据库中存储的数据,也表现了用户界面中的字段。
对于像留言簿这样的小型应用程序,这是足够的。但是,随着应用程序复杂性的提升,当复杂的用户界面结构必须不直接映射模型的结构时,即视图数据与模型结构不同,往往需要将两者分开。比如,让我们对Guestbook应用程序添加一个新的页面,以显示每个用户已递交了多少评论的摘要,如图所示。
为了创建这一屏幕,首先需要创建一个视图模型,它每一列含有一个属性——用户名和已递交的评论数:
public class CommentSummary { public string UserName { get; set; } public string NumberOfComments { get; set; } }
现在需要创建一个控制器动作,查询数据库以获取显示所必需的数据,然后将其注入CommentSummary类实例。
public ActionResult CommentSummary() { var entries = from entry in _db.Entries group entry by entry.Name into groupedByName orderby groupedByName.Count() descending select new CommentSummary { NumberOfComments = groupedByName.Count(), UserName = groupedByName.Key }; return View(entries.ToList()); }
这里使用了LINQ来查询留言簿数据,并按用户名对递交的评论进行分组。接着将数据投影成视图模型实例,然后便可以将其传递给视图。
@model IEnumerable<Guestbook.Models.CommentSummary> <table> <tr> <th>Number of comments</th> <th>User name</th> </tr> @foreach(var summaryRow in Model) { <tr> <td>@summaryRow.NumberOfComments</td> <td>@summaryRow.UserName</td> </tr> } </table>
2.输入验证
回顾第2章,我们曾考擦了一个在GuestbookController的Create动作中接受用户输入的例子:
[HttpPost] public ActionResult Create(GuestbookEntry entry) { entry.DateAdded = DateTime.Now; _db.Entries.Add(entry); _db.SaveChanges(); return RedirectToAction("Index"); }
这个动作以GuestbookEntry对象形式简单地接受了New Comment页面所递交的输入,设置了日期,然后把它插入数据库。虽然这样可以工作得很好,但这实际上并不是最好的办法,没有任何验证。此刻用户可以递交未输入姓名或评论的表单。通过添加一些基本的验证,可以对此加以改进。
我们要做的第一件事是,用Required注解属性来注释GuestbookEntry类的Name和Message属性。
public class GuestbookEntry { public int Id { get; set; } [Required] public string Name { get; set; } [Required] public string Message { get; set; } public DateTime DateAdded { get; set; } }
Required注解属性位于System.ComponentModel.DataAnnotations命名空间,并提供了对一个对象的特定属性进行验证的方式。一旦做了注释,MVC将在调用Create动作时自动地验证这些属性。通过检查ModelState.IsValid属性,可以检查验证是成功或失败,然后决定验证失败时做什么。以下是Create动作更新后的版本。
[HttpPost] public ActionResult Create(GuestbookEntry entry) { if (ModelState.IsValid) { entry.DateAdded = DateTime.Now; _db.Entries.Add(entry); _db.SaveChanges(); return RedirectToAction("Index"); } return View(entry); }
这一次,我们不是简单地在数据库中存储新条目,而是首先检查ModelState.IsValid是否返回true。如果是,便还像前面那样保存这一新条目。然而,如果失败,则重新渲染Create视图,这让用户能够在再次提交之前纠正任何问题。
通过调用Html.ValidationSummary方法,我们可以在视图中显示验证失败所生成的错误消息。
<h2>添加留言</h2> @Html.ValidationSummary() @using (Html.BeginForm()) { <p>Please enter your name:</p> @Html.TextBox("Name") <p>Please enter your message:</p> @Html.TextArea("Message", new { rows = 10, cols = 40 }) <br /> <br /> <input type="submit" value="Submit Entry" /> }
除了在视图的顶部调用ValidationSummary方法外,还要注意到,现在也使用了MVC的HTML辅助器来生成页面上的文本输入。使用这些辅助器的一个好处是,MVC将自动检测验证错误消息,并运用一个CSS的class指示该字段有错误。
在图中所看到的错误消息是ASP.NET MVC默认用于必需字段的错误消息。我们可以对其加以重写并使用自己的消息,只要修改Required注解属性的声明,让其包含一条自定义消息即可:
[Required(ErrorMessage="Please enter your name")] public string Name { get; set; }