2008年5月20日火曜日

ひた隠しに

動くものを作ろうと思うと、やっぱり流行りのスタイルに乗っかりたい。そう思うのは浅はかなことでしょうか。で、ASP.NET MVCでRESTfulっぽく作るならやっぱり、HTTP MethodでActionを自動で振り分けたい。 そう思うのは、いたしかたなし。

画像を操作するResourceControllerがあったとしましょう。 画像を追加したいならResourceControllerにPhotoAddNewとかPhotoCreateなんていうActionを作りたくなるでしょう。でも待って!それじゃRESTfulじゃないよ!

複数形とか単数形は置いといて、とりあえず/Photoに対するアクセスでCRUDを処理したいよね! 特に興味無いですか?あ、そうですか。

まぁ、聞いて下さいよ。 以前のエントリーでRESTfulFilterなんてものを書いてみましたが、ぶっちゃけあれじゃ効率悪し。 送り側にActionFilterAttributeを書く方法だと、毎回リクレクションが必要ジャン? それなら、受け側でHTTP Method毎のAction名を指定するほうがよっぽど効率がいいってものですよ。 その辺踏まえて、以下のようなActionに。

/ResourceController [RESTfulFilter] public void Photo(int? id){...} public void PhotoPost(){...} public void PhotoPut(int id){...} public void PhotoDelete(int id){...} ※ActionResultじゃなくてvoidなのは気にしない。

で、 RESTfulFilterのExecutingでHTTP Method見て、現在のAction名の後ろに自動でInvokeActionする名前を作る(もちろん、属性のパラーメータでHTTP Method毎のAction名をセットできるようにした方が優しい)。 こんな風にしとけば、ActionFilterAttributeは1か所に指定するだけで済むので、効率よしって話です。 が、その他のActionがpublic(じゃないと、InvokeAction出来ない、よね?)なので、直接そのActionにもアクセスできちゃうのが何か気持ち悪し。出来て何が悪いってわけじゃ全然ないんだけども。なんとなく。直接のアクセスは拒否したいな、なんて思ったり思わなかったり。

そんなときはまたしてもActionFilterAttribute作成しましょう。 たとえば...RequireReactionAttributeとかって言う名前で。 RESTfulFilter でInvokeActionする直前に、Controllerのプロパティで例えばIsReaction = tureとかしておけば、その先でIsReactionがfalseなら実行をキャンセル(filterContext.Cancel=true)にしちゃう。そうしとけば直接アクセスしてほしくないActionに制限かけれたり(Controllerのbaseプロパティで分かる方法あるのかな)。 同じく、RequireAjaxAttributeとかって言うのも作っちゃって、XmlHtpRequest以外の要求は拒否(リクエストヘッダに何か入れとく)なんてことも、簡単(ヘッダ書き換えできちゃうから完璧なわけじゃないけど)。 prototype.jsなら勝手にヘッダーに入れてくれて楽ちんだし、DELETEやPUTのときには_methodにHTTP Method入れてPOSTにするから、そこ見て判定するようにしとくと、さらに楽ちん。

まだまだ工夫の余地ありだけど、HTTP Methodの自動InvokeActionだけでも、だいぶコード量削減出来てうれしい限りです。