一、 在 koa 中经常使用的 app.use()
函数源码以下git
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;
}
复制代码
this.middleware = [fn,fn,fn...]
,为一个中间件函数数组,每次使用 app.use(middleware())
,都会将middleware
中间件添加进 this.middleware
数组中。github
在app.listen('3000')
中,会运行以下函数数组
listen(...args) {
debug("listen");
const server = http.createServer(this.callback());
return server.listen(...args);
}
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;
}
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);
}
复制代码
首先咱们看下 http.createServer()
干了啥事,其核心源码以下bash
function Server(options, requestListener) {
if (!(this instanceof Server)) return new Server(options, requestListener);
if (typeof options === 'function') {
requestListener = options;
options = {};
} else if (options == null || typeof options === 'object') {
options = { ...options };
} else {
throw new ERR_INVALID_ARG_TYPE('options', 'object', options);
}
this[kIncomingMessage] = options.IncomingMessage || IncomingMessage;
this[kServerResponse] = options.ServerResponse || ServerResponse;
net.Server.call(this, { allowHalfOpen: true });
if (requestListener) {
this.on('request', requestListener);
}
.......
}
复制代码
核心是 this.on('request', requestListener)
这句,意识是每次来新请求都会调用 requestListener
函数,也就是 koa 源码中的 this.callback()
运行的结果。app
先来看一下 const fn = compose(this.middleware);
的结果,compose 是一个将 中间件函数数组 转化成洋葱式调用函数的工具函数,compose 核心源码以下所示koa
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);
}
}
};
复制代码
以 koa-bodyParser 中间件为例,其源码以下所示async
return async function bodyParser(ctx, next) {
if (ctx.request.body !== undefined) return await next();
if (ctx.disableBodyParser) return await next();
try {
const res = await parseBody(ctx);
ctx.request.body = "parsed" in res ? res.parsed : {};
if (ctx.request.rawBody === undefined) ctx.request.rawBody = res.raw;
} catch (err) {
if (onerror) {
onerror(err, ctx);
} else {
throw err;
}
}
await next();
};
复制代码
若是 this.middleware = [bodyParser1, bodyParser2, bodyParser3]
,this.middleware
通过 compose
处理后结果返回以下。函数
Promise.resolve()
支持链式调用工具
function(context, next) {
return Promise.resolve(async function bodyParser1(context, next) {
...
await async function bodyParser2(context, next) {
...
await async function bodyParser3(context, next) {
...
await Promise.resolve()
}
}
})
}
复制代码
每次有新请求,先调用 this.callback()
中的ui
(req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
复制代码
再调用 通过 compose
处理后的中间件函数 fn
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);
}
复制代码