中间件基本使用
app.use(async(ctx,next)=>{
ctx.state={
username:'jeff'
};
await next();
...
})
app.use(async(ctx,next)=>{
ctx.state={
username:'jeff2'
};
await next();
...
})
Koa 初始化应用实例
const app = new Koa()
- 为 app 实例添加 context、request、response、middleware 等属性
constructor(options) {
super();
this.middleware = [];
// 每一个 app 实例,都有下面三个对象的实例
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;
}
}
app.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 || '-');
// 直接存入到 middleware 数组中,后续统一处理
this.middleware.push(fn);
return this;
}
app.listen() 监听
listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}
this.callback()
- 在 Node 的 http 模块,对于每一个请求,都会走到回调函数 callback 中去。所以这个 callback 是用于处理实际请求的
callback() {
// 包装所有的中间件,返回一个可执行的函数。koa-compose 实现了洋葱圈模型
const fn = compose(this.middleware);
if (!this.listenerCount('error')) this.on('error', this.onerror);
// 实际触发的函数
const handleRequest = (req, res) => {
// req res 是 node http模块原生请求参数
const ctx = this.createContext(req, res);
// 将创建的 ctx 返回,传给所有中间件,作为整个请求的上下文
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
this.createContext
- 明确 context 挂载的内容,这个 context 是每一次请求都会生成的
- 每次请求都生成避免了全局污染
this.createContex createContext(req, res) {
// 每一个请求对应一个 ctx、request、response、req、res
const context = Object.create(this.context);
const request = context.request = Object.create(this.request);
const response = context.response = Object.create(this.response);
context.app = request.app = response.app = this;
// 挂载 node 原生请求参数 req res 到 context、request、response 上
context.req = request.req = response.req = req;
context.res = request.res = response.res = res;
request.ctx = response.ctx = context;
request.response = response;
response.request = request;
context.originalUrl = request.originalUrl = req.url;
context.state = {};
return context;
}
compose 实现洋葱圈模型
- 遍历中间件列表,将下一个中间件的执行函数函数作为参数传递给next,通过递归的方式,在执行当前中间件的next时,进入下一个中间件
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!')
}
//返回的洋葱模型的可执行函数
return function (context, next) {
// 上一次被执行的中间件
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
// 没有fn的话,直接返回一个已经reolved的Promise对象
if (!fn) return Promise.resolve()
try {
/*
原代码是一行,为了方便理解拆成了三行
//获取下一个中间件的执行函数
const next = dispatch.bind(null, i + 1);
//执行本次中间件函数,执行next就会进行下一个中间件next的分配和执行
const fnResult = fn(context, next);
//将此每次中间件执行结果Promise化
return Promise.resolve(fnResult);
*/
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
this.handleRequest
- fnMiddleware为执行中间件递归处理的函数,返回Promise
handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404;
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
// 执行 compose 后的中间件函数,最后执行 respond
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}