目录
1. 通过koa()创建了什么
// application.js 删除了部分代码
module.exports = class Application extends Emitter {
constructor(options) {
super();
options = options || {};
this.proxy = options.proxy || false;
this.subdomainOffset = options.subdomainOffset || 2;
this.proxyIpHeader = options.proxyIpHeader || 'X-Forwarded-For';
this.maxIpsCount = options.maxIpsCount || 0;
this.env = options.env || process.env.NODE_ENV || 'development';
if (options.keys) this.keys = options.keys;
this.middleware = [];
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
if (util.inspect.custom) {
this[util.inspect.custom] = this.inspect;
}
}
};
创建了一个继承自events的Application对象,在constructor中进行了许多初始化操作。
2. app.listen()是如何启动服务器的
// application.js中的class Application中的类方法listen
listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}
调用application.js中的class Application中的类方法listen,本质上还是调用了http.createServer。
callback方法在下面的“用户发送了请求,中间件是如何被回调的”中详细介绍。
3. app.use(中间件) 中间件放到了哪里
// application.js中的class Application中的类方法use
use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
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.push(fn);
return this;
}
调用了application.js中的class Application中的类方法use,主要是将函数放入middleware这个数组中,等待被调用。
4. 用户发送了请求,中间件是如何被回调的
在listen中调用了callback函数。
// application.js中的class Application中的类方法listen
listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}
让我们来看看callback方法。
// application.js中的class Application中的类方法callback
callback() {
const fn = 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;
}
将所有的中间件进行compose组合后传入handleRequest函数。
先来看看compose函数,它是从koa-compose中引入的。
// koa-compose/index.js
function compose (middleware) {
if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
for (const fn of middleware) {
if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
}
/**
* @param {Object} context
* @return {Promise}
* @api public
*/
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)
}
}
}
}
在这个函数中返回dispatch(0)的执行结果(第一个中间件是需要程序中自动执行的,因为没有前一个next);
在dispatch函数中首先根据传入的 i 获得第i个中间件(i从0开始),并通过Promise.resolve包裹;
若用户调用了next函数,则会调用dispatch函数。
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
因此,compose函数主要的作用就是返回一个Promise,若用户调用了next函数,这个Promise会递归调用用户传入的中间件。
再让我们再回过头看一看callback函数中的handleRequest函数。
// application.js中的class Application中的类方法handleRequest
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所返回的Promise对象fnMiddleware。
以上,大概就是中间件被回调的过程。