概念
Koa中间件的最大特色就是中间件(middleware)的设计。
中间件是一个函数,它处在HTTP Request和HTTP Response中间,用来实现某种中间功能,通过app.use()
来加载中间件。
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx) => {
ctx.response.body = 'GO'
});
app.listen(8080, () => {
console.log('app is listening 8080...');
});
中间件的执行顺序
多个中间件会形成栈结构,以先进后出的顺序执行:
- 最外层的中间件首先执行
- 代用
next
函数,把执行权交给下一个中间件 - …
- 最内层的中间件最后执行
- 执行结束后,把执行权交回上一层的中间件
- …
- 最外层的中间件收回执行权后,执行
next
函数后面的代码
看下面的例子:
app.use(async (ctx, next) => {
console.log(1-1);
ctx.response.body = 'GO';
next();
console.log(1-2);
});
app.use(async (ctx, next) => {
console.log(2-1);
next();
console.log(2-2);
});
app.use(async (ctx, next) => {
console.log(3-1);
next();
console.log(3-2);
});
app.listen(8080, () => {
console.log('app is listening 8080...');
});
执行结果是:
1-1
2-1
3-1
3-2
2-2
1-2
这种先进后出的加载模型也可以叫做洋葱圈的模型:
如果中间件内部没有调用next
函数,那么执行权就不会传递下去。
异步中间件
当中间件中包含异步操作时,中间件应该写成Async函数:
app.use(async (ctx, next) => {
ctx.response.body = await fse.readFile('../demo3/test.txt', 'utf-8');
});
注意,如果调用next
,必须等待完成
app.use(async (ctx, next) => {
console.log(1);
next();
});
app.use((ctx) => {
ctx.response.body = await fse.readFile('../demo3/test.txt', 'utf-8');
});
如果是上面的形式,返回的body
中将没有任何内容,这是因为Koa在Promise链被机械系了之后就结束了请求,这意味着我们在设置ctx.response.body
之前,response
就已经被发送了给客户端,正确的做法应该是在第一个中间件的next
之前添加await
:
app.use(async (ctx, next) => {
console.log(1);
await next();
});
app.use((ctx) => {
ctx.response.body = await fse.readFile('../demo3/test.txt', 'utf-8');
});
当使用纯粹的Promise(不使用Async/Await)应该写成这样:
app.use((ctx, next) => {
ctx.statu