NerdDinnerチュートリアルNerdDinnerステップ4:コントローラとビューScott Guthrie 著/Chica 訳2009/07/17 |
|
|
[これは無償の"NerdDinner"アプリケーション・チュートリアルのステップ4で、ASP.NET MVCを使用して、小さいながらも完全なWebアプリケーションを構築する手順を紹介しています。]
従来のWebフレームワーク(クラシックASP、PHP、ASP.NET Webフォームなど)では、移動先のURLは通常、ディスク上のファイルにマッピングされています。例えば、“/Products.aspx”または“/Products.php”などのURLへのリクエストがあると、“Products.aspx”または“Products.php”ファイルが処理することになると思います。
WebベースのMVCフレームワークでは、少し違う方法でURLをサーバのコードにマッピングします。移動先のURLをファイルにマッピングする代わりに、クラスのメソッドにURLをマッピングします。これらのクラスは“コントローラ”と呼ばれ、受信したHTTP要求の処理、ユーザーの入力処理、データの取得と保存、クライアントへ返信する応答の決定(HTML表示、ファイルのダウンロード、別のURLへのリダイレクトなど)を担当します。
すでにNerdDinnerアプリケーションの基本モデルが構築できているので、次の手順は、サイト上で夕食会データ一覧/詳細の操作をユーザーに提供するために利用できるコントローラをアプリケーションに追加します。
DinnersControllerコントローラの追加
まず、Webプロジェクトにある“Controllers”フォルダを右クリックして[追加]−[Controller]メニュー・コマンドを選択します(または、[Ctrl]+[M]、[Ctrl]+[C]をタイプすると、このコマンドを実行できます)。
図1 |
これにより“Add Controller”ダイアログがポップアップします。
図2 |
この新しいコントローラを“DinnersController”という名前にし、“Add”ボタンをクリックします。そうすると、Visual Studioは、DinnersController.csファイルを\Controllersディレクトリ配下に追加します。
図3 |
同時に、DinnersControllerクラスがコード・エディタ内で開きます。
IndexとDetailsアクション・メソッドをDinnersControllerクラスに追加
このアプリケーションでは、訪問者が未開催の夕食会の一覧を見ることができ、一覧にあるすべての夕食会は、クリックしてその詳細が見られるようにしたいと思っています。このために、アプリケーションでは次のURLを公開します。
| ||||||
表 | ||||||
DinnersControllerクラスに以下のような2つのpublicの“アクション・メソッド”を追加することで、これらURLの初回の実装を公開します。
|
それではNerdDinnerアプリケーションを実行して、ブラウザからそれらを呼び出してみましょう。“/Dinners/”のURLを入力すると、Indexメソッドが実行され、以下のような応答が返ってきます。
図4 |
“/Dinners/Details/2”のURLを入力すると、Detailsメソッドが実行され、以下のような応答が返ってきます。
図5 |
ASP.NET MVCはどのようにしてDinnersControllerクラスを作成し、それらのメソッドを呼び出すのだろうと疑問に持たれるかもしれません。これを理解するために、ルーティングの動作を簡単に見てみましょう。
ASP.NET MVCルーティングの理解
ASP.NET MVCには強力なURLルーティング・エンジンがあり、どのようにURLがコントローラ・クラスにマッピングされるのかを非常に柔軟に制御できます。これにより、ASP.NET MVCがどのコントローラ・クラスを作成し、その上でどのメソッドを実行するのかという選択を完全にカスタマイズでき、また、URL/クエリ文字列から変数を自動的にパースし、パラメータ引数としてメソッドへ引き渡す方法をさまざまな方法で構成できます。SEO(検索エンジン最適化)に対して完全に最適化する柔軟性を提供でき、アプリケーションでどのようなURL構造でも公開できます。
新しいASP.NET MVCプロジェクトにはデフォルトで、登録済みの事前に構成されたURLルーティング・ルール一式が付いてきます。これにより、明示的に何かを構成しなくてもアプリケーションを簡単に開始できます。デフォルトのルーティング・ルールの登録はプロジェクトの“Application”クラスの中にあり、プロジェクトのルートにある“Global.asax”ファイルをダブルクリックすれば開けます。
図6 |
デフォルトのASP.NET MVCルーティング・ルールはこのクラスの“RegisterRoutes”メソッド内に登録されています。
|
上記の“routes.MapRoute”メソッド呼び出しは、受信URLを、“/{controller}/{action}/{id}”というURLフォーマットを使用するコントローラ・クラスにマッピングするデフォルトのルーティング・ルールを登録しており、“controller”はインスタンス化するコントローラ・クラスの名前、“action”はその上で実行するpublicメソッド名、“id”はそのメソッドに引数として引き渡すことができる、URLに埋め込まれたオプションの引数です。“MapRoute”のメソッド呼び出しへ渡される3つ目の引数(Controller ="Home", Action="Index", Id="")は、URLにそれらがなかった場合に、controller/action/idの値に対して使用される一連のデフォルト値です。
以下はデフォルトの“/{controllers}/{action}/{id}”ルーティング・ルールを使用してマッピングされるURLの多様さを示した表です。
| ||||||||||||||||||||||||||||
表 | ||||||||||||||||||||||||||||
最後の3つの行では、デフォルト値の(Controller = Home, Action = Index, Id = "")が使用されていることを示しています。“Index”メソッドは、特定されなかった場合のデフォルトのアクション名として登録されているため、“/Dinners”や“/Home”のURLは、Indexアクション・メソッドをコントローラ・クラス上で実行します。“Home”コントローラは特定されなかった場合のデフォルトのコントローラとして登録されているため、“/”URLはHomeControllerを作成し、そのうえでIndexアクション・メソッドを実行します。
もしこれらのデフォルトURLルーティング・ルールが嫌な場合、幸いにして、簡単に変えることができます。上記のRegisterRoutesメソッド内で編集するだけです。しかしNerdDinnerアプリケーションでは、デフォルトのURLルーティング・ルールを変更せずに、そのまま使用していきます。
DinnersControllerからDinnerRepositoryを使用
DinnersControllerのIndexとDetailsアクション・メソッドの現在の実装を、モデルが使用する実装に置き換えましょう。
この動作を実装するために、先に構築したDinnerRepositoryクラスを使用します。まず“using”文を追加して、“NerdDinner.Models”名前空間を参照するようにし、DinnerRepositoryのインスタンスをDinnerControllerクラス上のフィールドとして宣言します。
本章の後半では、“依存関係の挿入(Dependency Injection)”の概念を紹介し、コントローラで、よりうまく単体テストできるようになるDinnerRepositoryへの参照を取得する別の方法を示します。ですが現時点では、単純に以下のようにインラインでDinnerRepositoryのインスタンスを作成します。
|
取得したデータモデル・オブジェクトを使用して、HTMLのレスポンスを生成する用意ができました。
コントローラとともにビューを使用
HTMLを組み立てるアクション・メソッド内でコードを書き、Response.Writeヘルパー・メソッドを使用してクライアントに応答することは可能ですが、この方法はすぐに非常に扱いにくくなります。よりよい方法は、DinnersControllerアクション・メソッド内でアプリケーションとデータ・ロジックだけを実行させ、HTML表示の出力を担う別の“view”テンプレートに、HTMLレスポンスの描画に必要なデータを引き渡します。すぐに見ることになりますが、“view”テンプレートは通常、HTMLタグと、埋め込まれた描画コードとの組み合わせになっています。
ビューの描画からコントローラのロジックを別離すると、大きな利点がいくつか得られます。特に、アプリケーション・コードと、UIのフォーマット/描画コードの間で、明確に“関心の分離”が施行されるようにできます。これにより、UI描画ロジックから分離させたアプリケーション・ロジックの単体テストが非常に簡単になります。後でアプリケーション・コードを変更することなく、UI描画テンプレートを修正することも簡単になります。そして、開発者とデザイナのプロジェクト上での協業が簡単になります。
2つのアクション・メソッドのメソッド・シグネチャで、戻り値の型が“void”から“ActionResult”になるように変更すれば、ビュー・テンプレートを使用してHTML UIレスポンスを返すDinnersControllerに更新できます。そうすれば、以下のように、“ViewResult”オブジェクトを返す、Controller基本クラスのViewヘルパー・メソッドを呼び出すことができます。
|
上記で使用したViewヘルパー・メソッドのシグネチャは、以下のようになります。
図7 |
Viewヘルパー・メソッドの最初のパラメータは、HTMLレスポンスを描画するのに使用したいビュー・テンプレートの名前です。2つ目のパラメータは、HTMLレスポンスを描画するためにビュー・テンプレートが必要となるデータが含まれたモデル・オブジェクトです。
Indexアクション・メソッド内では、Viewヘルパー・メソッドを呼び出し、“Index”ビュー・テンプレートを使用して夕食会のHTMLによる一覧を描画したいことを示しています。ビュー・テンプレートに一連のDinnerオブジェクトを引き渡し、そこから一覧を生成するようにしています。
|
Detailsアクション・メソッド内では、URLで提供されているidを使用してDinnerオブジェクトを取得しようとしています。もし有効なDinnerが見つかれば、“Details”ビュー・テンプレートを使用して、取得したDinnerオブジェクトを描画したいことを示したViewヘルパー・メソッドを呼び出します。もし無効なDinnerが要求された場合、“NotFound”ビュー・テンプレート(と、テンプレート名だけを取るViewヘルパー・メソッドのオーバーロードされたバージョン)を使用して、そのDinnerが存在しないことを示す分かりやすいエラー・メッセージを描画します。
|
では“NotFound”、“Details”、“Index”ビュー・テンプレートを実装しましょう。
“NotFound”ビュー・テンプレートの実装
まず“NotFound”ビュー・テンプレートの実装から始めます。これは要求された夕食会が見つからないことを示す、ユーザーフレンドリなエラー・メッセージを表示します。
コントローラのアクション・メソッド内にテキスト・カーソルを置き、右クリックして、“Add View”メニュー・コマンドを選択して、新しいビュー・テンプレートを作成します([Ctrl]+[M]、[Ctrl]+[V]をタイプすることでも、このコマンドを実行できます)。
図8 |
これにより“Add View”ダイアログが以下のようにポップアップします。デフォルトで、ダイアログが起動されたときにカーソルのあるアクション・メソッド名に合致するようにビューの名前が作成されます(今回は“Details”)。まず“NotFound”テンプレートを実装したいので、このビュー名を上書きして、“NotFound”を設定します。
図9 |
“Add”ボタンをクリックすると、Visual Studioは、“\Views\Dinners”ディレクトリ内(ディレクトリが存在しない場合は、これも新規作成します)に新しい“NotFound.aspx”ビュー・テンプレートを作成します。
図10 |
新しい“NotFound.aspx”ビュー・テンプレートもコード・エディタで開かれます。
図11 |
ビュー・テンプレートはデフォルトで2つの“コンテンツ・エリア”を持ち、そこへコンテンツとコードを追加できます。1つ目では、送信するHTMLページの“タイトル”をカスタマイズできます。2つ目では、送信するHTMLページの“メイン・コンテンツ”をカスタマイズできます。
“NotFound”ビュー・テンプレートを実装するために、いくつかの基本的なコンテンツを追加します。
|
そして、ブラウザ内で試すことができます。これを行うために、“/Dinners/Details/9999”というURLを要求しましょう。これはデータベースに現在存在しない夕食会を参照しており、“NotFound”ビュー・テンプレートを描画するDinnersController.Detailsアクション・メソッドを実行します。
図12 |
上記のスクリーンショットで1つ気付かれると思いますが、基本的なビュー・テンプレートには、スクリーン上でメイン・コンテンツを囲む数多くのHTMLを継承しています。これはビュー・テンプレートが“マスターページ”テンプレートを使用していて、サイト上のすべてのビューにわたり、一律のレイアウトを適用できるようになっているからです。マスターページの動作についての詳細は、本チュートリアルの後の方で話します。
“Details”ビュー・テンプレートの実装
では“Details”ビュー・テンプレートを実装しましょう。これは1つのDinnerモデルに対してHTMLを生成します。
これを行うには、Detailsアクション・メソッド内にテキスト・カーソルを置いて、右クリックして“Add View”メニュー・コマンドを選択します(または[Ctrl]+[M]、[Ctrl]+[V]をタイプ)。
図13 |
これにより、“Add View”ダイアログがポップアップします。デフォルトのビュー名(“Details”)のままにしておきます。またダイアログでは、“Create a strongly-typed View(強く型付けされたビューの作成)”チェック・ボックスも選択します。このビューにDinnerオブジェクト(この型に完全に適した名前は、“NerdDinner.Models.Dinner”です)を引き渡します。
図14 |
“空のビュー”の作成を選択した以前のテンプレートと違い、今回は“Details”テンプレートを使用して、ビューを自動的に“スキャフォールド”するように選択します。上記のダイアログで“View content”ドロップダウンを変更して、これを指示できます。
“スキャフォールディング”は、そこへ引き渡しているDinnerオブジェクトに基づいた詳細ビュー・テンプレートの初回実装を生成します。これにより、今回のビュー・テンプレート実装ですぐに開始できる簡単な方法が提供されます。
“Add”ボタンをクリックすると、Visual Studioは“\Views\Dinners”ディレクトリ内に新しい“Details.aspx”ビュー・テンプレートを作成します。
図15 |
また、コード・エディタでその新しい“Details.aspx”ビュー・テンプレートが開かれます。それには、Dinnerモデルに基づいた詳細ビューの初回スキャフォールド実装が含まれています。スキャフォールド・エンジンは.NETリフレクション使って、そのクラスに引き渡された公開されているpublicプロパティを調べ、見つかった各型に基づいて適切なコンテンツを追加します。
|
“/Dinners/Details/1”URLを要求すれば、この“details”スキャフォールド実装がブラウザでどのように見えるかを確認できます。このURLでは、データベースを最初に作成したときに、手動で追加した夕食会の1つを表示します。
図16 |
スキャフォールドにより素早く立ち上げることができ、Details.aspxビューの初回実装を得ることができます。その後、満足のいくUIにするため、微調整などのカスタマイズが行えます。
より詳細にDetails.aspxテンプレートを見ると、静的なHTMLと、埋め込まれた描画コードがあるのが分かります。<% %>コードの固まりは、ビュー・テンプレートが描画する際にコードを実行し、<%= %>コードの固まりは、その中に含まれているコードを実行し、テンプレートの出力ストリームへ結果を描画します。
ビューの中には、強く型付けされた“Model”プロパティを使って、コントローラから引き渡された“Dinner”モデル・オブジェクトにアクセスするコードを書くことができます。Visual Studioは、エディタ内でこの“Model”プロパティにアクセスするときに、コードに対する完全なIntelliSenseを提供します。
図17 |
最終的なDetailsビュー・テンプレートのソースが以下のようになるように、少し微調整しましょう。
|
“/Dinners/Details/1”URLに再度アクセスすると、以下のようになります。
図18 |
“Index”ビュー・テンプレートの実装
では“Index”ビュー・テンプレートの実装をしましょう。これは未開催の夕食会の一覧を生成します。これを行うには、Indexアクション・メソッド内にテキスト・カーソルを置き、右クリックして“Add View”メニュー・コマンドを選択します(または[Ctrl]+[M]、[Ctrl]+[V]をタイプ)。
“Add View”ダイアログでは、ビュー・テンプレートの名前を“Index”のままにして、“Create a strongly-typed View(強く型付けされたビューを作成)”チェック・ボックスを選択します。今回は“List”ビュー・テンプレートの自動生成を選択し、そのビューへ引き渡されるモデルの型として“NerdDinner.Models.Dinner”を選択します(これは、“List”を作成すると指示していたため、スキャフォールドによりコントローラからビューへ一連のDinnerオブジェクトを引き渡すものと、Add Viewダイアログでは仮定されます)。
図19 |
“Add”ボタンをクリックすると、Visual Studioは新しい“Index.aspx”ビュー・テンプレートのファイルを“\Views\Dinners”ディレクトリ配下に作成します。そして初回実装を“スキャフォールド”して、ビューへ引き渡す夕食会のHTMLテーブルによる一覧を提供します。
アプリケーションを実行し、“/Dinners/”URLにアクセスしたとき、次のように夕食会の一覧を描画します。
図20 |
上記のテーブルによるソリューションは、夕食会データをグリッドのようなレイアウトで提供します。しかしこれは、夕食会一覧を見る消費者に提供したいものではありません。Index.aspxビュー・テンプレートを更新して、それをデータ列がもっと少ない一覧になるように修正し、以下のようなコードを使用して、テーブルの代わりに<ul>要素で描画するようにします。
|
上記の中で“var”キーワードをforeach文で使用して、モデルの中で各夕食会をループします。C# 3.0にあまりなじみのない人は、“var”を使用するということは、そのdinnerオブジェクトが遅延バインディングされることだと思うかもしれません。しかし、コンパイラは強く型付けされた“Model”プロパティに対する型推論を使用し、ローカルの“dinner”変数をDinner型としてコンパイルします。つまり、コードブロック内で、その完全なIntelliSenseとコンパイル時のチェックが得られるということです。
図21 |
ブラウザで/Dinners URLにして更新ボタンを押すと、更新されたビューは以下のようになります。
図22 |
見栄えは良くなりましたが、まだ完全ではありません。最後のステップは、一覧にある各夕食会をエンドユーザーがクリックして、その詳細を見ることができるようにします。これを実装するには、HTMLハイパーリンク要素を描画して、DinnersController上のDetailsアクション・メソッドへリンクします。
2つの方法のどちらかで、これらのハイパーリンクをIndexビューに生成します。1つは、<a>HTML要素内に<% %>ブロックを埋め込んで、手動でHTMLの<a>要素を以下のように作成します。
図23 |
もう1つのアプローチでは、ASP.NET MVCビルトインの“Html.ActionLink”ヘルパー・メソッドを利用します。これは、コントローラ上の別のアクション・メソッドへリンクするHTMLの<a>要素のプログラム的な作成をサポートします。
|
Html.ActionLinkヘルパー・メソッドの最初のパラメータは、表示するリンクのテキスト(今回の場合、夕食会のタイトル)、2つ目のパラメータは、生成したいリンク先のコントローラのアクション名(今回の場合Detailsメソッド)、3つ目のパラメータは、アクションへ送るパラメータのセット(プロパティの名前/値により匿名型として実装)です。今回の場合、リンクしたい夕食会の“id”パラメータを指定します。またASP.NET MVCのデフォルトのURLルーティング・ルールが“{Controller}/{Action}/{id}”であるため、Html.ActionLinkヘルパー・メソッドは次のような出力を生成します。
|
今回のIndex.aspxビューには、Html.ActionLinkヘルパー・メソッドを使用し、一覧にある各夕食会が適切な詳細URLへリンクするようにします。
|
そして/DinnersのURLをたたくと、夕食会の一覧は以下のようになります。
図24 |
一覧にあるどの夕食会をクリックしても、その詳細が見られるようにナビゲートされます。
図25 |
コンベンション・ベースの名前付けと\Viewsディレクトリ構造
ASP.NET MVCアプリケーションはビュー・テンプレートを解決するとき、デフォルトでコンベンション・ベースのディレクトリの名前付け構造を使用します。これにより、開発者はコントローラ・クラス内からビューを参照するとき、完全に対応したロケーション・パスを持たなくてもよくなります。デフォルトでASP.NET MVCはアプリケーションの下にある\Views\[ControllerName]\ディレクトリ内でそのビュー・テンプレートを探します。
例えば、ここまでDinnersControllerクラスを扱っていますが、これは明示的に3つのビュー・テンプレート(“Index”、“Details”、“NotFound”)を参照しています。ASP.NET MVCはデフォルトで、これらのビューをアプリケーションのルート・ディレクトリの下の\Views\Dinnersディレクトリ内で探します。
図26 |
現在3つコントローラ・クラスがプロジェクト内でどのように存在するか(DinnersController、HomeController、AccountController、最後の2つはプロジェクトが作成された時にデフォルトで追加されたものです)、そして\Viewsディレクトリ内で3つのサブディレクトリ(各コントローラに1つ)がどのように存在するかご確認ください。
HomeとAccountコントローラから参照されるビューは、それぞれ\Views\Homeと\Views\Accountのディレクトリから自動的に、それらのビュー・テンプレートを解決します。\Views\Sharedサブディレクトリはアプリケーション内の複数のコントローラが再利用するビュー・テンプレートの保存方法を提供します。ASP.NET MVCがビュー・テンプレートを解決しようとするとき、まず\Views\[Controller]で特定されたディレクトリ内をチェックして、もしそこにビュー・テンプレートが見つからない場合、\Views\Sharedディレクトリ内を探します。
各ビュー・テンプレートの名前付けに関して、推奨のガイダンスは、描画を発生させるアクション・メソッドと同じ名前をビュー・テンプレートに付けることです。例えば、上記の“Index”アクション・メソッドは“Index”ビューを使ってビューの結果を描画します。そして、“Details”アクション・メソッドは“Details”ビューを使って、その結果を描画します。これにより、どのテンプレートが各アクションに関連付いているのかを簡単に把握できるようになります。
コントローラ上で発生するアクション・メソッドと同じ名前をビュー・テンプレートが持っている場合、開発者は明示的にビュー・テンプレートの名前を指定する必要がありません。代わりに、単純に“View”ヘルパー・メソッドへ(ビュー名を指定せずに)モデル・オブジェクトを引き渡すと、描画のためにディスク上の\Views\[ControllerName]\[ActionName]ビュー・テンプレートを使用したいということをASP.NET MVCが自動的に推測します。
これにより、少しコントローラのコードをクリーンにでき、コード上での名前の重複を避けられます。
|
上記のコードは、サイト上で素晴らしい夕食会の一覧/詳細の体験を実装するために必要なもののすべてです。
次のステップ
これで素晴らしい夕食会の一覧体験が構築できました。
では、CRUD(作成、読み込み、更新、削除)データ・フォームの編集サポートを有効にしましょう。
[注: NerdDinnerアプリケーションの完成版はhttp://nerddinner.codeplex.com/からダウンロードできます。]
「NerdDinnerチュートリアル」 |
- 第2回 簡潔なコーディングのために (2017/7/26)
ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている - 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう - 第1回 明瞭なコーディングのために (2017/7/19)
C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える - Presentation Translator (2017/7/18)
Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
|
|