ASP.NET MVC3 Razor 结合 jQuery 入门指南






4.92/5 (207投票s)
创建第一个 MVC 应用的简单教程。
目录
引言
在本文中,您将找到创建第一个 ASP.NET MVC 应用程序的最简单方法。这是一个面向 ASP.NET MVC 完全初学者的教程。遵循本教程的唯一先决条件是您了解如何使用 Visual Studio,并理解 HTML 和 C# 语法。此外,我将简要展示如何使用一些 jQuery 插件,因此了解一些 jQuery 语法会很有帮助。如果您不熟悉 jQuery,也没关系,您可以跳过那些部分,因为它们只是可选的增强功能。
背景
什么是 MVC(模型-视图-控制器)?MVC 是一种简单的架构,其中所有组件都分离到三个类中
模型 (Model)
- 包含将显示给用户的数据的类视图 (View)
- 将模型显示给用户的组件控制器 (Controller)
- 处理与用户的任何交互的组件
在 Web 框架中,用户会在 ASP.NET MVC 应用程序中输入某个 URL,控制器、模型和视图将处理此请求并返回 HTML 给用户。浏览器与拥有控制器、模型和视图组件的服务器之间的交互如下图所示
处理 MVC 请求的这个简单场景按以下步骤描述
- 用户在浏览器中输入某个 URL,该 URL 将发送到服务器,例如:https:///Product/List。
- 框架会分析用户请求,以确定应该调用哪个控制器。
- 控制器接收用户发送的参数,调用模型以获取数据,并加载应该显示的模型对象。
- 控制器将模型对象传递给视图。
- 视图从模型获取数据,将其放入 HTML 模板中,然后将响应发送回用户浏览器。
- 浏览器显示从服务器收到的 HTML。
在本例中,我将展示如何使用 ASP.NET MVC 和 Razor 语法创建一个简单的应用程序,该应用程序允许您列出和管理用户详细信息。
首先,什么是 Razor?当您创建一个视图组件,该组件将 C# 数据对象转换为 HTML 视图时,您可以使用多种模板语法。Razor 是一种简洁明了的语法,用于组合 C# 和 HTML 代码。在以下清单中,您可以看到 Razor 语法的一个简单示例
@{
string name = "Jovan";
var dateOfBirth = new { Day = 8, Month = 12, Year = 1980 };
string[] skills = new string[] { "MVC", "C#", "JQuery", "ASP.NET" };
}
<h2>@name</h2>
<p>Born in year: @dateOfBirth.Year</p>
<ul>
@foreach(skill in skills){
<li>skill</li>
}
</ul>
在第一部分,我定义了一些 C# 变量,然后将它们注入到 HTML 代码中。生成的 HTML 输出显示在以下清单中
<h2>Jovan</h2>
<p>Born in year: 1980</p>
<ul>
<li>MVC</li>
<li>C#</li>
<li>JQuery</li>
<li>ASP.NET</li>
</ul>
正如您所见,C# 代码将被丢弃,只有来自该代码的值将被注入到 HTML 模板中。使用 Razor 语法将 C# 数据放入 HTML 非常简单——只需在 C# 变量名前加上 @
前缀即可。这适用于简单类型和对象字段。即使您有更复杂的处理,例如使用循环输出元素列表,语法也非常简洁。
如果您不熟悉 Razor,可以在此处找到快速参考,或在ASP.NET MVC 网站上找到完整的参考。
请注意,在此示例中,C# 数据直接定义在视图中。然而,在实际代码中,C# 数据将来自控制器提供的模型(请参阅上面的图)。
使用代码
要开始使用此代码,您需要安装 Visual Studio 或至少 Web 开发人员工具。如果您没有,可以从ASP.NET MVC 网站下载。在那里您可以找到安装 MVC 框架所需的所有先决条件。
安装 Web 开发人员工具后,创建 MVC 应用程序将非常简单。只需转到文件/新建项目,选择 ASP.NET MVC Web 应用程序,然后向导将引导您完成设置过程。
在这里,您应该输入项目名称并确定其存放位置。如果您愿意,可以使用默认设置。以下部分将解释创建的 MVC 项目的结构和元素。
MVC 项目结构
创建 MVC Web 应用程序后,它将按以下文件夹组织
- Model 文件夹,包含您的模型类。
- Controller 文件夹,其中放置命名为 <CONTROLLER-NAME>Controller 的类。这些类包含在发送 HTTP 请求时将调用的操作方法。
- Views 文件夹 - 在此文件夹中,放置用于生成 HTML 作为响应的模板文件。视图被划分为子文件夹。每个控制器都有自己的子文件夹,名称与控制器相同,其中放置了该控制器的视图。
- CSS 文件和图像将放置在 Content 文件夹中。
- JavaScript 文件将放置在 Scripts 文件夹中。
下面一张图展示了这种结构的示例
在此图中,您可以看到上面描述的所有组件。我们有两个控制器 User 和 School,以及它们在 /Views/School 和 /Views/User 文件夹中的视图。放置在 /Views/Shared 文件夹中的视图在所有控制器和视图之间共享。请注意,视图具有 .cshtml 文件扩展名。这意味着它们使用 Razor 语法作为视图模板(本教程使用此语法)。
模型 (Model)
模型可以是任何类,它定义了将显示给用户的数据结构。它可以是一个普通的 C# 类,一个从数据库生成的 Entity Framework 模型,甚至是一个 DataSet
(尽管不推荐在 MVC 中使用它)。在本例中,我将使用一个普通对象列表来模拟数据存储库。本文中的示例将显示一个用户表,显示/编辑列表中的用户对象详细信息,并允许您从列表中添加/删除用户。应用程序中使用的 Model
类显示在以下清单中
public partial class User
{
public int UserID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Town { get; set; }
public string County { get; set; }
public string Country { get; set; }
public string Email { get; set; }
public DateTime DoB { get; set; }
public bool IsActive { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public int Rating { get; set; }
}
此示例将不使用持久化数据存储(例如数据库)。我将 User
对象放入一个普通列表中,应用程序将使用它来显示用户数据。以下示例展示了这种“存储库”的类型
public class UserRepository
{
public static List<User> Users = new List<User>();
}
尽管我不会解释任何高级数据访问技术,但我将使用一些简单的 LINQ 查询来从列表中查找用户数据。以下清单显示了查找用户的 LINQ 查询
var user = userList.First(x => x.UserID == id);
var bestUsers = userList.Where(x => x.Rating > 8);
第一行代码查找用户列表中 UserID
属性等于 ID
变量的第一个对象(即按 ID 查找用户)。第二行查找所有评分大于 8 的用户。
=>
表达式是一个内联函数(lambda 表达式),用于定义从列表中查找用户的标准。它只是内联函数的简写形式,看起来像
function(parameter){ return expression; }
您可以在 “使用 LINQ 查询” 文章中找到有关 LINQ 查询的更多信息。
视图 (View)
视图是一个普通文件,它定义了将用于显示模型数据的模板。在最简单的情况下,您可以将视图想象成一个普通 HTML 代码,其中放置了将注入 Model
属性的位置。以下清单展示了这种视图的示例
<fieldset>
<legend>User</legend>
<div class="display-label">Name</div>
<div class="display-field">@Model.Name</div>
<div class="display-label">Address</div>
<div class="display-field">@Model.Address</div>
</fieldset>
在此视图中,定义了将显示给用户的 HTML 结构,以及将放置模型数据的位置。该示例会将模型 Name
和 Address
属性注入 HTML 代码。语法 @Model.<<PropertyName>>
定义了一个将在 View
中放置 Model
属性的位置。如果您有名称为“Jane Smith
”且地址为“Regent street 12, London
”的值,将生成以下 HTML 输出
<fieldset>
<legend>User</legend>
<div class="display-label">Name</div>
<div class="display-field">Jane Smith</div>
<div class="display-label">Address</div>
<div class="display-field">Regent street 12, London</div>
</fieldset>
当您生成复杂的表单元素(如 INPUT
、TEXTAREA
等)时,可以使用与上一个示例相同的方法。您可以为 INPUT
放置 HTML 代码,并将模型的某个属性直接注入 value
属性。以下清单展示了一个示例
<input name="address" id="address" value="@Model.Address" class="medium-size" />
但是,您可以改用许多内置的 HTML 类扩展方法,而不是纯 HTML。
@Html.TextBox( "address", Model.Address)
@Html.TextBoxFor( model => model.Address )
第一个参数是文本框的名称/ID,第二个参数指定它应具有的值。此辅助方法将呈现一个 INPUT
标签和一个 type="text"
属性。在第二个示例中,使用了 TextBoxFor
方法,该方法传递了一个指定 Address
属性的 lambda 表达式。在这种情况下,我们无需显式指定输入字段的名称/ID——辅助方法将从属性名称中获取它。还有其他辅助方法可用于生成其他表单元素
Html.DropDownListFor()
Html.CheckboxFor()
Html.RadioButtonFor()
Html.ListBoxFor()
Html.PasswordFor()
Html.HiddenFor()
Html.LabelFor()
您还可以添加自己的方法来呈现自定义 HTML,例如 Html.Table()
、Html.Calendar()
等。有关更多详细信息,请参阅 MVC 网站上的“创建自定义助手”。您还可以使用 Html.DisplayFor
和 Html.EditorFor
方法生成 HTML,而无需指定类型。以下清单展示了一个示例
@Html.DisplayFor( model => model.Address )
@Html.EditorFor( model => model.Address )
在这些方法中,我们根本没有指定输入类型,因为它将根据要显示的属性类型来确定。例如,如果属性类型是 string
,EditorFor
将生成文本输入。更有趣的示例是将某个对象传递给 EditorFor
。默认情况下,EditorFor
方法将根据类型为对象中的每个属性生成一个 INPUT
元素(例如,字符串为文本输入,布尔值为复选框等)。DisplayFor
执行相同的操作,除了它将呈现不可编辑的标记。您可以在此处找到有关显示模板的更多详细信息。但是,当您创建自定义对象类型模板时,才能真正看到这些方法的强大功能。这是一个更高级的主题,但如果您对此感兴趣,可以参阅本文了解更多详细信息。
现在回到基础——我们如何添加一个新的 View
?在上面的文件夹结构中,您可以看到 Views 文件夹以及名为 User、School、Shared 等的子文件夹。只需右键单击要放置视图的文件夹,然后选择“添加新视图”,就会显示以下对话框
在这里,您可以输入视图的名称(通常与控制器操作的名称相同)以及将显示的 Model
对象的类型。此外,您可以添加一个空的视图并开始编写代码,或者选择一些预定义的模板,如列表、详细信息、编辑、创建或删除。
您可以将视图想象成显示给用户的独立页面。但是,还有其他几种类型的视图(如下所述)。
布局页 (Layout Page)
当您创建一组页面时,很可能需要在许多页面上使用相同的元素。例如,所有页面都将具有相同的菜单、相同的 CSS/JavaScript 文件等。在 ASP.NET MVC 中,您无需在每个页面上复制相同的元素。您可以使用所谓的布局页,在其中定义整个 MVC 应用程序的通用布局,并将其用于每个视图页面。以下清单展示了一个将在此项目中使用的布局页的示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>This is a title of the page</title>
<link href="/Content/Site.css" rel="stylesheet" type="text/css" />
<script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
<script src="/Scripts/modernizr-1.7.min.js" type="text/javascript"></script>
@RenderSection("PageScripts",false)
</head>
<body>
@RenderBody()
</body>
</html>
正如您所见,在布局页中,我包含了所有必要的 CSS/JavaScript 文件,这些文件将在所有页面上使用。我还定义了两个自定义占位符,每个视图都可以将特定内容放在其中。这两个占位符是
- Section
PageScripts
- 每个页面都可以在其中放置仅在该页面上需要的 JavaScript 或 CSS 文件。 RenderBody
语句,它定义了特定视图的内容将注入到通用布局中的位置。
部分视图 (Partial Views)
组织和重用 View
代码的另一种方法是将多个视图中使用的元素放入单独的文件中。这些不是显示给用户的独立视图——它们只是包含在其他主视图页面中的部分视图。
部分视图与常规视图一样是 HTML 模板。以下清单展示了一个显示当前日期的部分视图的示例
<div class="now">Current date is @DateTime.Now.ToString()</div>
无论何时需要包含此部分视图,都可以通过名称调用它,如下面的示例所示
@Html.Partial("_CurrentDateView")
在调用处,将包含 View
的全部内容。您可以将此语句放在标准视图、布局页甚至其他部分视图中。
控制器 (Controller)
控制器是 MVC 模型中处理逻辑的核心。当 URL 以 /<<Controller>>/<<Action>>/<<Parameter>> 的格式发送到 Internet 应用程序时(例如,/User/Details/1),它将被解析,找到名为 <<Controller>>Controller
的类(例如,上述 URL 中的 UserController
),调用该控制器中的 <<Action>>
方法(例如,Details
方法),并将参数传递给该方法。控制器将执行所有必要的处理——它将找到一个模型并将其传递给视图。
控制器可以具有任何 public
方法,并带有可以传递的任意参数。例如,以下清单显示了一个名为 ExampleController
的控制器,其中有一个 ControllerAction
方法,该方法接收三个参数——id
、name
和 flag
。
public ExampleController
{
public ActionResult ControllerAction(int id, string name, bool flag)
{
var model = GetModel(id, name, flag);
return View(model);
}
}
您可以使用以下 URL 调用此 Controller
的操作
/Example/ControllerAction?id=17&name=test&flag=true
这是 URL 路由和控制器/操作之间的默认映射,其中 URL 中的第一个单词代表控制器名称,第二个单词是操作方法名称。但是,您可以修改它并添加自己的路由映射规则。有关自定义路由的更多详细信息,请参阅“创建自定义路由”文章。
Controller
会自动将请求中的参数按名称映射到方法参数。如果您有一个包含许多参数的表单,可以自动从请求和操作方法主体中的用户处读取这些参数,这将非常有用。
如何添加新的 Controller
?这与添加新视图类似——转到 Controller 文件夹,右键单击,然后添加新 Controller
。将显示以下对话框
在这里,您可以输入 Controller
的名称——请注意,它必须是 <<Name>>Controller
格式。此外,您可以选择其中一个预定义的模板——空控制器、带有列表、详细信息、编辑、创建和删除操作的控制器,以及已通过 Entity Framework 连接到数据库的控制器。在本例中,我从一个空控制器开始。创建空控制器后,可以按照以下格式添加操作方法
public MyController{
[Get]
public ActionResult MyAction()
{
return View();
}
}
操作方法应返回一个视图,该视图将在浏览器中显示 HTML(上面示例中的 ActionResult
类型)。默认情况下,如果您在 MyController
类中添加 MyAction
方法,当发送 /My/MyAction URL 时,它将被调用。您可以更改此行为,但这不会在本入门级文章中进行描述。此外,您可能会注意到方法前面的 [Get]
属性。此属性告诉操作它应该仅响应 GET
HTTP 协议。作为替代,我可以将 [Post]
设置为 [Get]
,或者将其留空,这样该操作就可以在 GET
和 POST
协议上打开。
此外,您肯定注意到操作中的最后一条语句是 return View()
。如果 MyAction
操作位于 MyController
类中,则此语句将在 Views 部分的 My 文件夹中找到 MyAction.cshtml 视图。这是在操作方法中返回视图的默认规则。如果您不想使用此默认视图,可以通过指定 View 名称
来选择应返回哪个 View
,例如
return View(name);
请注意,您有一种简单的方法可以转到 View
,或者为特定操作添加 View
。只需右键单击操作,您就会看到一个上下文菜单,允许您为此操作添加视图。添加视图操作将打开“添加视图”对话框,如上一节所示。如果存在与操作同名的视图,则“转到视图”将打开视图页面。
jQuery
尽管 jQuery 不是 MVC 的直接组成部分,但它是一个非常有用的 JavaScript 库,可以增强您的用户界面。您可以做的第一个有用事情是轻松地在 HTML 中查找 HTML 元素。以下表显示了一些最简单的查询
jQuery 选择器 | 描述 |
$("table") | 查找 HTML 中的所有 table 节点 |
$("#Name") | 查找 ID 为 Name 的 HTML 元素 |
$(".required") | 查找 HTML 中具有 CSS 类“required ”的所有元素 |
$("table a.delete") | 在任何 table 中,查找所有具有 CSS 类“delete ”的 A 标签 |
$("table tr:odd") | 查找 table 中的所有奇数行 |
一旦使用 jQuery 选择器找到元素,您就可以隐藏它们、更改它们的颜色、属性(类、值、内部文本)、向它们添加一些事件处理程序(单击、双击)等。以下清单展示了一个将单击处理程序附加到链接的代码示例
$('a').click(function (event) {
// prevent default action (going to the another page)
event.preventDefault()
// Do something
});
此外,jQuery 包含一组有用的插件,可用于增强您的 UI 元素。以下表展示了一些示例
插件 | 描述 |
$("input.calendar").datepicker() | 在每个具有 CSS 类“calendar ”的输入元素上添加日期选择器(日历)对话框 |
$("form").validate() | 为表单添加 JavaScript 验证 |
$("table").dataTable() | 在 table 中添加 JavaScript 排序、分页和过滤功能 |
您可以在 jQuery 文档网站上找到有关 jQuery 的更多详细信息和教程。还有一个有趣的“jQuery 入门”文章,您可以在其中找到一些有趣的示例。这是可选的,但在 MVC 应用程序中添加非常简单且有用。请注意,创建 MVC 应用程序时,jQuery 会自动包含在项目中。但是,插件不是 MVC 项目中包含的核心 jQuery 的一部分,因此您需要手动找到它们并将它们放入您的项目中。
如何使用此 JavaScript 代码?首先,您需要从 jQuery 下载页面包含 jQuery 库。在您的 MVC 项目中,您可能会在 scripts 文件夹中找到此库,因此您只需在布局页中添加对此脚本的引用。然后您就可以使用此库了——以下清单展示了一个示例
<script src="/Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$("table").fadeOut("slow");
}
</script>
在此示例中,我包含了 jQuery 版本 1.7.1,并为 table
元素添加了淡出效果。请注意,此语句放在文档就绪包装器中。使用此包装器,其中包含的任何 JavaScript 代码将在整个文档加载并准备好处理时调用。这是 jQuery 编程中的常见做法。在这里,我将 jQuery 代码添加为内联脚本,但您可以将其放入单独的文件中。
创建你的第一个 MVC 应用程序
在本节中,我将展示如何创建一个简单的 MVC 应用程序,该应用程序可以列出、编辑、创建和删除用户。
列表/索引页 (List/Index Page)
在列表页上,将显示一个包含存储库中用户列表的表。当调用 /User/Index URL 时,将打开该页面。因此,我们需要创建一个 Index
方法并将其放在 UserController
类中。以下清单展示了 Index
方法的示例
public ActionResult Index()
{
var model = UserRepository.Users;
return View(model);
}
这个方法很简单。它从存储库中获取用户列表并将其传递给视图。列表视图接收此列表并显示一个表,如下面的清单所示
<table class="display">
<thead>
<tr>
<th>
Name
</th>
<th>
Address
</th>
<th>
Town
</th>
<th>
County
</th>
<th>
Rating
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@item.Name
</td>
<td>
@item.Address
</td>
<td>
@item.Town
</td>
<td>
@item.County
</td>
<td>
@item.Rating
</td>
<td>
<a href="/User/Details/@item.UserID">Details</a> |
<a href="/User/Edit/@item.UserID">Edit</a> |
<a href="/User/Delete/@item.UserID" class="delete">Delete</a>
</td>
</tr>
}
</tbody>
</table>
正如您所见,此列表视图接收了一系列用户对象,并通过 foreach
循环将它们输出为 HTML 表。请注意,您无需创建带有 head
和 body
标签的 HTML 包装器。这些是在布局页中定义的,而这部分 HTML 代码只是被注入到页面的中间(上面布局页中的 RenderBody
语句)。下图展示了列表页的示例
应用 jQuery DataTables 插件
您看到的表格只是一个普通的表格,没有任何功能。但是,您可以轻松地使用 jQuery JavaScript 库增强此表格,以添加排序、分页、过滤等高级功能。在布局页中,您看到我放置了一个 PageScripts
占位符,允许每个视图放置一些自定义 JavaScript 或 CSS。在本例中,我将仅在列表页上添加一些 JavaScript 来增强此表格。此附加脚本的示例显示在以下清单中
@section PageScripts{
<link href="/Content/dataTable/demo_table.css" rel="stylesheet" type="text/css" />
<script src="/Scripts/jquery.dataTables.1.8.2.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$("table").dataTable();
}
</script>
}
此脚本包含了 jQuery DataTables 的 CSS 和 JavaScript 文件,并将 DataTables 函数应用于 HTML 表格。结果是,您会将此普通表格转换为功能丰富的表格,如下图所示
我将不再详细解释如何使用这个 jQuery 插件,但如果您感兴趣,可以查看“使用 jQuery DataTables 插件增强 HTML 表格”文章。
详情页 (Details Page)
在详情页上,将显示按 ID 查找的特定用户信息。发送到服务器端的请求格式如下
/User/Details/123
在此请求中,您可以看到将调用 UserController
,并将执行带有参数 123 的 Details
方法。123 是要显示的记录的 ID。Details
方法将获取用户的 ID,在模型存储库中查找此用户,并将其传递给视图
public ActionResult Details(int id)
{
var model = UserRepository.Users.First(user => user.UserID == id);
return View(model);
}
视图也很简单——它接收 user
模型对象并将用户属性注入到视图中。视图的一部分显示在以下清单中
@model JQueryMVC.Models.User
<h2>User Details</h2>
<fieldset>
<legend>User</legend>
<div class="display-label">Name</div>
<div class="display-field">
@Model.Name
</div>
<div class="display-label">Address</div>
<div class="display-field">
@Model.Address
</div>
<div class="display-label">Town</div>
<div class="display-field">
@Model.Town
</div>
<div class="display-label">County</div>
<div class="display-field">
@Model.County
</div>
</fieldset>
您会注意到页面大部分是纯 HTML——我们只在 HTML 代码的某些部分放置了模型属性。结果将显示以下用户详情页
请注意,在上面的清单中,我只放置了视图的一部分。您可以在附加的源代码中找到完整的清单。
编辑页 (Edit Page)
编辑页与详情页非常相似。Controller
将接收相同的参数(ID
),找到具有该 ID
的对象,并将其传递给视图。
public ActionResult Edit(int id)
{
var model = UserRepository.Users.First(user => user.UserID == id);
return View(model);
}
视图与详情视图相似,只是这里我们使用 HTML 输入元素而不是用于显示纯标签的 DIV
元素。在以下清单中,显示了编辑视图的一部分
<form method="post" action="/User/Edit/@Model.UserID">
@Html.ValidationSummary(true)
<fieldset>
<legend>User</legend>
<input type="hidden" name="UserID" id="UserID" value="@Model.UserID" />
<div class="editor-label">
<label for="Name">Name</label>
</div>
<div class="editor-field">
<input type="text" name="Name" id="Name" value="@Model.Name" />
@Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
<label for="Email">Email</label>
</div>
<div class="editor-field">
<input type="text" name="Email"
id="Email" value="@Model.Email" />
@Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
<label for="DoB">Date of birth</label>
</div>
<div class="editor-field">
<input type="text" name="DoB" id="DoB"
value="@Model.DoB" class="calendar"/>
@Html.ValidationMessageFor(model => model.DoB)
</div>
<div class="editor-label">
<label for="IsActive">Is Active</label>
</div>
<div class="editor-field">
<input type="checkbox" name="IsActive"
id="IsActive" value="@Model.IsActive" />
@Html.ValidationMessageFor(model => model.IsActive)
</div>
<div class="editor-label">
<label for="UserName">User name</label>
</div>
<div class="editor-field">
<input type="text" name="UserName"
id="UserName" value="@Model.UserName" />
@Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
<label for="Password">Password</label>
</div>
<div class="editor-field">
<input type="password" name="Password"
id="Password" value="@Model.Password" />
@Html.ValidationMessageFor(model => model.Password)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
</form>
输入后还放置了一些验证元素——这些稍后将进行描述。编辑页显示在下图
我们已经看到了读取用户列表并将其传递给视图的操作,但我们还需要一个操作来处理当用户数据更新时发送的更新请求。处理 POST
请求的 Action
显示在以下清单中
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var model = UserRepository.Users.First(user => user.UserID == id);
try
{
UpdateModel(model);
return RedirectToAction("Index");
}
catch
{
return View(model);
}
}
此操作通过表单集合获取正在编辑的用户的 ID 和在 HTML 表单中输入的用户名。然后,它按 ID
查找用户并更新它。如果一切正常,将显示 Index 视图;否则,如果发生错误,将再次显示相同的视图。
添加 jQuery DatePicker
在此示例中,出生日期(DoB
属性)以纯文本形式输入。我将使用 jQuery DatePicker
替换纯文本输入,以便用户可以从日历弹出窗口中选择日期。为了集成 jQuery DatePicker
,我将在编辑视图中放入以下自定义脚本
@section PageScripts{
<link href="/Content/themes/base/jquery.ui.all.css" rel="stylesheet" type="text/css" />
<script src="/Scripts/jquery-ui-1.8.11.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$("input#DoB").datepicker();
});
</script>
}
此自定义脚本将包含所有必需的 JavaScript 和 CSS 文件(jQuery UI),并将 DatePicker
添加到 ID 为 DoB
的输入元素上。结果是,每次编辑出生日期时都会打开以下日历
这是另一个示例,说明如何仅用几行代码就可以增强您的用户界面。
创建功能 (Create Functionality)
当需要创建新用户时,应显示一个与编辑表单类似的空白表单。创建表单的 URL 将是
/User/Create
由于此表单不包含任何数据,因此控制器中的 Create
操作非常简单
// GET: /User/Create
public ActionResult Create()
{
return View();
}
此操作没有执行任何有用操作——当调用 /User/Create 页面时,它只是返回一个空视图。创建页的视图的一部分显示在以下清单中
<form action="/User/Create" method="post">
@Html.ValidationSummary(true)
<fieldset>
<legend>User</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.IsActive)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.IsActive)
@Html.ValidationMessageFor(model => model.IsActive)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Password)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Password)
@Html.ValidationMessageFor(model => model.Password)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
</form>
这是一个简单的表单,包含 INPUT
字段,可以在其中输入用户信息。请注意,在这种情况下,我们可以使用编辑示例中的纯 HTML 输入元素;然而,在这种情况下,我使用了 MVC 的 LabelFor
和 EditorFor
函数。结果将显示以下表单
纯 HTML 和 MVC 辅助函数之间没有区别——两种方式都会生成标准的 HTML 输入。如果您使用纯 HTML,修改 HTML 代码会更容易,但如果您使用编辑器,它将自动生成文本输入或复选框,具体取决于属性的类型(string
、bool
等)。此外,EditorFor
函数的一个优点是它可以处理模型为 null
的情况(在纯 HTML 模式下,您需要添加一些条件来检查是否有任何可以显示的数据)。然而,在实践中,我相信您将根据具体需求同时使用这两种方式。与编辑示例一样,我们需要另一个操作来处理表单信息发布到服务器时发送的请求。此操作显示在以下清单中
// POST: /User/Create
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
// TODO: Add insert logic here
var user = new User();
UpdateModel(user);
UserRepository.Users.Add(user);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
此操作与上一节中更新用户数据的操作类似。但是,在这种情况下,不是按 ID 查找用户,而是创建一个新的用户对象,用表单提交的参数更新它,并将其添加到列表中。如果一切正常,将显示索引;否则,将再次显示创建视图。
删除功能 (Delete Functionality)
可以使用与其他页面相同的方式创建标准的删除页面。您可以创建一个删除视图,向用户显示一个确认页面,从模型中删除用户,然后返回列表。但是,在本例中,我将展示如何创建 AJAX 化功能来删除用户数据。
我不会创建单独的删除视图——我将修改已经创建的列表视图,其中我们在每个表格行中都放置了一个删除链接。在列表视图中,我将附加一个 jQuery AJAX 调用,该调用在服务器端删除数据并从表格中删除行。发送到服务器端的请求格式如下
/User/Delete/123
在此请求中,您可以看到将调用 UserController
,并将执行带有参数 123 的 Delete
方法。123 是要删除的记录的 ID。
首先,我需要将一个 jQuery 事件处理程序附加到放置在表格中的删除链接上。这段代码不只是几行,因此不方便直接放在视图中。因此,此脚本应放在单独的 JavaScript 文件中(例如,delete-user.js)。在 index.cshtml 视图的 PageScripts
部分应放置指向此文件的引用
@section PageScripts{
<script src="/Scripts/delete-users.js" type="text/javascript"></script>
}
为什么将其放在单独的文件中?首先,将 HTML 和 JavaScript 混合在同一个视图中不是好的做法。如果您的 JavaScript 代码只有几行,那没关系,但如果您有大量代码,最好将其移到一个单独的文件中。此外,如果 JavaScript 功能放在单独的文件中,它可以在浏览器中缓存,因为此文件的内容不会改变。这样,总是会更改的视图将显着变小,因此加载速度会更快,并且在第二次加载页面时,JavaScript 文件可能已经存在于浏览器缓存中。
delete-user.js 文件中删除用户功能的实际实现显示在以下清单中
/// <reference path="jquery-1.7.1.min.js" />
$(document).ready(function () {
$("table a.delete").click(function (event) {
event.preventDefault();
var link = this;
if (confirm("Are you sure that you want to delete this user?")) {
$.ajax({
type: "POST",
url: link.href,
success: function (data) {
$(link).parents("tr").remove();
},
error: function (data) {
alert("This user cannot be deleted");
}
});
}
}
);
});
在此代码示例中,我已将一个函数附加到表格中具有“delete
”类的链接上。当点击删除链接时,将调用此函数。您需要做的第一件事是阻止默认事件操作,因为您将用自定义 AJAX 调用替换它。然后,事件处理程序会询问用户是否要删除当前行。如果用户确认,此函数将获取链接的 href
属性并创建到服务器端页面的 AJAX 调用。如果删除请求成功,该行将从表格中删除;否则,将显示错误消息。在服务器端,我们需要一个 Delete
操作,在错误处理程序中发送 AJAX 请求时调用该操作。
另外,请注意 JavaScript 文件中的第一行,它引用了 jQuery 库。这一行只是一个普通的注释,在浏览器中不起作用。但是,如果您使用的是 Visual Studio/Web Developer,并且有 jquery-1.7.1.vsdoc 文件,使用此行,您将获得 JavaScript 的 Intellisense 支持。当您键入 $.
时,您将看到 jQuery 库中所有可用 JavaScript 方法的列表及其描述(几乎与普通 C# 代码的 Intellisense 支持相同)。有关 JavaScript Intellisense 的更多详细信息,请参阅此处。
处理删除请求的控制器操作显示在以下清单中
public void Delete(int id)
{
var model = UserRepository.Users.First(user => user.UserID == id);
UserRepository.Users.Remove(model);
}
此操作从请求中获取 ID,按 ID 从存储库中查找对象,并将其从列表中删除。在实际代码中,您将创建一些 DELETE
SQL 代码,或从 Entity Framework 模型中删除对象。
搜索 (Search)
此处显示的最后一个功能是用户搜索。这不会是一个单独的页面——我将创建一个部分视图,可以放置在任何页面上。部分视图显示在以下清单中
_SearchUsers.cshtml
<form method="post" action="/User/Search">
<fieldset>
<legend>Search for users</legend>
<div class="editor-label">
<label for="active">Is Active</label>
</div>
<div class="editor-field">
Yes<input type="radio" name="active"
id="active" value="true" />
No<input type="radio" name="active"
id="active" value="false" checked="checked" />
</div>
<div class="editor-label">
<label for="rating">Minimal Rating</label>
</div>
<div class="editor-field">
<input type="text" name="rating"
id="rating" value="0" />
</div>
<input type="submit" value="Search" />
</fieldset>
</form>
正如您所见,我只是创建了一个带有活动单选按钮和一个最小评分文本字段的表单。您可以使用 @Html.Partial("_SearchUsers")
语句将其放在任何视图中。每次放置此语句时,MVC 将在视图中注入以下表单
当按下 **Search** 按钮时,表单中的参数将被发送到 /User/Search URL,因此我们需要一个控制器来接受表单数据并执行用户搜索。搜索操作显示在以下清单中
// POST/GET: /User/Search
public ActionResult Search(bool? active, int? rating)
{
var model = UserRepository.Users.Where
(user => user.IsActive == active && user.Rating > rating);
return View("Index", model);
}
首先,您可以在方法签名中看到,方法参数的名称与表单元素匹配。当表单提交时,表单参数将放置在这些参数中。此操作查找所有 IsActive
标志等于表单中选定值的用户,并且 Rating 大于表单输入值。
请注意,在本例中,我没有创建用于显示搜索结果的视图。在标准情况下,我需要创建一个 Search 视图,该视图将接收匹配搜索条件的用户,并将它们显示在某种表格中。然而,由于我已经有了那种视图(Index),我将使用它。
此查询的结果(模型)将传递给名为 Index
的视图。Index 视图不关心是哪个控制器调用了它——它只会显示提供的模型。结果是,您将在索引页上看到按搜索条件过滤的用户。您可以看到我使用了 View("Index", model)
调用而不是 View(model)
,以防止 Controller
操作通过操作名称匹配视图。
验证 (Validation)
应用程序中应添加的最重要的事情之一是验证。例如,您需要设置一些验证规则来检查是否输入了用户名,出生日期是否是有效日期,评分是否在 0 到 10 的范围内,等等。如果某些验证规则不满足,将显示类似下图的错误消息
在视图页面中,您已经看到我放置了 ValidationFor
元素。以下清单展示了一个将在 DoB
属性发生错误时显示错误消息的验证占位符的示例
<div class="editor-label">
<label for="DoB">Date of birth</label>
</div>
<div class="editor-field">
<input type="text" name="DoB" id="DoB" value="@Model.DoB"/>
@Html.ValidationMessageFor(model => model.DoB)
</div>
此代码用于显示验证消息,但您需要实现一些规则来检查是否满足验证要求。在本文中,我将向您展示三种实现验证规则的方法
- 使用模型中的数据注释
- 使用模型元数据类中的注释
- 使用显式规则
您可以在“ASP.NET MVC3 验证基础”文章中找到有关验证的更多详细信息。
使用数据注释
实现验证的最简单方法是在模型类中放置注释属性。例如,我将在用户的 Name
属性中放置 [Required]
属性。
public partial class User
{
public int UserID { get; set; }
[Required]
public string Name { get; set; }
public string Address { get; set; }
public string Town { get; set; }
public string County { get; set; }
public string Country { get; set; }
public string Email { get; set; }
public DateTime DoB { get; set; }
public bool IsActive { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public int Rating { get; set; }
}
如果用户未填写用户名,UpdateModel
将引发异常,并且必需字段消息将在视图中显示。
使用元数据类 (MetaData Class)
有时您无法修改模型类,特别是如果它是自动生成的。在这种情况下,您可以为模型类创建一个 MetaData
类,并在其中放置验证规则。以下清单展示了一个示例
[MetadataType(typeof(UserMetadata))]
public partial class User
{
public class UserMetadata
{
[StringLength(50), Required]
public object Name { get; set; }
[StringLength(5)]
public object Email { get; set; }
[Range(0, 10)]
public object Rating { get; set; }
}
}
在这种情况下,在模型类中定义 UserMetaData
将是 User
类的元数据类,其中将放置验证规则。在元数据类中,您可以放置与模型属性同名的属性并在那里放置注释。这是实现验证的一个有用方法,即使您可以修改原始模型类,但您不希望混合验证规则和属性。唯一的先决条件是模型类被定义为部分类,以便我们可以将类的第二部分放在单独的文件中。
自定义验证
第三种选择是将自定义验证规则直接放入控制器代码中。以下清单展示了在控制器中显式设置错误消息的示例
ModelState.AddModelError("Country", "This is useless message attached to the 'Country' input");
AddModelError
方法接收输入元素的 ID(应显示错误标签的位置)和错误消息的文本。此代码放置在控制器的操作方法中,它会添加一个新错误,该错误将与 Country
输入相关联。
高级主题 (Advanced Topics)
在本节中,您可以找到 MVC 框架中的一些高级主题。这些实际上不是初学者级别的项目,但我相信它们可能是一个很好的进一步研究的指导。
路由 (Routing)
如您所见,MVC 框架会将 URL 映射到控制器和操作。例如,如果您有一个 URL,如 https:///User/Details/1,它将被映射到 UserController
和 Details
方法。这不是硬编码的映射规则。如果您打开 Global.asax.cs 文件,可能会看到以下代码部分
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
// Parameter defaults
);
}
在此代码中,定义了用于将 URL 模式映射到 Controller/Action 的默认路由。第一个标记 {controller}
将决定调用哪个控制器,第二个标记 {action}
将映射到方法,最后一个是可选的,将作为 ID 传递。此外,这里还指定了默认值。如果您只输入 https:///User/,将调用 Index
方法,尽管未指定操作。如果您只输入 https:///,将调用 UserController
并使用默认的 Index
方法。如果您想使用不同的路由,可以添加自定义规则。例如,我将为用户搜索添加此规则
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Search", // Route name
"Search/{keyword}", // URL with parameters
new { controller = "User", action = "SearchByKeyword",
keyword = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
// Parameter defaults
);
}
“Search
”规则将采用以 /Search 开头的 URL,并调用 UserController
中的 Search
方法。搜索之后的第一个标记将被作为关键字传递给 SearchKeyword
方法。例如,如果您在浏览器中输入 https:///Search/Andersson,将调用 UserController
,并将字符串 "Andersson"
传递给 Search
方法。您需要做的就是添加执行搜索的操作——类似以下清单中的内容
public ActionResult SearchByKeyword(string keyword)
{
var model = UserRepository.Users.Where(user => user.Name.Contains(keyword));
return View("Index", model);
}
您可以在此处定义任何您想要的路由。请注意,也可以使用默认路由来调用此控制器。例如,如果您输入 https:///User/SearchByKeyword?keyword=Andersson,将调用相同的操作,因为它已通过第二个规则映射。
区域 (Areas)
在标准应用程序中,您有一组位于根文件夹中的控制器/视图。但是,您可以创建一组不同的控制器和视图,并将它们放在单独的区域中。如果您右键单击项目,选择添加 > 新建区域,您将能够创建应用程序的新子区域。新区域将放置在 Areas 文件夹中,并拥有独立的 Model、View 和 Controllers,如下图所示
在这里,您可以看到应用程序中有两个区域,名为 Admin 和 Site。此外,您将为每个区域拥有不同的 URL,并具有单独的路由机制。默认情况下,区域中的控制器通过 <Area>/<<Controller>>/<<Action>> 格式调用(例如,Admin/User/Index 或 Site/User/Search)。新路由在每个区域中放置的 AreaRegistration.cs 文件中定义。例如,如果您创建一个 Admin 区域,以下代码将被添加到 AdminAreaRegistration.cs 文件中
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
此代码添加了一个新的映射规则,该规则将发送到区域的请求映射到区域控制器。您也可以在此处为区域添加自己的映射。
视图包 (View Bag)
在标准的用法中,您的控制器会将模型(或对象集合)传递给视图。然而,有时您可能希望传递几个不是模型一部分的不同对象。例如,您可能希望传递信息,这些信息将放置在标题中的 meta 标签(页面标题、meta 关键字和 meta 描述)。如果这些信息不是从模型中计算出来的,那么从控制器向视图传递不同的对象集可能会很困难。
如果您需要传递模型之外的其他对象,可以使用 ViewBag
集合。ViewBag
是一组可以传递给视图的动态属性。以下清单展示了向 bag 添加附加属性的示例
public ActionResult Index()
{
ViewBag.Title = "Index page";
ViewBag.SeoDescription = "Description of the page";
ViewBag.SeoKeywords = "MVC, ASP.NET, CodeProject";
var model = UserRepository.Users;
return View(model);
}
在 View
中,您可以使用与标准 Model
对象相同的方式使用视图包中的属性。以下清单展示了一个示例
<html>
<head>
<title>@ViewBag.Title</title>
<meta name="description" content="@ViewBag.SeoDescription">
<meta name="keyword" content="@ViewBag.SeoKeywords">
</head>
<body>
</body>
</html>
如果您需要显示来自配置/资源文件中的某些信息,从数据库中读取它们,或者从另一个类的属性/方法中读取它们,最好在控制器中加载它们,然后通过 ViewBag
将它们传递给视图。
自定义视图模板 (Custom View Templates)
在上面的示例中,我描述了如何使用 HTML 辅助方法生成 HTML,而不是直接在代码中编写纯 HTML,以及如何注入模型的值。例如,您可以使用以下代码为 Name
、IsActive
和 Password
属性生成输入框和复选框
@Html.EditorFor(model => model.Name)
@Html.EditorFor(model => model.IsActive)
@Html.EditorFor(model => model.Password)
当您调用 Html.EditorFor
方法时,它会检查属性的类型,然后使用模板来编辑该类型。默认情况下,文本类型的输入使用 string
和 DateTime
的编辑器,布尔值使用 checkbox
等。
但是,您可以定义自己的自定义显示和编辑器模板,这些模板将根据类型应用。您只需在 <TypeName>.cshtml 格式的子文件夹 /DisplayTemplates 和 /EditorTemplates 中创建部分视图。当使用 Html.DisplayFor
辅助方法时,MVC 框架将尝试根据要显示的属性类型查找应使用的模板。如果您在 /DisplayTemplates 文件夹中有一个匹配的 <TypeName>.cshtml 文件,框架将使用该模板显示对象,而不是默认模板。相同的逻辑也适用于编辑器模板。例如,假设您想在 INPUT
文本框中以类“calendar
”渲染 DateTime
属性。您可以创建一个 DateTime.cshtml 文件并将其放入 /EditorTemplates 子文件夹,而不是在每次出现 input
时都添加类“calendar
”。然后,该模板将用于渲染每个 DateTime
属性的编辑器。以下清单展示了 DateTime.cshtml 文件的示例
@inherits System.Web.Mvc.WebViewPage<System.DateTime>
@Html.TextBox("", (Model.ToShortDateString()), new { @class = "calendar" })
您可以将 EditorTemplates 文件夹放在 /Views/Shared 文件夹、/Views/User 文件夹,或者两者都放在其中,如下图所示
如果您将其放在 /Shared 文件夹中,它可以在应用程序中的任何视图中使用。但是,如果您将其放在特定文件夹(例如,/User)中,它将仅在这些文件夹中的视图中使用。如果您想更改某些数据类型的默认显示方式,请使用自定义视图模板。
扩展方法 (Extension Methods)
如上所述,HtmlHelper
类中有许多辅助方法可用于生成文本框、复选框、密码字段等。以下清单展示了一些示例
@Html.TextBox("Name", Model.Name)
@Html.TextArea("Address", Model.Address)
@Html.CheckBox("IsActive", Model.IsActive)
但是,如果您需要更多类型,可以通过扩展 HtmlHelper
类来添加它们。想象一下,您需要一个方法来生成日历,如上面的示例(带有类 calendar
的输入)。您可以添加以下 DateTime
辅助扩展
namespace System.Web.Mvc
{
public static class HtmlDateTimeExtension
{
public static MvcHtmlString DateTime
(this HtmlHelper helper, string name, string label, DateTime val)
{
return MvcHtmlString.Create( String.Format(@"<label for='{0}'>{1}</label>
<input type='text' name='{0}' id='{0}' value='{2}' class='calendar'/>",
name,
label,
val.ToShortDateString()) );
}
}
}
您只需要做的是添加一个新的 static
方法,该方法的第一个参数是这个 HtmlHelper
辅助,这个方法将被添加到其他 HtmlHelper
static
方法中。其他参数将用作方法的参数。此方法应返回一个 HTML string
,该字符串将被放置在输出 HTML 中。现在,您可以使用 HTML 类直接生成 DateTime
输入,如以下清单所示
@Html.DateTime("DoB", "Date of birth", Model.DoB);
效果与自定义视图相同,只是您需要显式调用此方法。如果您需要在方法中添加一些附加代码,例如根据条件选择不同的模板,在数据库中检查是否应显示此元素等,这种方法很有用。
如果您需要在自定义视图模板和扩展方法之间进行选择,选择很简单。如果您需要大量 C# 代码来生成输出,请使用扩展方法;否则,如果您有大量 HTML 代码,并且只有少数地方用于放置数据,请使用自定义视图。
辅助方法 (Helper Methods)
组织 View
中代码的另一种方法是创建内联辅助方法。内联辅助方法是直接在 View
中定义的普通函数,可以像任何标准函数一样调用。以下清单展示了一个辅助函数的示例
@helper DateTime(string name, string label, DateTime val){
@String.Format(@"<label for='{0}'>{1}</label>
<input type='text' name='{0}' id='{0}' value='{2}' class='calendar'/>",
name,
label,
val.ToShortDateString())
}
创建辅助函数后,就可以像任何函数一样在视图中使用它。以下清单展示了一个示例
@DateTime("DoB", "DoB", Model.DoB);
当您想重构视图中重复的代码时,辅助函数很有用;然而,如果您将在多个 View
s 中使用它,我认为最好使用一些 HTML 扩展辅助方法或自定义视图。您可以在ScottGu 的帖子中找到有关辅助方法的更多详细信息。
结论
在本文中,我向您展示了带有 Razor 语法的 ASP.NET MVC3 的基础知识。遵循这些说明并使用附带的代码,您将能够创建一个简单的 MVC Web 应用程序并对其进行扩展。
请注意,MVC 还有更多内容——您可以找到许多附加功能和自定义项,例如与 Entity Framework 集成、使用区域、定义自定义路由、应用过滤器等。您可以查看ASP.NET MVC 网站,了解有关这些功能的更多详细信息。此外,我在这里为视图使用了 Razor 语法,但是您可能想探索其他备选的视图语法,如标准 ASP.NET、Spark 或 NHaml。您可以探索很多可能性,希望这将是您一个好的起点。
历史
- 2012 年 3 月 13 日:初始版本