2009年11月20日金曜日

Html.ActionとHtml.RenderAction

なんともエントリを書かなすぎでした。いろいろ遊んだりしてたんす。シバトラ読みふけったり。

Html.RenderAction and Html.Action

PDC09が盛り上がりまくって、Feedの確認が全然追いつかないなか、ASP.NET MVC 2もベータが公開。早速Philさんが面白い機能の紹介をしてくれてたので少し確認。確認してタンブラのほうで軽くコメント書いとこうと思ってたけど、内容が面白すぎたので、こっちに書いてみるっす。

まずはプロジェクトを作ろうとVS立ち上げたけど、なんかちゃんとできない。なんで~!と思ったらベータインストールしてなかった...。ソースだけダウンロードして見てるだけな楽しみ方もありですよね。無しだな。Preview2をアンインストールしてからベータ入れて見るものの、V1の時と同じように日本語環境にはちゃんと入ってくれなかった。いきなりギャフンですね。プロジェクトテンプレートを1041にコピーしてdevenv /installvatemplates。

準備も出来たところで、サブジェクトの機能について簡単に説明。

Html.Actionはアクションの実行結果をMvcHtmlStringにして返してくれるもので、Html.RenderActionは同じくアクションの実行をしてくれるけど、こっちは文字列としてではなく現在のレスポンスストリームに結果を書き出してくれるものです。

もともとFuturesに入ってた機能ですけど、出世してリリースアセンブリに含まれるようになりました。パッと見、Html.ActionLinkと勘違いしそうなヘルパーだけど、中身は全然違う物です。ソースではChildActionExtensionsにまとまってるので興味のある方はぜひ。

で、中では何をしてるのかというと、Server.Executeです(なのでPageの派生クラス使ってる)。IHttpHandlerとしてラッピングしてProcessRequestを実行してます。この辺の仕組みはFuturesの頃から変わってないです。コードはカッコ良くリファクタリングされてますが。ちなみに今回のリリースには非同期アクション実行も含まれてるので、IHttpAsyncHandlerにももちろん対応してます。ここら辺の実装がHttpHandlerUtil.WrapForServerExecuteですね。500エラー以外のHttpExceptionをServer.Executeが伝播してくれないらしく、がんばった感じが見て取れます。

    public ActionResult Partial(string id)
    {
      return Content("Partial result" + id);
    }

↑こんなアクションメソッドをHomeControllerに定義しておき、Home/Index.aspxで以下のように書いておく。

    <div><% = Html.Action("Partial",new{id=" cool!"}) %></div>
    <div><% Html.RenderAction("Partial", new {id = " so nice!"}); %></div>

そうすると出力されるのは↓こんな感じです。

action

でもって、このPartialアクションメソッドはそのまま"/home/partial"ってやっても呼び出せてしまいますね。Ajaxでの部分更新に使うならいいけど、そうじゃなくHtml.Action/RenderActionでしか使わないならChildActionOnly属性をアクションメソッドに指定しておきましょう。

そしたら↓こう。

action2

ブラウザからは直接アクセス出来なくなります。なんで~!と気になったらソースを確認。ChildActionExtensions.CreateRouteDataでServer.Execute対象のRouteDataを生成してるんですが、その時に現在のViewContextをRoute.DataTokensに入れてます。Areaでnamespaceを渡して違うnamespaceのControllerをRoute登録するのと同じやり方ですね。そのDataTokensが存在してるのかどうかをChildActionOnlyAttributeがチェックして存在してなければ、実行出来ないようにする仕組みです。素敵だね。

で、Viewのコードを見てみるとHtml.Actionは<%= … %>で実行してるのがわかるでしょうか?これはつまり文字列をそのままViewの一部に埋め込んで最後にまとめてレスポンスですよね。これに対してHtml.RenderActionは<% …; %>コード実行の書き方です。なので、レスポンスストリームに書き出すものです。なので、Philさんが書いてる通り、部分的なCacheを有効にしたいときはRenderActionを使う必要があります。Response.WriteSubstitutionを使った出力はFuturesに別途用意されてるね。Html.Substitute。

ね?面白いでしょ?