单页应用程序:KnockoutJS 模板

淘汰 MVC 模板是 ASP.NET 和 Web 工具 2012.2 的一部分

下载 ASP.NET 和 Web 工具 2012.2

ASP.NET 和 Web 工具 2012.2 更新包括适用于 ASP.NET MVC 4 的 Single-Page 应用程序 (SPA) 模板。 此模板旨在帮助你快速开始构建交互式客户端 Web 应用。

“单页应用程序” (SPA) 是 Web 应用程序的通用术语,该应用程序加载单个 HTML 页面,然后动态更新页面,而不是加载新页面。 初始页面加载后,SPA 通过 AJAX 请求与服务器通信。

显示标记为“客户端”和“服务器”的两个框的关系图。标记为 AJAX 的箭头从客户端转到服务器。标记为 H T M L 的箭头和标记为 J SON 的箭头从服务器转到客户端。

AJAX 并不是什么新鲜事,但如今有 JavaScript 框架可以更轻松地生成和维护大型复杂 SPA 应用程序。 此外,HTML 5 和 CSS3 使创建丰富的 UI 变得更加容易。

为了帮助你入门,SPA 模板创建了一个示例“To-do list”应用程序。 在本教程中,我们将进行模板的引导教程。 首先,我们将查看待办事项列表应用程序本身,然后检查使其正常工作的技术部分。

创建新的 SPA 模板项目

要求:

  • Visual Studio 2012 或 Visual Studio Express 2012 for Web
  • ASP.NET Web 工具 2012.2 更新。 可以 在此处安装更新。

启动 Visual Studio,然后从“开始”页中选择“ 新建项目 ”。 或者,从“ 文件 ”菜单中选择“ 新建 ”,然后选择“ 项目”。

“模板 ”窗格中,选择“ 已安装的模板 ”,然后展开 “Visual C# ”节点。 在 “Visual C#”下,选择“ Web”。 在项目模板列表中,选择“ ASP.NET MVC 4 Web 应用程序”。 给该项目命名,然后单击“确定”。

显示“新建项目”对话框的屏幕截图。从 Web 模板列表中选择了 S P dot NET M V C 4 Web 应用程序。

“新建项目 ”向导中,选择“ 单页应用程序”。

显示“新建 A S P 点 NET M V C 4 项目”对话框的屏幕截图。已选择“单页应用程序”模板。

按 F5 生成并运行应用程序。 应用程序首次运行时,会显示登录屏幕。

显示“我的办事项列表”登录屏幕的屏幕截图。

单击“注册”链接并创建新用户。

显示“注册”屏幕的屏幕截图。

登录后,应用程序会创建包含两个项的默认 Todo 列表。 可以单击“添加 Todo 列表”添加新列表。

显示顶部的两个“未完成事项列表”和“添加未完成事项列表”按钮的屏幕截图。

重命名列表,将项添加到列表中,然后检查它们。 还可以删除项目或删除整个列表。 此时,更改会自动保存到服务器上的数据库, (实际为 LocalDB,因为在本地运行应用程序) 。

显示包含三个项的列表的屏幕截图。最后一项经过检查,并通过它进行罢工。

SPA 模板的体系结构

此图显示了应用程序的main构建基块。

显示客户端和服务器的单独构建基块的关系图。淘汰点 j s、H T M L 和 J SON 位于“客户端”下。S P dot NET M V C、A S P dot NET Web A P I、Entity Framework 和 Database 位于服务器下。

在服务器端,ASP.NET MVC 为 HTML 提供服务,还处理基于表单的身份验证。

ASP.NET Web API处理与 ToDoLists 和 ToDoItems 相关的所有请求,包括获取、创建、更新和删除。 客户端以 JSON 格式与 Web API 交换数据。

实体框架 (EF) 是 O/RM 层。 它在面向对象的 ASP.NET 世界与基础数据库之间进行中介。 数据库使用 LocalDB,但你可以在 Web.config 文件中对此进行更改。 通常,使用 LocalDB 进行本地开发,然后使用 EF 代码优先迁移部署到服务器上的 SQL 数据库。

在客户端,Knockout.js库处理来自 AJAX 请求的页面更新。 Knockout 使用数据绑定将页面与最新数据同步。 这样,就不必编写任何遍历 JSON 数据并更新 DOM 的代码。 相反,你将声明性属性放在 HTML 中,告诉 Knockout 如何呈现数据。

此体系结构的一大优点是它将表示层与应用程序逻辑分开。 可以在不知道网页外观的情况下创建 Web API 部分。 在客户端上,创建一个“视图模型”来表示该数据,视图模型使用 Knockout 绑定到 HTML。 这样,无需更改视图模型即可轻松更改 HTML。 (稍后我们将介绍淘汰赛。)

模型

在 Visual Studio 项目中,Models 文件夹包含服务器端使用的模型。 (客户端上也有模型;我们将访问那些。)

显示“模型”文件夹已打开的屏幕截图。

TodoItem、TodoList

这些是 Entity Framework Code First 的数据库模型。 请注意,这些模型具有相互指向的属性。 ToDoList 包含 ToDoItems 的集合,并且每个集合都有 ToDoItem 对其父 ToDoList 的引用。 这些属性称为导航属性,它们表示一对多关系,即一对多关系,即“一对多”列表及其未完成事项。

ToDoItem 还使用 [ForeignKey] 属性来指定 ToDoListId 是表中的 ToDoList 外键。 这会告知 EF 向数据库添加外键约束。

[ForeignKey("TodoList")]
public int TodoListId { get; set; }
public virtual TodoList TodoList { get; set; }

TodoItemDto、TodoListDto

这些类定义将发送到客户端的数据。 “DTO”代表“数据传输对象”。DTO 定义如何将实体序列化为 JSON。 通常,使用 DTO 有几个原因:

  • 控制序列化哪些属性。 DTO 可以包含域模型中的属性子集。 这样做可能是出于安全原因, (隐藏敏感数据) 或只是为了减少发送的数据量。
  • 更改数据的形状,例如平展更复杂的数据结构。
  • 若要将任何业务逻辑排除在 DTO 之外, () 关注点分离。
  • 如果由于某种原因无法序列化域模型。 例如,序列化对象时循环引用可能会导致问题 Web API 中处理此问题的方法 (请参阅 处理循环对象引用) ;但使用 DTO 完全避免了此问题。

在 SPA 模板中,DTO 包含与域模型相同的数据。 但是,它们仍然很有用,因为它们避免了导航属性中的循环引用,并演示了常规 DTO 模式。

AccountModels.cs

此文件包含网站成员身份的模型。 类 UserProfile 定义成员资格数据库中用户配置文件的架构。 (在这种情况下,唯一的信息是用户 ID 和用户名。) 此文件中的其他模型类用于创建用户注册和登录表单。

Entity Framework

SPA 模板使用 EF Code First。 在 Code First 开发中,首先在代码中定义模型,然后 EF 使用该模型创建数据库。 还可以将 EF 与现有数据库一起使用 (Database First) 。

TodoItemContext Models 文件夹中的 类派生自 DbContext。 此类提供模型和 EF 之间的“粘附”。 包含 TodoItemContext 一个 ToDoItem 集合和一个 TodoList 集合。 若要查询数据库,只需针对这些集合编写 LINQ 查询。 例如,下面介绍如何为用户“Alice”选择所有要办事项列表:

TodoItemContext db = new TodoItemContext();
IEnumerable<TodoList> lists = 
    from td in db.TodoLists where td.UserId == "Alice" select td;

还可以向集合添加新项、更新项或删除集合中的项,并将更改保存到数据库。

ASP.NET Web API 控制器

在 ASP.NET Web API中,控制器是处理 HTTP 请求的对象。 如前所述,SPA 模板使用 Web API 在 和 ToDoItem 实例上ToDoList启用 CRUD 操作。 控制器位于解决方案的 Controllers 文件夹中。

显示“控制器”文件夹已打开的屏幕截图。To do Controller dot c s 和 To do List Controller dot c s 均以红色圈出。

  • TodoController:处理待办事项的 HTTP 请求
  • TodoListController:处理待办事项列表的 HTTP 请求。

这些名称非常重要,因为 Web API 将 URI 路径与控制器名称匹配。 (若要了解 Web API 如何将 HTTP 请求路由到控制器,请参阅 ASP.NET Web API.) 中的路由

让我们看一下 ToDoListController 类。 它包含单个数据成员:

private TodoItemContext db = new TodoItemContext();

TodoItemContext用于与 EF 通信,如前所述。 控制器上的方法实现 CRUD 操作。 Web API 将 HTTP 请求从客户端映射到控制器方法,如下所示:

HTTP 请求 控制器方法 说明
GET /api/todo GetTodoLists 获取未完成事项列表的集合。
GET /api/todo/id GetTodoList 按 ID 获取一个未完成事项列表
PUT /api/todo/id PutTodoList 汇报一个未完成事项列表。
POST /api/todo PostTodoList 创建新的“任务”列表。
DELETE /api/todo/id DeleteTodoList 删除 TODO 列表。

请注意,某些操作的 URI 包含 ID 值的占位符。 例如,若要删除 ID 为 42 的要列表,URI 为 /api/todo/42

若要详细了解如何将 Web API 用于 CRUD 操作,请参阅 创建支持 CRUD 操作的 Web API。 此控制器的代码相当简单。 下面是一些有趣的要点:

  • 方法 GetTodoLists 使用 LINQ 查询按登录用户的 ID 筛选结果。 这样,用户只能看到属于他或她的数据。 另请注意,Select 语句用于将 ToDoList 实例转换为 TodoListDto 实例。
  • PUT 和 POST 方法在修改数据库之前检查模型状态。 如果 ModelState.IsValid 为 false,则这些方法将返回 HTTP 400 错误请求。 有关 Web API 中的模型验证的详细信息,请参阅 模型验证
  • 控制器类还使用 [Authorize] 属性进行修饰。 此属性检查是否对 HTTP 请求进行身份验证。 如果未对请求进行身份验证,则客户端会收到 HTTP 401,未授权。 有关身份验证的详细信息,请参阅 ASP.NET Web API 中的身份验证和授权

TodoController 与 非常相似 TodoListController。 最大的区别在于它不定义任何 GET 方法,因为客户端将获取每个未完成事项列表。

MVC 控制器和视图

MVC 控制器也位于解决方案的 Controllers 文件夹中。 HomeController呈现应用程序的main HTML。 主控制器的视图在 Views/Home/Index.cshtml 中定义。 “主页”视图根据用户是否已登录呈现不同的内容:

@if (@User.Identity.IsAuthenticated)
{
    // ....
}

用户登录后,会看到main UI。 否则,他们会看到登录面板。 请注意,此条件呈现发生在服务器端。 切勿尝试在客户端隐藏敏感内容- 在 HTTP 响应中发送的任何内容都对正在观看原始 HTTP 消息的用户可见。

Client-Side JavaScript 和 Knockout.js

现在,让我们从应用程序的服务器端转向客户端。 SPA 模板结合使用 jQuery 和 Knockout.js 来创建流畅的交互式 UI。 Knockout.js是一个 JavaScript 库,可以轻松地将 HTML 绑定到数据。 Knockout.js使用名为“Model-View-ViewModel”的模式。

  • 该模型是 toDo 列表和 toDo 项) (域数据。
  • 视图是 HTML 文档。
  • 视图模型是保存模型数据的 JavaScript 对象。 视图模型是 UI 的代码抽象。 它不知道 HTML 表示形式。 相反,它表示视图的抽象功能,例如“ToDo 项列表”。

视图的数据绑定到视图模型。 视图模型的汇报会自动反映在视图中。 绑定也适用于另一个方向。 DOM (中的事件(如单击) )将数据绑定到视图模型上的函数,从而触发 AJAX 调用。

SPA 模板将客户端 JavaScript 组织为三个层:

  • todo.datacontext.js:发送 AJAX 请求。
  • todo.model.js:定义模型。
  • todo.viewmodel.js:定义视图模型。

此图显示了一个箭头,从“敲除点 j”到“查看模型”到“模型”到“数据上下文”。敲除点 j 和视图模型之间的箭头标记为数据绑定,并指向这两项。

这些脚本文件位于解决方案的脚本/应用文件夹中。

显示标记为“打开的应用”子文件夹的屏幕截图。

todo.datacontext 处理对 Web API 控制器的所有 AJAX 调用。 (ajaxlogin.js.)

todo.model.js 定义客户端 (浏览器) 模型以用于未完成事项列表。 有两个模型类:todoItem 和 todoList。

模型类中的许多属性的类型为“ko.observable”。 可观测值是敲门如何发挥其魔力。 在 Knockout 文档中:可观测对象是一个“JavaScript 对象,可以通知订阅者更改”。当可观测值发生更改时,Knockout 会更新绑定到这些可观测对象的任何 HTML 元素。 例如,todoItem 具有游戏和 isDone 属性的可观测值:

self.title = ko.observable(data.title);
self.isDone = ko.observable(data.isDone);

还可以在代码中订阅可观测对象。 例如,todoItem 类订阅“isDone”和“title”属性中的更改:

saveChanges = function () {
    return datacontext.saveChangedTodoItem(self);
};

// Auto-save when these properties change
self.isDone.subscribe(saveChanges);
self.title.subscribe(saveChanges);

查看模型

视图模型在 todo.viewmodel.js 中定义。 视图模型是应用程序将 HTML 页面元素绑定到域数据的中心点。 在 SPA 模板中,视图模型包含可观测的 todoList 数组。 视图模型中的以下代码指示 Knockout 应用绑定:

ko.applyBindings(window.todoApp.todoListViewModel);

HTML 和数据绑定

页面main HTML 在 Views/Home/Index.cshtml 中定义。 由于我们使用的是数据绑定,因此 HTML 只是实际呈现内容的模板。 敲除使用 声明性 绑定。 通过将“数据绑定”属性添加到 元素,将页面元素绑定到数据。 下面是一个非常简单的示例,取自 Knockout 文档:

<p>There are <span data-bind="text: myItems().count"></span> items<p>

在此示例中,Knockout 使用 值myItems.count()更新 span> 元素的内容<。 每当此值更改时,Knockout 将更新文档。

敲除提供了许多不同的绑定类型。 下面是 SPA 模板中使用的一些绑定:

  • foreach:允许循环访问循环,并将相同的标记应用于列表中的每个项。 这用于呈现未完成事项列表和要完成的项。 在 foreach 中,绑定将应用于列表的元素。
  • 可见:用于切换可见性。 当集合为空时隐藏标记,或使错误消息可见。
  • value:用于填充表单值。
  • click:将 click 事件绑定到视图模型上的函数。

反 CSRF 保护

跨站点请求伪造 (CSRF) 是恶意站点将请求发送到用户当前登录的易受攻击的站点的攻击。 为了帮助防止 CSRF 攻击,ASP.NET MVC 使用 防伪令牌,也称为请求验证令牌。 其思路是服务器将随机生成的令牌放入网页中。 当客户端将数据提交到服务器时,它必须在请求消息中包含此值。

防伪令牌之所以有效,是因为恶意页面由于同源策略而无法读取用户的令牌。 (同源策略阻止托管在两个不同网站上的文档访问彼此的内容。)

ASP.NET MVC 通过 AntiForgery 类和 [ValidateAntiForgeryToken] 属性为防伪令牌提供内置支持。 目前,此功能未内置于 Web API 中。 但是,SPA 模板包含 Web API 的自定义实现。 此代码在 类中 ValidateHttpAntiForgeryTokenAttribute 定义,该类位于解决方案的 Filters 文件夹中。 若要详细了解 Web API 中的反 CSRF,请参阅 防止跨站点请求伪造 (CSRF) 攻击

结论

SPA 模板旨在帮助你快速开始编写新式交互式 Web 应用程序。 它使用 Knockout.js 库将呈现 (HTML 标记) 与数据和应用程序逻辑分开。 但 Knockout 并不是可用于创建 SPA 的唯一 JavaScript 库。 如果要浏览其他一些选项,请查看 社区创建的 SPA 模板