koa2中间件的使用分析

437 阅读3分钟

在前面的一篇文章深入理解node框架Express之中间件中,我们以express框架为例,详细了解了中间件的概念、中间件的作用以及中间件的分类。

这篇文章我们来详细看下node当下最流行的框架koa2中关于中间件的使用以及与express中中间件的区别。

1.中间件设计模型区别

  • express中间件的执行顺序是流水线按照注册顺序执行。通常会将response写在最后一个中间件中。
  • koa中间件的执行顺序被设计为洋葱模型,中间件之间通过next函数联系,当一个中间件调用 next() 后,会将控制权交给下一个中间件, 直到下一个中间件不再执行 next() 后, 将会沿路折返,将控制权依次交换给前一个中间件。 洋葱模型图:

001.png

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}`);
});