單一頁面應用程式:KnockoutJS 範本
此 MVC 範本是 ASP.NET 和 Web 工具 2012.2 的一部分
ASP.NET 和 Web 工具 2012.2 更新包含適用于 ASP.NET MVC 4 的 Single-Page 應用程式 (SPA) 範本。 此範本的設計目的是讓您快速開始建置互動式用戶端 Web 應用程式。
「單頁應用程式」 (SPA) 是 Web 應用程式的一般詞彙,它會載入單一 HTML 頁面,然後動態更新頁面,而不是載入新頁面。 初始頁面載入之後,SPA 會透過 AJAX 要求與伺服器通訊。
AJAX 不是新的,但目前有 JavaScript 架構可讓您更輕鬆地建置和維護大型複雜 SPA 應用程式。 此外,HTML 5 和 CSS3 可讓您更輕鬆地建立豐富的 UI。
為了讓您開始使用,SPA 範本會建立範例「To-do 清單」應用程式。 在本教學課程中,我們將引導式導覽範本。 首先,我們將查看待辦事項清單應用程式本身,然後檢查讓它運作的技術片段。
建立新的 SPA 範本專案
需求:
- Visual Studio 2012 或 Visual Studio Express 2012 for Web
- ASP.NET Web Tools 2012.2 更新。 您可以 在這裡安裝更新。
啟動 Visual Studio,然後從 [開始] 頁面中選取 [ 新增專案 ]。 或者,從 [ 檔案] 功能表中,選取 [ 新增 ],然後選取 [ 專案]。
在 [ 範本] 窗格中,選取 [ 已安裝的範本 ],然後展開 [Visual C# ] 節點。 在 [Visual C#] 底下,選取 [Web]。 在專案範本清單中,選取 [ASP.NET MVC 4 Web 應用程式]。 為專案命名,然後按一下 [確定]。
在 [ 新增專案 精靈] 中,選取 [單一頁面應用程式]。
按 F5 以建置並執行應用程式。 應用程式第一次執行時,會顯示登入畫面。
按一下 [註冊] 連結,然後建立新的使用者。
登入之後,應用程式會建立具有兩個專案的預設待辦事項清單。 您可以按一下 [新增待辦事項清單] 來新增清單。
重新命名清單、將專案新增至清單,然後將它們簽出。 您也可以刪除專案或刪除整個清單。 此時,變更會自動儲存到伺服器上的資料庫 (實際上為 LocalDB,因為您是在本機執行應用程式) 。
SPA 範本的架構
下圖顯示應用程式的主要建置組塊。
在伺服器端,ASP.NET MVC 會提供 HTML,也會處理表單型驗證。
ASP.NET Web API處理與 ToDoLists 和 ToDoItems 相關的所有要求,包括取得、建立、更新和刪除。 用戶端會以 JSON 格式與 Web API 交換資料。
Entity Framework (EF) 是 O/RM 層。 它會在 ASP.NET 的物件導向世界與基礎資料庫之間進行媒體處理。 資料庫會使用 LocalDB,但您可以在 Web.config 檔案中變更此專案。 一般而言,您會使用 LocalDB 進行本機開發,然後使用 EF 程式碼優先移轉部署到伺服器上的 SQL 資料庫。
在用戶端上,Knockout.js程式庫會處理來自 AJAX 要求的頁面更新。 將頁面與最新資料同步處理時,會使用資料系結。 如此一來,您就不需要撰寫任何逐步解說 JSON 資料和更新 DOM 的程式碼。 相反地,您會在 HTML 中放置宣告式屬性,告知您如何呈現資料。
此架構的一大優點是,它會將呈現層與應用程式邏輯分開。 您可以建立 Web API 部分,而不需要知道網頁的外觀。 在用戶端上,您會建立「檢視模型」來代表該資料,而檢視模型會使用一個將它系結至 HTML。 這可讓您輕鬆地變更 HTML,而不需要變更檢視模型。 (我們將稍後再看一下)
模型
在 Visual Studio 專案中,Models 資料夾包含伺服器端所使用的模型。 (用戶端也有模型;我們將前往 those.)
TodoItem、TodoList
這些是 Entity Framework Code First 的資料庫模型。 請注意,這些模型具有指向彼此的屬性。 ToDoList
包含 ToDoItems 的集合,而且每個 ToDoItem
集合都有其父 ToDoList 的參考。 這些屬性稱為導覽屬性,它們代表一對多關聯性 to-do 清單及其 To-do 專案。
類別 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
會定義成員資格 DB 中使用者設定檔的架構。 (在此情況下,唯一的資訊是使用者識別碼和使用者名稱。) 此檔案中的其他模型類別是用來建立使用者註冊和登入表單。
Entity Framework
SPA 範本會使用 EF Code First。 在 Code First 開發中,您會先在程式碼中定義模型,然後 EF 會使用模型來建立資料庫。 您也可以搭配現有的資料庫使用 EF, (Database First) 。
TodoItemContext
Models 資料夾中的 類別衍生自DbCoNtext。 這個類別提供模型與 EF 之間的「黏附」。 會 TodoItemContext
保存 ToDoItem
集合和 TodoList
集合。 若要查詢資料庫,您只需針對這些集合撰寫 LINQ 查詢即可。 例如,以下是您可以選取使用者 「Alice」 的所有 To-do 清單的方式:
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 資料夾中。
TodoController
:處理對辦事項的 HTTP 要求TodoListController
:處理 To-do 清單的 HTTP 要求。
這些名稱很重要,因為 Web API 會比對控制器名稱的 URI 路徑。 (若要瞭解 Web API 如何將 HTTP 要求路由傳送至控制器,請參閱 ASP.NET Web API.) 中的路由
讓我們看看 類別 ToDoListController
。 它包含單一資料成員:
private TodoItemContext db = new TodoItemContext();
TodoItemContext
用來與 EF 通訊,如先前所述。 控制器上的方法會實作 CRUD 作業。 Web API 會將來自用戶端的 HTTP 要求對應至控制器方法,如下所示:
HTTP 要求 | Controller 方法 | 描述 |
---|---|---|
GET /api/todo | GetTodoLists |
取得 To-do 清單的集合。 |
GET /api/todo/id | GetTodoList |
依識別碼取得 To-do 清單 |
PUT /api/todo/id | PutTodoList |
更新 to-do 清單。 |
POST /api/todo | PostTodoList |
建立新的 To-do 清單。 |
DELETE /api/todo/id | DeleteTodoList |
刪除待辦事項清單。 |
請注意,某些作業的 URI 包含識別碼值的預留位置。 例如,若要刪除識別碼為 42 的 to-list,URI 為 /api/todo/42
。
若要深入瞭解如何針對 CRUD 作業使用 Web API,請參閱 建立支援 CRUD 作業的 Web API。 此控制器的程式碼相當簡單。 以下是一些有趣的重點:
- 方法
GetTodoLists
會使用 LINQ 查詢,依登入使用者的識別碼來篩選結果。 如此一來,使用者只會看到屬於其的資料。 此外,請注意,Select 語句是用來將ToDoList
實例轉換成TodoListDto
實例。 - PUT 和 POST 方法會在修改資料庫之前檢查模型狀態。 如果 ModelState.IsValid 為 false,這些方法會傳回 HTTP 400,不正確的要求。 若要深入瞭解 Web API 中的模型驗證,請參閱 模型驗證。
- 控制器類別也會以 [Authorize] 屬性裝飾。 這個屬性會檢查 HTTP 要求是否已通過驗證。 如果未驗證要求,用戶端會收到 HTTP 401,未經授權。 若要深入瞭解驗證,請參閱ASP.NET Web API 中的驗證和授權。
類別 TodoController
非常類似于 TodoListController
。 最大的差異在於它不會定義任何 GET 方法,因為用戶端會取得 To-do 專案以及每個 To-do 清單。
MVC 控制器和檢視
MVC 控制器也位於方案的 Controllers 資料夾中。 HomeController
會呈現應用程式的主要 HTML。 Home 控制器的檢視定義于 Views/Home/Index.cshtml 中。 [首頁] 檢視會根據使用者是否登入來轉譯不同的內容:
@if (@User.Identity.IsAuthenticated)
{
// ....
}
當使用者登入時,他們會看到主要 UI。 否則,他們會看到登入面板。 請注意,此條件式轉譯會在伺服器端發生。 絕不會嘗試隱藏用戶端上的敏感性內容—您透過 HTTP 回應傳送的任何專案,都可以看到正在監看原始 HTTP 訊息的人員。
Client-Side JavaScript 和 Knockout.js
現在讓我們從應用程式的伺服器端轉向用戶端。 SPA 範本會使用 jQuery 和Knockout.js的組合來建立順暢的互動式 UI。 Knockout.js是 JavaScript 程式庫,可讓您輕鬆地將 HTML 系結至資料。 Knockout.js使用名為 「Model-View-ViewModel」 的模式。
- 此模型是待辦事項清單和待辦事項) (領域資料。
- 檢視是 HTML 檔案。
- 檢視模型是保留模型資料的 JavaScript 物件。 檢視模型是 UI 的程式碼抽象概念。 它不知道 HTML 標記法。 相反地,它代表檢視的抽象特徵,例如「待辦事項清單」。
檢視會系結至檢視模型的資料。 檢視模型更新會自動反映在檢視中。 系結也會以其他方向運作。 DOM (中的事件,例如點選) ,會系結至檢視模型上的函式,這會觸發 AJAX 呼叫。
SPA 範本會將用戶端 JavaScript 組織成三層:
- todo.datacontext.js:傳送 AJAX 要求。
- todo.model.js:定義模型。
- todo.viewmodel.js:定義檢視模型。
這些腳本檔案位於方案的 Scripts/app 資料夾中。
todo.datacoNtext 會處理 Web API 控制器的所有 AJAX 呼叫。 (在 ajaxlogin.js.) 中定義用於登入的 AJAX 呼叫
todo.model.js 定義 to-do 清單的用戶端 (瀏覽器) 模型。 有兩個模型類別:todoItem 和 todoList。
模型類別中的許多屬性類型為 「ko.observable」。 可觀察是一種可觀察專案如何執行其魔術。 從 「可觀察」檔:可觀察的是「JavaScript 物件,可通知訂閱者有關變更」。當可觀察的值變更時,會更新系結至這些可觀察專案的任何 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 範本中,檢視模型包含 todoLists 的可觀察陣列。 檢視模型中的下列程式碼會指示一個將系結套用到一起:
ko.applyBindings(window.todoApp.todoListViewModel);
HTML 和資料系結
頁面的主要 HTML 定義于 Views/Home/Index.cshtml 中。 因為我們使用資料系結,所以 HTML 只是實際呈現內容的範本。 會使用 宣告式系 結。 您可以將 「資料系結」屬性新增至 元素,以將頁面元素系結至資料。 以下是一個非常簡單的範例,取自一般檔:
<p>There are <span data-bind="text: myItems().count"></span> items<p>
在此範例中,會以 的值 myItems.count()
更新span > 元素的內容 <。 每當此值變更時,會更新 Document。
外門提供數種不同的系結類型。 以下是 SPA 範本中使用的一些系結:
- foreach:可讓您逐一查看迴圈,並將相同的標記套用至清單中的每個專案。 這可用來轉譯 To-do 清單和 To-do 專案。 在 foreach內,系結會套用至清單的專案。
- 可見:用來切換可見度。 當集合是空的時隱藏標記,或讓錯誤訊息可見。
- value:用來填入表單值。
- click:將 click 事件系結至檢視模型上的函式。
反 CSRF 保護
跨網站偽造要求 (CSRF) 是惡意網站將要求傳送至使用者目前登入的易受攻擊網站。 為了協助防止 CSRF 攻擊,ASP.NET MVC 使用 反偽造權杖,也稱為要求驗證權杖。 概念是伺服器會將隨機產生的權杖放入網頁。 當用戶端將資料提交至伺服器時,它必須在要求訊息中包含此值。
防偽造權杖的運作方式是惡意頁面無法讀取使用者的權杖,因為相同的原始原則。 (相同原始原則可防止裝載于兩個不同網站上的檔存取彼此的內容。)
ASP.NET MVC 透過 AntiForgery 類別和 [ValidateAntiForgeryToken] 屬性,提供反偽造權杖的內建支援。 目前,此功能並未內建于 Web API 中。 不過,SPA 範本包含 Web API 的自訂實作。 此程式碼定義于 類別中 ValidateHttpAntiForgeryTokenAttribute
,其位於方案的 [篩選] 資料夾中。 若要深入瞭解 Web API 中的反 CSRF,請參閱 防止跨網站偽造要求 (CSRF) 攻擊。
結論
SPA 範本的設計目的是讓您快速撰寫現代化互動式 Web 應用程式。 它會使用 Knockout.js 程式庫,將呈現 (HTML 標籤) 與資料和應用程式邏輯分開。 但是,要建立 SPA 的唯一 JavaScript 程式庫不是唯一的 JavaScript 程式庫。 如果您想要探索一些其他選項,請查看 社群建立的 SPA 範本。