记录本次koa中间件加载过程源码学习,主要内容:
- koa启动过程
- koa中间件加载过程
koa启动过程
koa启动过程主要为以下三个步骤:
- 引入Koa模块,创建应用app对象
- 加载中间件
- 启动应用监听
// 示例
const Koa = require('koa');
const app = new Koa();
app.use((ctx, next) => {
Log.info('request'. ctx.reqesut);
next();
})
// response
app.use(ctx => {
ctx.body = 'Hello Koa';
});
app.listen(3000);
中间件加载
中间件注册
koa调用app.use()加载中间件,源码application.js中定义如下:
use (fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!')
debug('use %s', fn._name || fn.name || '-')
this.middleware.push(fn)
return this
}
中间件被调用
中间件注册完成后,在http.createServer创建回调中被调用
// http server回调
listen (...args) {
debug('listen')
const server = http.createServer(this.callback())
return server.listen(...args)
}
// callback
callback () {
// 通过compose形成中间件链式串行调用
const fn = this.compose(this.middleware)
if (!this.listenerCount('error')) this.on('error', this.onerror)
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res)
return this.handleRequest(ctx, fn)
}
return handleRequest
}
// request handle
handleRequest (ctx, fnMiddleware) {
const res = ctx.res
res.statusCode = 404
const onerror = err => ctx.onerror(err)
const handleResponse = () => respond(ctx)
onFinished(res, onerror)
// 中间件处理请求
return fnMiddleware(ctx).then(handleResponse).catch(onerror)
}
中间件链式串行调用核心方法compose
compose方法接受中间件数组入参[middleware1, middleware2, middleware3, ...],最终生成一个请求处理函数,然后内部上一个中间件处理完请求后,依次调用下一个中间件进行处理,核心过程如下:
// 中间方法定义
function middlewareFn(context, next) {
// todo
...
// 启动下一个中间件
next();
}
// compose 生成请求链式处理函数,核心流程如下
function (middleware) {
// 最终生成的处理函数
return function(context, next) {
// 启动第一个中间件
return dispatch(0);
// 中间件 i 会启动中间件 i+1
function dispatch(i) {
let index = -1;
if (i <= index) {
return Promise.reject(new Error('error'));
}
index = i;
let middlewareFn = middleware[i];
// 中间件调用完毕则执行外部传入的调用
if (i === middleware.length) {
middlewareFn = next;
}
// 处理完成后结束
if (!middlewareFn){
return Promise.resolve();
}
try {
// 中间件依次调用
return Promise.resolve(middlewareFn(context, dispatch.bind(null, i + 1)))
} catch (error) {
return Promise.reject(error);
}
}
}
}