本連載では、ASP.NETの新たなWebアプリケーション・フレームワークである「ASP.NET MVC 3」を基礎から解説している。前回までは、Entity Framework 4.1(Code First)と連携したモデル開発と検証機能の実装について解説してきた。ここまでの内容で、まずは基本的なデータベース連携アプリケーションの仕組みについては理解できたはずだ。なお、Code Firstについては、なかなかに奥深い世界であるので、現在進行中の連載である「Entity Framework 4.1入門」も併せて参照されたい。
さて、今回からはガラリと目線を転じて、ASP.NET MVC 3から標準で導入されたビュー・エンジン「Razor」について、3回にわたって解説していく。今回扱うのは、Razorの基本文法と、ASP.NET MVC 3で利用できるビュー・ヘルパーについてだ。
ASPXとの比較で理解するRazor文法
実際に利用していけば分かるように、Razorはとても賢いビュー・エンジンであり、開発者の手をできるだけ煩わせないよう、細部にまで配慮(=自動化)が行き届いている。正しい文法を必ずしも意識しなくても、「何となく」書けてしまうのもRazorのよいところだろう。
しかし、それがすなわち正しい文法を理解していなくてもよいということにはならない。思わぬ落とし穴にはまらないためにも、Razorがどのようなルールでコードをパース(解析)しているのかを知ることは重要だ。
そこで本稿前半ではまず、これまで何となく書いてきたRazorの基本的な文法についてまとめていく。なお、サンプルには従来のASPXエンジンによる同義のコードも付与している。すでにASPXエンジンに慣れている諸氏は、ASPXのコードがRazorではどのように書けるのか、という見地でも確認いただきたい。
■インライン式
インライン式は、テンプレートに与えられた式の値をHTMLコードに埋め込むための記法である。Razorの最も基本的な記法で、「@expression」の形式で記述できる。ASPXエンジンでの「<%: expression %>」に相当する記法だ。
|
リスト1 モデル・オブジェクトのIsbnプロパティを出力するコード(上:Razor、下:ASPX) |
ASPXの<%:……%>とは異なり、Razorにはいわゆる“終了のデリミタ”が存在しない点に注目だ。Razorでは、C#、またはVisual Basic(VB)の識別子を認識して、識別子として有効でない文字を検出すると、自動的にそれをコードの終了と見なすわけだ*1。
*1 正確には、識別子として有効な文字に加えて、「[」「(」(これらはC#およびVBのインデクサの始まり)、「.」(ドット演算子)を検出した場合には、継続して以降の文字をコードとして解析しようとする。
|
もっとも、その性質上、Razorでは以下のようなコードは正しく認識できないので要注意だ。以下は、Priceプロパティに1.05を掛けたものを出力することを期待したコードだ。
|
リスト2 Priceプロパティに1.05を掛けたものの出力を期待したコード |
しかし、上の結果は例えば「2800 * 1.05」となってしまう(Priceプロパティが2800の場合)。Razorは、「@Model.Price」の直後の空白が識別子として有効な文字でないため、ここでコードの終了と見なすわけだ(つまり「* 1.05」はただの文字列と見なされる)。
これを避けるためには、Razorの明示的コード・ナゲット「@(expression)」を利用しなければならない*2。式全体を丸カッコでくくることで、コード範囲を明示的に示しているわけだ。つまり、先ほどのコードであれば、以下のように書ける。
*2 これに対して、標準的な「@expression」文法のことを「暗黙的コード・ナゲット」という場合もある。
|
|
リスト3 明示的コード・ナゲット「@(empression)」を利用 |
果たして今度は、「2800 * 1.05」の結果として、「2940」(Priceプロパティが2800の場合)のような演算結果が得られるはずだ。
明示的コード・ナゲットを利用することで、逆に、不明瞭なコードの区切りを明確にすることもできる。例えば、以下のコードはsrc属性として「<Isbnプロパティ>_logo.jpg」のように認識されることを企図したコードであるが、正しく認識されない。
<img src="@Model.Isbn_logo.jpg" />
|
|
リスト4 「<Isbnプロパティ>_logo.jpg」の出力を期待したコード |
「_」がC#/Visual Basicの識別子として有効であるため、Razorは、モデル・オブジェクトのIsbn_logoプロパティにアクセスしようとし、(恐らく存在しないはずなので)例外を発生するわけだ。
このような場合にも、明示的コード・ナゲットを利用して、以下のように記述する必要がある。
<img src="@(Model.Isbn)_logo.jpg" />
|
|
リスト5 明示的にIsbnプロパティを指定 |
■インライン式(エスケープ処理をスキップ)
Razorのインライン式は、式の値をエスケープ処理したうえで出力する。もっとも、出力するコンテンツがすでにエスケープ済みである、もしくは「安全な」HTMLコードであることが分かっている場合、与えられた文字列をそのまま出力したいということもあるだろう。
そのような場合、RazorではHtml.Rawメソッドで明示的に式を修飾する必要がある。ASPXでは、「<%= …… %>」に相当する表現である。
|
リスト6 モデル・オブジェクトのBodyプロパティを出力するコード(上:Razor、下:ASPX) |
Html.Rawメソッドは、以下のようにHtmlStringオブジェクトで代替しても構わない。HtmlStringはHTMLエンコード済みの文字列を表すオブジェクトで、ASP.NETに対して「文字列をエンコードしてはならない」ことを通知するものだ(実は、Html.Rawメソッドも内部的にはHtmlStringオブジェクトを生成して返しているに過ぎない)。
@Html.Raw(new HtmlString(Model.Body))
|
|
リスト7 Html.Rawメソッドは内部的にはHtmlStringオブジェクトを生成 |
■コード・ブロック
コード・ブロックは、(一般的に)出力を伴わない任意のコードを表す記法である。ASPXエンジンの「<% statement %>」に相当する記法で、「@{ statement }」(Visual Basicでは「@Code statement End Code」)の形式で表せる。
@{ var str = "ビュー・エンジン"; }
|
@Code
Dim str = "ビュー・エンジン"
End Code
|
<% var str = "ビュー・エンジン"; %>
|
<% Dim str = "ビュー・エンジン" %>
|
|
リスト8 変数strに値を代入するためのコード・ブロック |
上からRazor(C#)、Razor(Visual Basic)、ASPX(C#)、ASPX(Visual Basic) |
コード・ブロック(C#)の中では、RazorもASPXの場合と同じく、文はセミコロン(;)で終了しなければならない点に要注意。
■「@」文字のエスケープ
Razorのパーサーは賢くできており、予約文字である「@」すらも(多くの場合は)静的コンテンツとしてそのまま利用できる。
例えば、以下のようなメール・アドレス文字列は、Razorの中でも特に意識することなく表現できる。Razorは「@」の前後を見て、(コードの始まりでなく)静的コンテンツの一部であると判断するためだ。
ただし、以下のようなコードは正しく認識できない。
|
リスト10 先頭が「@」であるため、後続の「IT」は識別子と見なされる |
この場合、Razorは「@」が先頭で始まるため、後続の「IT」を識別子と見なすのだ。このため、「名前 'IT' は現在のコンテキスト内に存在しません。」のようなコンパイル・エラーが発生するはずだ。
このようなケースでは、以下のように「@」をエスケープ処理しなければならない。「@」をエスケープするには、「@@」のように記述するだけだ。
|
リスト11 先頭に文字「@」を使用するには「@@」と記述する |
[参考]コンテンツ内部のインライン式に要注意
Razorの解析ルールからすると、以下のようなコードも正しく認識されないので注意されたい。
|
リスト12 意図したように認識されないインライン式 |
文字列途中の「@」は静的コンテンツの一部と見なされてしまうため、そのままインライン式は解析されずにそのままの文字列が出力されてしまうのだ。このようなケースでは、明示的コード・ナゲットを利用して、以下のように記述しなければならない。