ES6過渡期のMithril入門 ③WebフレームワークKoaにPOST


0) 今回は、web連携最初の関門、POST

先人のおかげで、あまり苦労せず。Expressに比べ、モジュール選定の自由が高い分、あちこち調べながらとはなったが。あと、Curlのお勉強も必要となった。

1) MithrilのPOST関係コードを見る

Mithril黒ムツ本(というらしい)のサンプルのうち、2章のToDo3を参考にする。
MVVMモデルのMithrilということで、追加ボタンを押した後、

[View]
- m("input", {onchange: m.withAttr("value", vm.description), value: vm.description()})
- m("button", {onclick: vm.add}, "追加")
[ViewModel]
- vm.add = function (){ ... Todo.save(vm.list())...}
[Model]
- Todo.save = function...

の順に、メソッドが呼ばれサーバへのPOSTが行われる。Mithtil APIのm.requestを用い、dataコレクションを送付:

引用元=github.com/oreilly-japan/mithril-book-sample/blob/master/chapter02_tutorial/todo3/client/app.js
// サーバに現在のタスクを送信
Todo.save = function (todoList) {
    var data = todoList.filter(function (todo) { return !todo.done(); });
    m.request({method: "POST", url: "/tasks", data: data});
};

受け側のexpressコードは以下:

引用元github.com/oreilly-japan/mithril-book-sample/blob/master/chapter02_tutorial/todo3/server.js
app.post("/tasks", function (req, res) {
    console.log("post tasks");
    fs.writeFileSync("todo.json", JSON.stringify(req.body));
    res.status(200).end();
});

req.bodyはJSONの配列で、これを、fsモジュールより、ローカルファイルのtodo.jsonに書き出している。

m.requestによる呼び出しに対応するcurlは以下:

curl localhost:8000/tasks -H 'Content-Type: application/json' --data-binary '[{"description":"入力内容","done":false}]'
cf. Curlどう書くんだっけ?という時は、こちらを参考: WebAPIリクエスト仕様書としてcurlコマンドのご提案

doneは、ToDoのチェックがされたかどうか(true/false)。

2) KoaでMithrilからのPOSTを受ける。

ということで、上記のcurlリクエストを受けるKoaコードを書いてみる。

まずは、今回の作業に必要となりそうな、Koaのモジュールを改めて導入。

npm init
npm install koa koa-router koa-bodyparser koa-json koa-static co-views jade fs --save

このうち、koa-bodyparser がPOSTされたbodyをパースしてくれる。本質コードは、これだけ:

router.post '/', (next)->
    fs.writeFileSync "todo.json",JSON.stringify(@request.body)
  yield next

curlの--data-binary以下から送付された内容(ここではJSON配列)が、@request.bodyに格納されるので、その内容をファイルに書き出す。

今回から、よく出来たラウター・モジュールとして、koa-routerを導入。
結果、app本体部分は以下のコードに:

app.coffee
koa = require 'koa'
api = do require 'koa-router'
serve = require 'koa-static'

api.use '/tasks', require './api/tasks' 
#/tasksへのリクエストは./api/task.coffeeにて処理

app = koa()
app
.use do require 'koa-bodyparser'
.use do require 'koa-json'
.use api.routes()
.use api.allowedMethods()
.use serve __dirname + '/static' #静的HTMLは、/static以下に置く。
.listen 3000

ToDoタスクの登録・一覧部分のコード:

/api/task.coffee
router = do require 'koa-router'
fs = require "fs"

# `POST /tasks` = タスク登録(todo.jsonに書き出し)
router.post '/', (next)->
    console.log "post tasks"
    fs.writeFileSync "todo.json",JSON.stringify(@request.body)
  yield next

# `GET /tasks` = タスク一覧(todo.jsonから読み出し)
router.get '/', (next)->
    console.log "get tasks"
    docs =  JSON.parse fs.readFileSync("todo.json", "utf8")
    @body = docs
    yield next

module.exports = router.routes()

koa-routerのおかげで、コードも適切にモジュール化され、めでたしめでたし。
・・・で終わりにしたかったのだが、router.post~yield nextだけでは、タスク一覧の更新をよろしくやってくれないらしい(ブラウザ画面をリフレッシュすると、更新されたタスク一覧が表示される)。このあたり、Mithril側の責務だと思うが、次回への宿題とする。

3) 次回以降

Mithrilとkoa-routerの組み合わせをマスターすると共に、mongodbあたりへの永続化を行う。
ここまでできたら、全体をgithubに上げることとしたい。

koa-routerとKoaでのmongoの扱いについては、先人:koaでjsonwebtokenを使っていい感じに認証するが、自前認証モジュール含め、かなりいい感じ。Thanks!