在前面的一篇文章深入理解node框架Express之中间件中,我们以express框架为例,详细了解了中间件的概念、中间件的作用以及中间件的分类。
这篇文章我们来详细看下node当下最流行的框架koa2中关于中间件的使用以及与express中中间件的区别。
1.中间件设计模型区别
- express中间件的执行顺序是流水线按照注册顺序执行。通常会将response写在最后一个中间件中。
- koa中间件的执行顺序被设计为洋葱模型,中间件之间通过next函数联系,当一个中间件调用 next() 后,会将控制权交给下一个中间件, 直到下一个中间件不再执行 next() 后, 将会沿路折返,将控制权依次交换给前一个中间件。 洋葱模型图:
2. 简单使用
koa中间件就是一个函数,接受2个参数,ctx和next()
例1
import Koa from 'koa';
const app = new Koa();
// 注册应用类型中间件
app.use((ctx, next) => {
console.log('hello koa2');
});
等价写法
import Koa from 'koa';
const app = new Koa();
// 这就是一个中间件函数
function printHello(ctx, next) {
console.log('hello koa2');
}
// 中间件注册
app.use(printHello);
注册多个
import Koa from 'koa';
const app = new Koa();
function printHello(ctx, next) {
console.log('hello print1');
next(); // 调用下一个中间件函数
}
app.use(printHello);
app.use((ctx, next) => {
console.log('hello print2');
next();
});
3.执行顺序
我们通过下面一个简单的代码示例,理解下koa中间件的执行顺序。
import Koa from 'koa';
const app = new Koa();
app.use((ctx, next) => {
console.log('print1');
next();
console.log('print2');
});
app.use((ctx, next) => {
console.log('print3');
next();
console.log('print4');
});
执行顺序:print1➡print3➡print4➡print2
这个打印顺序就是典型的洋葱模型,每个中间件函数被next分割成了上半部分和下半部分,当一个请求进来之后先执行fun1的上半部分,接着是fun2的上半部分,再然后是fun2的下半部分,最后是fun1的下半部分。
3. next是什么?
next是通过参数传递的方式,被传递进入中间件函数的第二个参数,通过开发者执行调用next函数,来调用下一个中间件函数。
既然next是一个函数,那么它有返回值吗?返回值是什么?我们来通过一段简单代码看下
import Koa from 'koa';
const app = new Koa();
app.use((ctx, next) => {
console.log('print3');
const nextValue = next();
console.log(nextValue); // Promise { undefined }
});
app.listen(8080, () => {
console.log(`server is runing in localhost://${8080}`);
});
console结果我们看到next函数的返回值是一个Promise,但里面的值是undefined,这是因为没有下一个中间件函数执行。
我们再来看另一个示例
import Koa from 'koa';
const app = new Koa();
app.use((ctx, next) => {
console.log('print3');
const nextValue = next();
console.log(nextValue);// Promise { '123' }
});
app.use((ctx, next) => {
return '123';
});
app.listen(8080, () => {
console.log(`server is runing in localhost://${8080}`);
});
我们可以看到,这次next函数的返回值为Promise { '123' },将后面一个中间件函数的返回值,用promise包装返回。
4. 获取next返回值的原始值
那么问题又来了,我们有时候就想要接收到的结果就直接是我返回的字符串123,而不是被包装过的promise{‘123’},怎么办呢?
是时候出来了--async和await
我们稍微改写下上面的例子
import Koa from 'koa';
const app = new Koa();
app.use(async (ctx, next) => {
console.log('print3');
const nextValue = await next();
console.log(nextValue); // 123
});
app.use((ctx, next) => {
return '123';
});
app.listen(8080, () => {
console.log(`server is runing in localhost://${8080}`);
});