そんなの分かってたことなんだよね。最適化なんてしてないし。この辺もちゃんと手をつけようと考えますわね。Blogもう、答え出ちゃってる感あるけど、まずはこのサイトを参考に。いうか、もうそのまま。Cacheに関してはここのクラスを使わずOutputCacheフィルターを使います。テスト用にプロジェクトを作成。スタイルシートのStylesheet1.css、Stylesheet2.css、Stylesheet3.cssと、JavaScriptのJScript1.js、JScript2.js、JScript3.jsをContentの中に作成(中身は適当で)。Helpersフォルダをルートに作って、その中にCompressAttribute.csとCompressHelper.csをそれぞれ“クラス”テンプレートで作成。↑こんな感じになりましょう。CompressAttribute.csの中身は先のページのままコピペ。ヘッダをみて、圧縮ロジックを決めて、Response.Filterにセットするだけ。}}続いて、ちょっと便利に使うためのヘルパー関数群を定義したCompressHelper.cs↓。}}namespaceをSystem.Web.Mvcにしてるのは、ページでImport書くのが面倒だから。HtmlHelperにしてないのはPage.Itemsを使いたいから。そのページで使うJavaScriptやらCSSやらを入れておいてまとめて出力するのに使ってます。Page.Itemsに入れておけば、マスターファイルとページファイルのそれぞれで入れた値をマスターファイル(Site.Masterね)から簡単に取り出せるから。要求内って意味でのコンテキスト)でのデータの受け渡しができる(Session使う必要ないし)。CSSの場合はページ上部にまとめて書いちゃうからPage.Itemsに入れる必要はないんだけど、その辺は好みの問題ってことで。名前をつけて置けば、名前ごとの圧縮ファイルにできると思って、keyを渡すようにします。やって使うかというと、まずはSite.Masterのheadタグの部分を↓こんな感じに変えましょう。%></head>CSSは文字列配列でがっつり渡す。JavaScriptはまだタグのレンダリングしたくないので、Page.Itemsに入れておく。headに書いてるのは定義場所が上の方が見やすいかな、と思ったから。Site.Masterのbody閉じ以降を↓こんな感じで。</body><%=Page.CompressScriptTag("msajax")%><%=Page.CompressScriptTag("myscript")%></html>ここまでで、"msajax"をキーにしたスクリプトは2つ定義してるけど、"myscript"をはどこで入れ点だよ!って思った?それはHome/Index.aspxの中で入れてるんですね~。各ページごとのスクリプトがサイト共通のスクリプトよりも上位に展開されると、悲しい結末が訪れるから、ちゃんとHTMLの最後に展開されるようにしましょう。Home/Index.aspxの最後の部分を↓こんな感じで。%></asp:Content>これを実行すると展開されるHTML(Home/Index)は↓こう。src="/Home/Compress?src=%2fContent%2fjscript1.js%2c%2fContent%2fjscript2.js%2c%2fContent%2fjscript3.js&type=application%2fx-javascript"></script></html>これだけだと、外部ファイルはちゃんと読み込めませんね。HomeコントローラーにCompressアクションを実装してませんでした。}srcパラメータにまとめるファイル名をカンマ区切りで渡すと(渡すときにUrlEncodeしても、自動でUrlDecodeしてくれます)、ファイルを読み込んで1個にまとめて出力するようにしてます。圧縮はCompressAttributeにお任せ。OutputCacheを指定して、パラメータ毎(外部ソース毎)に1時間キャッシュするように指定してみました。CompressとOutputCacheを外してFirebugで見た結果が↓。※CSSのレスポンス※JavaScriptのレスポンス。どっちも圧縮されてないし、Expiresヘッダもないね。CompressとOutputCacheをつけてFirebugで見た結果が↓。※CSSのレスポンス。※JavaScriptのレスポンス。いずれもファイルサイズも小さくなってるし、Expiresヘッダもついてクライアントキャッシュが有効になってます。ここまで来て圧縮なら「IISの静的ファイル圧縮使えば?」という思いが当然のごとく出てくるよね。試しましたよ!ファイルを1つにまとめる機能はないけど、圧縮はできるはずだもんね。なんかうまく出来なかったんですよ。CSSは出来てるのにJSが出来なかった。httpCompressionのstaticTypesの指定が悪いのかなんなのか。開発2008年9月3日水曜日ModelBinderに気をつけねば前のエントリでModelBinderを使って、ActionのパラメータにDBから取ってきたエンティティモデルをそのまま渡す方法を書いちゃったけど、あれはダメでした。DataContextをstaticにもつサンプルだったけど、そうだとしてもいつDisposeされるのかなんて分かんないから、試しに実装してみたらケチョンケチョン...。正しい使い方はこちら↓。いうのを、昨日ガスリー君のブログでも書かれててホッとした。Blogサンプルをチェックしてると、ProductをModelBinderでとってるじゃないかと思えるかもしれないけど、あくまで新規登録時の空エンティティの時にしか使ってないですよね。更新処理の時にはDataContextから取得したProductに対して、ModelUpdateを使って値の書き込み。この方法だとですね、更新時にはProductBinderのGetValue走らない。サンプルだから両方乗せてるんだろうけど(ModelUpdateとModelBinder)、最初はどんな意味が込められてるのか混乱しちゃった。ModelUpdateでセットされるデフォルトのエラーメッセージが気に入らなかったらどこで書き換えればいいのかはちょっと分かんなかった。リソースファイルに持ってるのをどうすればいいんだろか。IRuleEntityを実装。SQLのエンティティクラスに対して直接入力しないよね。一つ間にViewDataクラスをはさんで、ViewDataにDBから読み込んだ値を入れてFormに表示。更新の時にViewDataに読み込んだ後、エンティティクラスに値をマッピングしていって更新。そんな流れになると思うので、入力検証のサンプルが↓これ。5このサンプルではViewDataに直接値を入れてるけど、ViewDataのクラスを用意してViewPage<UserViewData>でレンダリングをView(model)にするんだと、↓こんな感じになるんじゃないかと思いますがどうですかね。すれば個々に入力値を取得しなくてもviewDataに埋め込みますわね。それだと入力検証できませんわね。全部stringだし。UserViewDataクラスに検証用のメソッドを追加して、それを呼び出すときにModelStateDictionaryを渡すのが簡単でいいんじゃないかと思います。modelStates.IsValid;}で、アクションでは↓。}なんかヘンテコなコード...。※ModelUpdateも中でModelStateDictionaryにエラー値を入れてくれます。キャストできないとか。※Prefixをつけた場合、今までは最後に'.'(ドット)を自分でつけなきゃいけなかったのに、自動でつくようになってちょっと涙目...。気がつくのに時間かかった。ちなみにUpdateModelのキー名はmodelに持ってる項目だけにすべし。Modelの項目を指定して、Formにない場合はエラーにならないけど、その逆はエラー(FormにあってModelにない)になるので気をつけよう。ModelState情報はうまく利用すれば、エラーフィールドを強調できる(class属性にinput-validation-errorが自動でつく)ので超便利です。RenderPartial時にViewDataDictionaryを渡さないと(ViewData.Modelだけだとダメ)ユーザーコントロールで取得できないから、入力項目を持つユーザーコントロールのRenderPartial時には問答無用でViewDataも渡すようにするのが吉!その他気になったところ。Blog便利にはなるよね。必須にしなくてもいいじゃないかと、思ってしまうんですよ。LINQで取り出した、ソースの最初の行に空(ここでいうoptionLabel)のレコードを連結させるコードを書いてたから、それがなくなるのはいいんだけど。ちなみにotionLabelに空文字""を指定すると何も起きないからレンダリング結果は今まで通り。何に使うのかサッパリわからなかったけど、こうやって使うんだね。CookieとForm(HIDDEN)POSTの値を比較して有効なリクエストか判定。ForumsFileResultの簡単な使い方。FileResultはResponse.TransmitFileとかで結果を直接返さずにActionResultとして返すことで、テストしやすくなるよね。AntiForgeryToken/FileResultともにMicrosoft.Web.Mvcに入ってるデス。MVC2008年9月1日月曜日ActionNameとAcceptVerbsやっと意味がわかりました。Haackさん(なんかインタビュービデオを見たんだけど、どう見てもモルダー...。ホントはFBIなんじゃん?)とこで細かく書かれてるんだけど、なんていうかさ、英語じゃん?でも、くじけるわけにはいかねっす。作ってるプロダクトがまったく動かないからね。今まで(Preview4)は、ControllerやActionの前後でなんかしたかったり、処理をざくっと注入するときはActionFilterAttributeクラスを派生したものを使ってました。Preview5でも同じクラスはあるんですよ。同じ名前のクラスなのにコロッとかわっててね。変わってるかというと、ActionFilterはもう前後に処理をはさむだけで、その中で別のActionを呼んだり、Actionそのものの実行をキャンセルするのは止めないか的な。いや、filterContextにはResultプロパティがついてて、そこに結果を入れてしまえばいいんだけど。}}こんな感じでStopAttributeをActionFilterAttributeから派生して作っておいて、Home/Indexアクションにセットする。}ばっちり中断して、違う結果を返してますね。別にこれがしたいわけじゃないんですよ。RESTfulAttributeっていうのをずいぶん前に書いた(進化の過程をウキウキウォッチング)んですよ。要は、GETの時に呼び出すActionとPOST/PUT/DELETEの時のActionを勝手に切り替えてくれるってもんです。Methodじゃなくて、そっちをみて判断するようにしたものです(Railsっぽいよね!)。Methodに合わせたActionを代わりに実効(ActionInvoke)して、その結果を返す必要があります。filterContext.Resultに変わりになるActionの実行結果を入れてしまえばいいってことになるんだけどさ。それも正解(上記サンプルのように実装できるし)。設計思想はそうじゃないっぽい。filterContext.Cancelがないし。そこで出てきたのがActionNameAttributeとAcceptVerbsAttribute。ActionNameAttributeは、単純にAction名を別名に置き換えるもので、AcceptVerbsAttributeはHttpMethodを見て、実行可能な場合のみ(GETだけとかPOSTだけとか)Actionを実行するもの。それぞれ派生元のクラスがさ、ActionFilterAttributeじゃないんですよ。ActionNameはAttributeクラス。AcceptVerbsはActionSelectionAttributeクラス。ActionSelectionAttributeクラスっていうのが今までなかったもので、今回追加されたんですね。methodInfo);たったこれだけ。このIsValidForRequestは何に使うのかというと、Actionの実行を許す時はtrueを返し、実行させたくないときはfalseを返す。いうクラスが今回追加されてきたってことはですね、Actionの実行そのものと、Actionの実行を許可するかどうかは別々に実装すべきなんじゃないですかっていう設計思想なんじゃないかと。いや、モルダーPhilさんがどう思ってるのかは知らないけど。※AuthorizeAttributeとかは別にActionSelectionAttribute派生じゃないから言い切れるわけじゃないっぺ!なので、そういうことならそっちに合わせた方がカッコイイんじゃないかと。作ってみたのがRESTfulVerbs。}}どうやって使うかというと↓。ActionNameAttributeとのコンボです。単純なんだけど、こういう形にしちゃうと既存コードの変更箇所が凄い多くなるのが痛い...。MVCでRESTful)に思ったんだけど、今回みたいな実装にするっていうことはですよ、リクエスト毎にグルグルとActionNameで指定したActionを探すことになるよね、きっと。それが嫌だしちょっとカッコ悪いと思ったから2個目の実装にしたのに。そんな負荷は微々たるもんだから気にするんじゃないってことかな?ちなみにfilterContext.ResultにActionの実行結果を入れる版のRESTfulAttributeは↓こんな感じの変更です。}※こっちのほうが修正少なくて楽...。早くソースが公開されないかな~。今回実装されてるFilter一覧を見てたらすごく気になるもの発見。この2つは基本クラス。CustomModelBinderAttribute();で、今までもあったものを含めて使えるフィルターがこれ。ActionFilterAttribute最後に気になるフィルターがこれ。methodInfo);何に使うんだろ...。