koa中间件原理
koa 功能
- 封装 http 模块【lib/application 下的 listen 方法】
- 构建中间件模型【通过 koa-compose 包】
- 整合了request,response,context【lib/application 下的 createContext 方法】
- 错误处理【lib/application 下的 onerror 方法】
一个基础模板代码如下:
const Koa = require('koa');
const app = new Koa();
// logger
app.use(async (ctx, next) => {
await next();
const rt = ctx.response.get(‘X-Response-Time’);
console.log(</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>ctx<span class="token punctuation">.</span>method<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>ctx<span class="token punctuation">.</span>url<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> - </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>rt<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">
);
});
// x-response-time
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.set(‘X-Response-Time’, </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>ms<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">ms</span><span class="token template-punctuation string">
);
});
// response
app.use(async ctx => {
ctx.body = ‘Hello World’;
});
app.listen(3000);
koa 里,中间件函数接收两个参数 context, next,
通过 use 方法,将函数添加到 middleware
数组里【 this.middleware.push(fn)
】,
在 listen 里
http.createServer(this.callback()).listen(...args);
调用了 callback
,在 callback
里,通过 compose(this.middleware)
,
处理之前添加过的中间件,返回 this.handleRequest(ctx, fn)
来执行中间件,
handleRequest
是返回 Promise 函数的,所以在编写中间件时,往往通过 async await 来构建,这里主要来解析下 compose
,它是引用了 koa-compose
包,主要源码如下:
function compose () {
return function (context, next) {
// last called middleware
let index = -1
return dispatch(0)
function dispatch (i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
compose 里会返回 dispatch 方法,dispatch 里会先执行第一个fn,并将 middleware 队列里下一个 fn 作为 next 参数,且 dispatch 返回的为 Promise,从而形成了先进后出洋葱式模型。