動くものを作ろうと思うと、やっぱり流行りのスタイルに乗っかりたい。そう思うのは浅はかなことでしょうか。で、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だけでも、だいぶコード量削減出来てうれしい限りです。