let http = require('http')
http.createServer(function (request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'})
response.end('Hello World')
}).listen(8081, () => {
console.log('Server running at http://...:81/')
})
これはnodeのhttpコアモジュールがどのように使われているかということですが、koaの登場によってhttpモジュールの3つの問題が解決されました。
ネイティブhttpモジュールの問題
- コードの書き方に欠陥があり、すべてのロジックが1つの関数に結合されています。
- reqとresの弱い機能性
- httpは、コールバックに基づいて一様にエラーを処理することはできません。
koaさんの回答
ミドルウェア
ミドルウェアを構築してロジックをスライスし、非同期機能を使用することで、非同期コードを同期的に記述できます
koaミドルウェアには古典的なオニオンモデルがあります。
リクエストはアウターミドルウェアからインナーミドルウェアを経由し、アウターレイヤーに戻ります。
各レイヤーは非同期関数なので、各レイヤーは次のレイヤーのミドルウェアが終了するのを待ってから実行を続行する機能を持っています。もちろん、next()の中にawaitを記述する必要があります。
各レイヤーは関数なので、オニオンモデルの構築は単純で、下位ミドルウェアを上位ミドルウェアに渡す関数のセットで、上位ミドルウェアから呼び出されます。
use(callback) { // ミドルウェアの登録
this.middlewares.push(callback)
}
compose(ctx) { // オニオンモデルを構築する
// 複数のプロミスをリンクして、順次実行されるプロミスのチェーンを形成する。
const dispatch = i => {
if (i === this.middlewares.length) return Promise.resolve()
let middleware = this.middlewares[i]
// 下位レイヤーのミドルウェアを遅延関数呼び出しを通して上位レイヤーのミドルウェアに渡す。
return Promise.resolve(middleware(ctx, () => dispatch(i+1)))
}
return dispatch(0)
}
reqとresの拡張
Koaインスタンスは、リクエスト、レスポンス、コンテキストの3つの拡張オブジェクトに依存し、各Koaインスタンスは個別の拡張オブジェクトを保持します。
また、各リクエストは個別の拡張オブジェクトを保持する必要があります。
createContext(req, res) { // reqとresビルドの機能を拡張するctx
// 各リクエストがコンテキストを共有しないようにする
let ctx = Object.create(this.context)
let _request = Object.create(this.request)
let _response = Object.create(this.response)
ctx.request = _request
ctx.req = ctx.request.req = req
ctx.response = _response
ctx.res = ctx.response.res = res
return ctx
}
どのような機能が拡張されるかについては、例えば、ctx.url
/*context*/
module.exports = {
get url() {
return this.request.url
}
}
/*request*/
module.exports = {
get url() {
return this.req.url
}
}
requestはreqを第一層のプロキシとして拡張し、contextは第二層のプロキシとしてリクエストとレスポンスの拡張機能を取得します。
イベントモジュールに基づく統一されたエラー処理
KoaはEmitterのサブスクリプションを発行する機能をnodeで継承しており、エラーイベントを発行し、それをリッスンすることができます。




