だるろぐ

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

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

f:id:daruyanagi:20130106043226p:plain

たとえば、

  • ~/Posts/ …… ページリストの表示
  • ~/Posts/:id …… 個別ページの表示

というのをやってみたいとき。

  • ~/Posts/Default.cshtml

を作成して、

  • UrlData.Count() == 0 ……ページリストの表示
  • UrlData.Count() == 1 ……個別ページの表示

という処理を書けばいいよね(WebMatrix のルーティング - だるろぐ)。

WebMatrix 2 / Razor:switch と RenderPage() - だるろぐ

これはウソだ。申し訳ない。まぁ、別に正しいことを書いているブログではないので、いいと言えばいいんだけど。正しくは、

  • ~/Posts.cshtml

を作成するだね。ほかにもウソがあるのだけど、それはまた別のエントリーでフォローするから!

――なにはともあれ。

この ~/Posts.cshtml をガリガリ書いていけば、いわゆる RESTful な Web アプリケーションが書けるのではないかと思いついた。RESTful の厳密な定義は知らないけど、だいたい

  • Lists:GET /Posts
  • Show:GET /Posts/:id
  • New: POST /Posts
  • Edit:PUT /Posts/:id
  • Remove:DELETE /Posts/:id

みたいな感じだよね。だったら、下のような感じで書けばいいんじゃないか(本質に関係ない部分は削ってる)。

@try{
    switch (Request.HttpMethod.ToUpper())
    {
        case "GET":
            switch (UrlData.Count)
            {
                case 0: // GET Posts/ 
                    @List()
                    break;
                case 1:
                    switch (UrlData[0].ToUpper())
                    {
                        case "NEW": // GET Posts/New
                            @New()
                            break;
                        default:    // GET Posts/1, Posts/Title
                            @Show(UrlData[0])
                            break;
                    }
                    break;
                case 2:
                    switch (UrlData[0].ToUpper())
                    {
                        case "EDIT":    // GET Posts/Edit/1
                            @Edit(UrlData[1])
                            break;
                        case "REMOVE":  // GET Posts/Delete/1
                            @Remove(UrlData[1])
                            break;
                    }
                    break;
            }
            break;
        case "POST":
            Create();
            break;
        case "PUT":
            Update(UrlData[0].AsInt());
            break;
        case "DELETE":
            Delete(UrlData[0].AsInt());
            break;
    }    
}
catch (Exception e)
{
    <p><span class="badge error">Error</span> @e.Message</p>
}

@List() や @New() の部分は、@functions で定義してる。

@functions
{
    HelperResult List()
    {
        var query = "SELECT * FROM Post ORDER BY CreatedAt DESC";

        using (var db = Database.Open("db"))
        {
            var posts = db.Query(query);
            return RenderPage("~/Views/Posts/_ListPosts.cshtml", posts);
        }
    }
}

HelperResult を返す関数にして @ をつけて呼べば、ちゃんと部分ビューがレンダリングされる。POST/PUT/DELETE のところで使う関数は、どうせ Response.Redirect() するから void でいいし、@Delete() なんて書く必要もない。

で。

Create() は動くのだけど、なぜか Update() と Delete() だけが動かない。ちゃんとビューで method も指定したのになぁ……

#Delete() → RenderPage()  ~/Views/Posts/_RemovePost.cshtml"

@{
    var model = PageData.First().Value;
}

<form method="delete" action="~/Posts/@model.Id">
    <div>
        <label>Title</label>
        <input type="text" name="Title" value="@model.Title" disabled />
    </div>
    <div>
        <label>Body</label>
        <textarea name="Body" disabled>@model.Body</textarea>
    </div>
    <div>
        <input type="submit" />
    </div>
</form>

なぜかこれを Submit すると GET での呼び出しになってしまう。よく知らんけど、ブラウザーは GET と POST しかサポートしていないらしい*1。あと、サーバー側にも設定がいるのかな。

とりあえず、GET と POST しか使えない。道理で、IsPost() なんていうのがお役に立つ訳だわ。なんで IsGet()、IsPut()、IsDelete() しかないのかなって思ってた。こういうのって、たぶん Web 開発者にとっては基本的な知識なんだろうな。やっぱりなにごとも経験しないとだめだねぇ。

というわけで、GET と POST だけ使っていろいろ書き直したのだけど、それはまた今度。一足先に感想を言えば、こういうのは「ASP.NET MVC」使った方が賢い(ぉ

*1:そういえば、Rails でも _method="delete" みたいな感じにして、ここら辺の問題を回避していた気がする