だるろぐ

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

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

WebMatrix 2:RESTful?な Web アプリケーション - だるろぐ の続き。とりあえず、

  • /Posts/ -> GET:List
  • /Posts/:id -> GET:Show
  • /Posts/New -> GET:New, POST:Create
  • /Posts/Edit/:id -> GET:Edit, POST:Update
  • /Posts/Remove/:id -> GET:Remove, POST:DELETE

って感じにマッピングされるように頑張ってみた。

# ~/Posts.cshtml

@{
    Layout = "_SiteLayout.cshtml";
}

@try{
    switch (UrlData[0].ToUpper())
    {
        case "": // <- / へアクセスするととりあえず UrlData[0] には string.empty が入るっぽい
        case "LIST":
            if (!IsPost) // GET
            {
                @List()
            }
            else // POST
            {
                throw new ApplicationException();
            }
            break;
            
        case "NEW":
        case "CREATE": // <- ビューを書き換えるのがめんどいので
            if (!IsPost) // GET
            {
                @New(UrlData[1])
            }
            else // POST
            {
                Create();
            }
            break;
            
        case "EDIT":
        case "UPDATE":
            if (!IsPost) // GET
            {
                @Edit(UrlData[1])
            }
            else // POST
            {
                Update(UrlData[1].AsInt());
            }
            break;
            
        case "REMOVE":
        case "DELETE":
            if (!IsPost) // GET
            {
                @Remove(UrlData[1])
            }
            else // POST
            {
                Delete(UrlData[1].AsInt());
            }
            break;

        default:
            if (!IsPost) // GET
            {
                @Show(UrlData[0])
            }
            else // POST
            {
                throw new ApplicationException();
            }
            break;
    }
}
catch (Exception e)
{
    <p><span class="badge error">Error</span> @e.Message</p>
}


@functions
{
    dynamic FindPostByIdOrTitle(string param)
    {
        int id = 0;
        dynamic post;
        
        using (var db = Database.Open("db"))
        {
            if (int.TryParse(param, out id))
            {
                var query = "SELECT * FROM Post WHERE Id=@0";
                post = db.QuerySingle(query, id);
            }
            else
            {
                var query = "SELECT * FROM Post WHERE Title=@0";
                post = db.QuerySingle(query, param);
            }
        }
        
        if (post == null)
            throw new HttpException(404, string.Format(
                @"Post ""{0}"" is not found.", param)
            );
        
        return post;
    }

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

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

    HelperResult Show(string param)
    {
        var post = FindPostByIdOrTitle(param);
        Page.Title = post.Title;
        return RenderPage("~/Views/Posts/_ShowPost.cshtml", post);
    }

    HelperResult New(string param)
    {
        Page.Title = "New Post:" + param != string.Empty ? param : "Untitled";
        return RenderPage("~/Views/Posts/_NewPost.cshtml");
    }

    HelperResult Edit(string param)
    {
        var post = FindPostByIdOrTitle(param);
        Page.Title = "Edit: " + post.Title;
        return RenderPage("~/Views/Posts/_EditPost.cshtml", post);
    }

    HelperResult Remove(string param)
    {
        var post = FindPostByIdOrTitle(param);
        Page.Title = "Remove: " + post.Title;
        return RenderPage("~/Views/Posts/_RemovePost.cshtml", post);
    }
    
    void Create()
    {
        var query = "INSERT INTO Post (Title, Body, CreatedAt, UpdatedAt)"
                  + "VALUES(@0, @1, @2, @3)";
        var title = Request["Title"];
        var body = Request["body"];
        var now = DateTime.Now;

        using (var db = Database.Open("db"))
        {
            db.Query(query, title, body, now, now);
            var key = db.QueryValue("SELECT @@IDENTITY");
            Response.Redirect("~/Posts/" + key.ToString());
        }
    }

    void Update(int id)
    {
        var query = "UPDATE Post SET Title=@0, Body=@1, UpdatedAt=@2 WHERE Id=@3";
        var title = Request["Title"];
        var body = Request["body"];
        var now = DateTime.Now;

        using (var db = Database.Open("db"))
        {
            db.Query(query, title, body, now, id);
            Response.Redirect("~/Posts/" + id.ToString());
        }
    }

    void Delete(int id)
    {
        var query = "DELETE FROM Post WHERE Id=@0";

        using (var db = Database.Open("db"))
        {
            db.Query(query, id);
            Response.Redirect("~/Posts/");
        }
    }
}

あんまり気に入らない。なんか当初の目標とズレてきたし。

でも、よく考えたら、Web Form で GET/POST しかリクエストできないとしても、JavaScript だったらできるわけだよね。そんな Web Form の仕様に合わせなくてもいいんじゃね?

そんなことを考えていたら、こんなナイスなページを見つけた。

HTML5より前のHTMLでは、フォームで利用できるHTTPメソッドはGETとPOSTだけである。そのため、PUTやDELETEを使う場合には、JavaScriptが必要になる(JavaScriptでのHTTPリクエストを参照)。一部のケータイのブラウザのように、JavaScriptをサポートしていないブラウザでは、PUTやDELETEはPOSTで代用しなければならない。PUTやDELETEをPOSTで代用する方法には、以下の2つがあるが、フォームで利用できるのは、パラメータ_methodを利用する方法だけである。

http://wiki.unfindable.net/webbook2/index.php/Web%e3%83%9a%e3%83%bc%e3%82%b8%e3%81%8b%e3%82%89%e3%81%aePUT%e3%81%a8DELETE

あーこれだ! Rails でもやっていたやつだよね?

というわけで、これベースでまた書き直しする。そのあとは、Twitter/Facebook 認証を追加することにしたいと思ってる。