シングル ページ アプリケーション: KnockoutJS テンプレート

Knockout MVC テンプレートは、ASP.NET and Web Tools 2012.2 の一部です

ASP.NET and Web Tools 2012.2 をダウンロード

ASP.NET and Web Tools 2012.2 更新プログラムには、ASP.NET MVC 4 用のシングルページ アプリケーション (SPA) テンプレートが含まれています。 このテンプレートは、対話型のクライアント側 Web アプリの構築をすばやく開始できるように設計されています。

"シングルページ アプリケーション" (SPA) は、新しいページを読み込むのではなく、単一の HTML ページを読み込み、ページを動的に更新する Web アプリケーションを指す一般的な用語です。 最初のページ読み込みの後、SPA は AJAX 要求を介してサーバーと対話します。

Diagram that shows two boxes labeled Client and Server. An arrow labeled AJAX goes from Client to Server. An arrow labeled H T M L and an arrow labeled J SON go from Server to Client.

AJAX は新しい機能ではありませんが、現在、大規模で高度な SPA アプリケーションの構築と保守を容易にする JavaScript フレームワークがあります。 また、HTML 5 と CSS3 を使用すると、豊富な UI を簡単に作成できます。

作業を開始するために、SPA テンプレートによってサンプルの "To Do リスト" アプリケーションが作成されます。 このチュートリアルでは、テンプレートのガイド付きツアーを行います。 まず、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 アプリケーション] を選択します。 プロジェクト名を指定して、 [OK] をクリックします。

Screenshot that shows the New Project dialog box. A S P dot NET M V C 4 Web Application is selected from the list of Web Templates.

[新しいプロジェクト] ウィザードで、[シングル ページ アプリケーション] を選択します。

Screenshot that shows the New A S P dot NET M V C 4 Project dialog box. The Single Page Application template is selected.

F5 キーを押してアプリケーションをビルドし、実行します。 アプリケーションを最初に実行したときに、ログイン画面が表示されます。

Screenshot that shows the My To do List login screen.

[サインアップ] リンクをクリックし、新しいユーザーを作成します。

Screenshot that shows the Sign up screen.

ログイン後、アプリケーションは 2 つの項目を含む既定の Todo リストを作成します。 [Todo リストの追加] をクリックして、新しいリストを追加できます。

Screenshot that shows two To do Lists and an Add To do List button at the top.

リストの名前を変更し、リストに項目を追加して、オフにします。 項目を削除したり、リスト全体を削除したりすることもできます。 変更は、サーバー上のデータベースに自動的に永続化されます (実際には、アプリケーションをローカルで実行しているため、この時点では LocalDB)。

Screenshot that shows a list with three items. The last item is checked and has a strike through it.

SPA テンプレートのアーキテクチャ

この図は、アプリケーションのメイン構成要素を示しています。

Diagram that shows the separate building blocks of the Client and Server. Knockout dot j s, H T M L, and J SON are under Client. A S P dot NET M V C, A S P dot NET Web A P I, Entity Framework, and Database are under Server.

サーバー側では、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 要求からのページ更新を処理します。 Knockout は、データ バインディングを使用して、ページを最新のデータと同期します。 これにより、JSON データを処理し、DOM を更新するコードを記述する必要はありません。 代わりに、データの表示方法を Knockout に指示する宣言属性を HTML に配置します。

このアーキテクチャの大きな利点は、プレゼンテーション レイヤーとアプリケーション ロジックを分離することです。 Web ページがどのように表示されるかについて何も知らなくても、Web API 部分を作成できます。 クライアント側では、そのデータを表す "ビュー モデル" を作成し、ビュー モデルは Knockout を使用して HTML にバインドします。 これにより、ビュー モデルを変更せずに HTML を簡単に変更できます。 (少し後で Knockout を見ていきます)

モデル

Visual Studio プロジェクトの Models フォルダーには、サーバー側で使用されるモデルが含まれています。 (クライアント側にもモデルがあります。モデルにアクセスします。)

Screenshot that shows the Models folder open.

TodoItem、TodoList

これらは Entity Framework Code First のデータベース モデルです。 これらのモデルには、互いを指すプロパティがあることに注意してください。 ToDoListには ToDoItems のコレクションが含まれており、各 ToDoItem に親の ToDoList への参照があります。 これらのプロパティはナビゲーション プロパティと呼ばれ、To Do リストとその To Do 項目の一対多リレーションシップを表します。

また、ToDoItem クラスは [ForeignKey] 属性を使用して、ToDoListIdToDoList テーブルの外部キーであることを指定します。 これは、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 のユーザー プロファイルのスキーマを定義します。 (この場合、情報はユーザー ID とユーザー名のみです。)このファイル内の他のモデル クラスは、ユーザー登録フォームとログイン フォームを作成するために使用されます。

Entity Framework

SPA テンプレートは EF Code First を使用します。 Code First 開発では、最初にコードでモデルを定義し、次に EF がモデルを使用して、データベースを作成します。 既存のデータベース (Database First) で EF を使用することもできます。

Models フォルダー内の TodoItemContext クラスは、DbContext から派生します。 このクラスは、モデルと EF の間に "接着" を提供します。 TodoItemContextToDoItem コレクションと 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 を使用して ToDoList インスタンスと ToDoItem インスタンスに対する CRUD 操作をを有効にします。 コントローラーは、ソリューションの Controllers フォルダーにあります。

Screenshot that shows the Controllers folder open. To do Controller dot c s and To do List Controller dot c s are both circled in red.

  • TodoController: To Do 項目の HTTP 要求を処理します
  • TodoListController: To Do リストの HTTP 要求を処理します。

Web API はコントローラー名への URI パスと一致するため、これらの名前は重要です。 (Web API が HTTP 要求をコントローラーにルーティングする方法については、「ASP.NET Web API でのルーティング」を参照。)

ToDoListController クラスを見てみましょう。 これには、1 つのデータ メンバーが含まれています。

private TodoItemContext db = new TodoItemContext();

前に説明したように、TodoItemContext は EF との通信に使用されます。 コントローラーのメソッドは CRUD 操作を実装します。 Web API は、クライアントからの HTTP 要求を次のようにコントローラー メソッドにマップします。

HTTP 要求 コントローラー メソッド 説明
GET /api/todo GetTodoLists To Do リストのコレクションを取得します。
GET /api/todo/id GetTodoList ID で To Do リストを取得します。
PUT /api/todo/id PutTodoList To Do リストを更新します。
POST /api/todo PostTodoList 新しい To Do リストを作成します。
DELETE /api/todo/id DeleteTodoList TODO リストを削除します。

一部の操作の URI には、ID 値のプレースホルダーが含まれていることに注意してください。 たとえば、ID が 42 の to-list を削除する場合、URI は /api/todo/42 です。

CRUD 操作に Web API を使用する方法の詳細については、「CRUD 操作をサポートする Web API の作成」を参照してください。 このコントローラーのコードはかなり簡単です。 興味深い点を次に示します。

  • GetTodoLists メソッドは LINQ クエリを使用して、ログインしているユーザーの ID で結果をフィルター処理します。 そうすることで、ユーザーには自分に属するデータのみが表示されます。 また、ToDoList インスタンスを TodoListDto インスタンスに変換するために Select ステートメントが使用されていることに注意してください。
  • PUT メソッドと POST メソッドは、データベースを変更する前にモデルの状態をチェックします。 ModelState.IsValid が false の場合、これらのメソッドは HTTP 400 Bad Request を返します。 Web API でのモデル検証の詳細については、「モデルの検証」を参照してください。
  • コントローラー クラスも [Authorize] 属性で装飾されます。 この属性は、HTTP 要求が認証されているかどうかを確認します。 要求が認証されていない場合、クライアントは HTTP 401 Unauthorized を受け取ります。 認証の詳細については、「ASP.NET Web API の認証と承認」を参照してください。

TodoController クラスは TodoListController とよく似ています。 最大の違いは、クライアントが To Do 項目を各 To Do リストと共に取得するため、GET メソッドを定義しない点です。

MVC コントローラーとビュー

MVC コントローラーは、ソリューションの Controllers フォルダーにも配置されます。 HomeController は、アプリケーションのメイン HTML をレンダリングします。 ホーム コントローラーのビューは、Views/Home/Index.cshtml で定義されています。 ホーム ビューは、ユーザーがログインしているかどうかに応じて、異なるコンテンツをレンダリングします。

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

ユーザーがログインすると、メイン UI が表示されます。 それ以外の場合は、ログイン パネルが表示されます。 この条件付きレンダリングはサーバー側で行われることに注意してください。 クライアント側で機密性の高いコンテンツを非表示にしないでください。HTTP 応答で送信したものは、生の HTTP メッセージを見ているユーザーに表示されます。

クライアント側の JavaScript と Knockout.js

次に、アプリケーションのサーバー側から注目をクライアントに移しましょう。 SPA テンプレートでは、jQuery と Knockout.js の組み合わせを使用して、スムーズな対話型の UI を作成します。 Knockout.js は、HTML をデータに簡単にバインドできる JavaScript ライブラリです。 Knockout.js では、"Model-View-ViewModel" というパターンが使用されます。

  • モデルはドメイン データ (ToDo リストと ToDo 項目) です。
  • ビューは HTML ドキュメントです。
  • ビュー モデルは、モデル データを保持する JavaScript オブジェクトです。 ビュー モデルは、UI のコード抽象化です。 これには、HTML 表現に関する知識がありません。 代わりに、"ToDo 項目のリスト" などのビューの抽象的な機能を表します。

ビューは、ビュー モデルにデータ バインドされます。 ビュー モデルへの更新は、ビューに自動的に反映されます。 バインドは他の方向にも機能します。 DOM 内のイベント (クリックなど) は、AJAX 呼び出しをトリガーするビュー モデル上の関数にデータバインドされます。

SPA テンプレートは、クライアント側の JavaScript を 3 つのレイヤーに整理します。

  • todo.datacontext.js: AJAX 要求を送信します。
  • todo.model.js: モデルを定義します。
  • todo.viewmodel.js: ビュー モデルを定義します。

Diagram that shows an arrow going from Knockout dot j s to View Model to Models to Data Context. The arrow between Knockout dot j s and View Model is labeled Data Binding and points to both items.

これらのスクリプト ファイルは、ソリューションの Scripts/app フォルダーにあります。

Screenshot that shows the subfolder labeled app open.

todo.datacontext は、Web API コントローラーに対するすべての AJAX 呼び出しを処理します。 (ログインの AJAX 呼び出しは別の場所で、ajaxlogin.js 内で定義されています)

todo.model.js は、To Do リストのクライアント側 (ブラウザー) モデルを定義します。 todoItem と todoList の 2 つのモデル クラスがあります。

モデル クラスのプロパティの多くは、"ko.observable" 型です。 observable では、Knockout の効力が最も発揮されます。 Knockout のドキュメントによると、observable は、"変更についてサブスクライバーに通知できる JavaScript オブジェクト" です。observable の値が変更されると、Knockout は、それらの observable にバインドされている HTML 要素を更新します。 たとえば、todoItem には、title プロパティと isDone プロパティに対する observable があります。

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

また、コードで observable をサブスクライブすることもできます。 たとえば、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 の監視可能な配列が含まれています。 ビュー モデルの次のコードは、バインドを適用するように Knockout に指示します。

ko.applyBindings(window.todoApp.todoListViewModel);

HTML とデータ バインディング

ページのメイン HTML は、Views/Home/Index.cshtml で定義されています。 データ バインディングを使用しているため、HTML は実際にレンダリングされるもののテンプレートにすぎません。 Knockout は "宣言型" バインディングを使用します。 ページ要素をデータにバインドするには、要素に "data-bind" 属性を追加します。 Knockout ドキュメントから取得した非常に簡単な例を次に示します。

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

この例では、Knockout は <span> 要素の内容を値 myItems.count() で更新します。 この値が変更されるたびに、Knockout によってドキュメントが更新されます。

Knockout には、さまざまなバインディングの種類が用意されています。 SPA テンプレートで使用されるバインディングの一部を次に示します。

  • foreach: ループを反復処理し、リスト内の各項目に同じマークアップを適用できます。 これは、To Do リストと To Do 項目をレンダリングするために使用されます。 foreach 内では、バインドがリストの要素に適用されます。
  • visible: 表示/非表示を切り替えるために使用します。 コレクションが空の場合はマークアップを非表示にするか、エラー メッセージを表示します。
  • value: フォーム値を設定するために使用されます。
  • click: クリック イベントをビュー モデルの関数にバインドします。

CSRF 対策保護

クロスサイト リクエスト フォージェリ (CSRF) は、悪意のあるサイトが、ユーザーが現在ログインしている脆弱なサイトに要求を送信する攻撃です。 CSRF 攻撃を防ぐために、ASP.NET MVC は偽造防止トークン (要求検証トークンとも呼ばれます) を使用します。 これは、サーバーが、ランダムに生成されたトークンを Web ページに配置するというものです。 クライアントがサーバーにデータを送信するときは、要求メッセージにこの値を含める必要があります。

同じ配信元ポリシーが原因で、悪意のあるページがユーザーのトークンを読み取ることができないため、偽造防止トークンはうまく機能します。 (同じ配信元ポリシーを使用すると、2 つの異なるサイトでホストされているドキュメントが互いのコンテンツにアクセスできなくなります。)

ASP.NET MVC は、AntiForgery クラスと [ValidateAntiForgeryToken] 属性を使用して、偽造防止トークンの組み込みサポートを提供します。 現時点では、この機能は Web API には組み込まれていません。 ただし、SPA テンプレートには Web API 用のカスタム実装が含まれています。 このコードは、ソリューションの Filters フォルダーにある ValidateHttpAntiForgeryTokenAttribute クラスで定義されています。 Web API での CSRF 対策の詳細については、「クロスサイト リクエスト フォージェリ (CSRF) 攻撃の防止」を参照してください。

まとめ

SPA テンプレートは、最新の対話型 Web アプリケーションの作成をすぐに開始できるように設計されています。 これは、Knockout.js ライブラリを使用して、プレゼンテーション (HTML マークアップ) をデータおよびアプリケーション ロジックから分離します。 ただし、SPA の作成に使用できる JavaScript ライブラリは Knockout だけではありません。 その他のオプションを調べる場合は、コミュニティで作成された SPA テンプレートをご確認ください。