use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
// 如果是一个生成器函数要转换成 async 函数,细节问题
if (isGeneratorFunction(fn)) {
deprecate('Support for generators will be removed in v3. ' +
'See the documentation for examples of how to convert old middleware ' +
'https://github.com/koajs/koa/blob/master/docs/migration.md');
fn = convert(fn);
}
debug('use %s', fn._name || fn.name || '-');
// 直接将回调函数存储到 this.middleware 当中
this.middleware.push(fn);
return this; // 通过返回自己可以进行链式调用
}
这篇文章介绍一个应用服务器框架的主要两个过程:app init 过程和 request handle 过程。一些有趣的细节问题看看以后再写,包括 context, request, response 三个对象,错误处理,egg.js 等等。
init 过程
通过一个简单的 demo(实际上就是官网的例子)来讲解 app init 过程。对于 Koa 来说,init 过程是比较简单的。
Koa 对象实例化
lib/application.js
use 增加 middleware
监听端口
this.callback()
方法返回一个回调函数,它符合 Node.js 原生 http.createServer 的要求,被当作 request handler.compose
这是个很重要的方法,其返回的
fn
, 将会在请求到达的时候实际负责 context 在 middleware 中的传递。request handle 过程
还是用上面的例子来讲解 request handle 过程。
当有 http 请求过来的时候,如下的方法最先被调用:
创建 context
处理过程
对 request 的实际处理过程。
context 在 middleware 中间的传递
当
fnMiddleware
被调用的时候,即这个函数被调用:可以看到这个函数是递归的:
用我们的例子:
dispatch(0)
, 我们注册的第一个异步函数被当成fn
, 然后fn(context, dispatch.bind(null, 1))
调用了这第一个异步函数next()
, 实际上执行了dispatch(1)
, 然后调用了第二个异步函数。next()
, 所以异步函数返回了状态为 resolved 的Promise<undefined>
return Promise.resolve()
把异步函数返回的 Promise 接着 resolved 下去dispatch(0)
中的 resolved 的 Promise 被 return 出去handleRequest
进入fnMiddleware(ctx).then(handleResponse)
, 执行handleResponse
例外情形
如果最后一个中间件也调用了
next
此时
fn === undefined
, 并且next === undefined
, 所以就会直接返回已 resolved 的 Promise, 开始回溯。如果有一个中间件调用了两次
next
我们已经知道每次调用
next
实际是调用了一次dispatch(i)
, 如果我们调用了同一个next
两次,那么第二次调用的时候,i === index
的条件就会成立。我们说过index
是指示 context 在 middleware 中的位置的。创建响应
这个方法和 Koa 的关系不大了,其实就是在处理 response 的各种可能情况,然后调用 http 模块 res 的方法返回响应。