読者です 読者をやめる 読者になる 読者になる

taketoncheir.log

Like the Decatoncheir by Poseidon Industrial, This blog is Yet Another Storage for My Long Term Memories.

YesodでJSON

YesodでJSON投げたり受けたり

Yesod1.1でJSON使う際のメモ書いときます。
これ出来ればサーバサイドだけYesodを使うということも可能です。

とりあえず全コードはこちら(github)

データ定義

まずはデータ定義から。

Todo json
    text Text
    done Text

config/modelsに定義すれば、mkPersistされてデータ型Todoが作成されます。

JSONとしてこのデータ型を使うには、Data.Aesonモジュールが提供する、FromJSON,ToJSONクラスのインスタンスにする必要があります。これにより、JSONの値に型を与えることが可能になります。

ただ、このインスタンス化作業は mkPersistがやってくれます(@thimuraさんのブログ参照)。
なので、ここですべきことはTodoの横に'json'の文字を加えておくことです。

Handler(サーバサイド)

JSONを受け、JSONで返します。

Handler/Home.hsにて

postTodosR :: Handler RepJson
postTodosR = do
        $(logInfo) "received json request"
        todo <- parseJsonBody_
        $(logInfo) "parse OK"
        tid <- runDB $ insert (todo :: Todo)
        jsonToRepJson $ object ["status" .= ("post" :: Text)]

JSONを受け取ってデータ型へ変換するには、parseJsonBody_(あるいはparseJsonBody)を使います。受け取ったJSONがTodo型であると推論し、todoに値を束縛します。ここではそのまま、runDB $ insert (todo :: Todo)でデータベースへ挿入しています。todoに型注釈を与えることで、このJSONがTodo型であると知らせます。

postTodosRの型はHandler RepJsonにし、JSONをレスポンスとして返します。
ここではobjectで作ったJSONをjsonToRepJsonでRepJson型に変換し、doの流れから返ってHandlerに包まれます。

JavaScript(クライアントサイド)

クライアント側です。

templates/homepage.juliusにて

function postTodoJson() {
    alert("postTodoJson called");
    alert($('#todo [name=f2]').val());
    alert($('#todo [name=f3]').val());

    $.ajax({
        url: '/todo',
        type: 'POST',
        datatype: 'json',
        data: JSON.stringify({
            text: $('#todo [name=f2]').val(),
            done: $('#todo [name=f3]').val()
        }),
        contentType: "application/json; charset=utf-8",
        success: function(o) {
            alert(o.status);
        },
        error: function() {
            alert("error in post");
        }
    });
}

contentTypeを指定して、かつJSON文字列に変換しないと、YesodはリクエストがJSONであると認識して正しくparseしてくれません(Thanks to lbolla for the comment on mailing-list)(contentType指定はなくても動くようです)。


以上でJSONのやり取りができます。
簡潔に書けますし、JSONの値に型がつきますし、非常によろしいんじゃないでしょうか。
ただ、parseJsonBodyに失敗した時のエラー内容が"500 Internal Server Error"としか出ないのはちと困りものですが。

参考サイト

広告を非表示にする