だるろぐ

明日できることは、今日しない。

WebMatrix 2:RESTful?な Web アプリケーション (3)

WebMatrix 2:RESTful?な Web アプリケーション (2) - だるろぐ の続き。日中戦争、ベトナム戦争並みに泥沼化してきたけど、突き進んでいこう!

PUT/DELETE メソッドを扱えるようにする

とりあえず、前回の宿題をさっさと終わらせる。

@{
    Layout = "_SiteLayout.cshtml";

    // POST で PUT/DELETE を代用
    string method = Request.HttpMethod.ToUpper();
    if (IsPost && Request["_method"] != null) 
    {
        method = Request["_method"].ToUpper();
    }
}

こんなのでいいのかな。あとはこれを switch すればよさそうだけど……

@switch (method)
{
    case "GET":
        // いろいろ
        break;
    case "POST":
        // さまざま
        break;
    case "PUT":
        // ほげほげ
        break;
    case "DELETE":
        // ふがふが
        break;
    default:
       throw new Exception("なにいってんだおまえ");
}

――ダサいな!

たまたま今日、北陸の女神様のブログ(ASP.NET WEB API ルーティングについていろいろ - miso_soup3 Blog)を読んでいたのだけど、ASP.NET Web API みたいに

// GET ~/api/values/
public void Get() { }

// GET ~/api/values/:id
public void Get(int id) { }

// POST ~/api/values/
public void Post() { }

// PUT ~/api/values/
public void Put() { }

こういうのを勝手にバインディング(っていうのかな?)して呼び出してくれたら、カッコいいのかもしれない。

メソッドのバインディング

というわけで、こういうのを考えてみた。

@using System.Reflection

@{
    Layout = "_SiteLayout.cshtml";

    // POST で PUT/DELETE を代用
    string method = Request.HttpMethod.ToUpper();
    if (IsPost && Request["_method"] != null) 
    {
        method = Request["_method"].ToUpper();
    }

    Type type = this.GetType();
    MethodInfo method_info = type.GetMethod(
        method,
        UrlData.Select(_ => _.GetType()).ToArray()
    );

    // みつからなかったら method_info == null っぽい

    <p>@method_info</p>
}

んでんで、

@functions 
{
   public HelperResult GET()
   {
       return new HelperResult(_ => _.WriteLine("GET()"));
   }

   public HelperResult GET(int id)
   {
       return new HelperResult(_ => _.WriteLine("Get(int id)"));
   }

   public HelperResult GET(string title)
   {
       return new HelperResult(_ => _.WriteLine("Get(string title)"));
   }

   public HelperResult GET(string s1, string s2)
   {
       return new HelperResult(_ => _.WriteLine("Get(string s1, s2)"));
   }

   public HelperResult POST()
   {
       return new HelperResult(_ => _.WriteLine("POST()"));
   }
}

こうやって適当に引数の異なるメソッドを用意しておく。

結果

f:id:daruyanagi:20130111054006p:plain

f:id:daruyanagi:20130111054007p:plain

なんかいい感じだ。出力を

<p>@method_info.Invoke(this, UrlData.Select(_ => _ as object).ToArray())</p>

に変えてみてもうまくいった(なんか不細工だなーもっといい書き方ある気がする)。

f:id:daruyanagi:20130111055006p:plain

POST も投げてみたけどイケてるみたい(わかりにくくてごめん、まだあんまり使い方がよくわかっていない!)。

f:id:daruyanagi:20130111060244p:plain

あとは、MethodInfo を取得するとき、単に UrlData の type[] を問い合わせるのではなくて

MethodInfo method_info = type.GetMethod(
    method,
    UrlData.Select(_ => _.なるべくintに変換する().GetType()).ToArray()
);

みたいなにすれば、GET(int id) でも呼べるはずだよね! ……今日はもう疲れたからやらないけど。ASP.NET MVC/Web API はこういうのをもっとエレガントにやっているんだろうなって思うと、だいぶ尊敬しちゃう。