Koa中间件加载源码学习笔记

68 阅读2分钟

记录本次koa中间件加载过程源码学习,主要内容:

  1. koa启动过程
  2. 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);
            }
        }
    }
}