Koa 系列 —— Koa 中间件机制解析

栏目: Node.js · 发布时间: 5年前

内容简介:上一篇讲了Koa 中间件采取的是中间件洋葱模型,具体原理可见当然实际上更复杂,还要考虑中间件的异步执行和中间件如何进行嵌套。Koa 中异步处理在 Koa1 中使用的是 generator + co.js,在 Koa2 中使用的是 async/await,我们本次采用 async/await 来处理异步。中间件的嵌套可以通过将中间件当参数传递来实现嵌套。据此我们对上面的代码进行进一步加工:

上一篇讲了 如何编写属于自己的 Koa 中间件 ,本篇将根据原理实现一个简单的中间件处理函数,并对 Koa 中间件处理函数 compose 函数进行源码解析。

1. compose 函数简单实现

Koa 中间件采取的是中间件洋葱模型,具体原理可见 如何编写属于自己的 Koa 中间件 。本质就是将中间件嵌套执行:

function middleware0(){
  console.log('middleware0')
}
function middleware1(){
  console.log('middleware1')
}
// 将两个中间件嵌套执行
middleware0(middleware1())

当然实际上更复杂,还要考虑中间件的异步执行和中间件如何进行嵌套。Koa 中异步处理在 Koa1 中使用的是 generator + co.js,在 Koa2 中使用的是 async/await,我们本次采用 async/await 来处理异步。中间件的嵌套可以通过将中间件当参数传递来实现嵌套。据此我们对上面的代码进行进一步加工:

ps:Node7.6+ 支持 async/await

async function middleware0(next){
  console.log('middleware0')
  await next()
}
async function middleware1(next){
  console.log('middleware1')
}
// 将两个中间件嵌套执行
middleware0(middleware1)

Koa 中通过 compose 函数对中间件的进行处理。compose 函数参数为 middleware 的数组, middleware 数组成员是通过 use 方法添加的中间件。下面写个简单的 compose 函数,来实现多个中间件的处理:

async function middleware0(next){
  console.log('middleware0')
  await next()
  console.log('middleware0 end')
}
async function middleware1(next){
  console.log('middleware1')
  await next()
  console.log('middleware1 end')
}
async function middleware2(next){
  console.log('middleware2')
  await next()
  console.log('middleware2 end')
}

/**
 * @param {Array} 中间件数组
 */
function compose (middleware) {
  // 从第一个中间件开始执行
  return dispatch(0) 
  function dispatch(i){
    // 获取第 i 个中间件
    fn = middleware[i]
    // 获取不到中间件,则直接返回结束
    if(!fn) return
    // 执行第 i 个中间件,并传入第 i + 1 个中间件
    return fn(() => dispatch(i + 1))
  }
}
// 执行
compose([middleware0, middleware1, middleware2])

2. 源码解析

我们已经简单实现了一个 compose 函数,现在来看下 Koa 中源码的实现。Koa 中的 compose 函数已经提取到 koa-compose 包中,其中的核心代码如下:

/**
 * @param {Array} 中间件数组
 * @return {Function}
 */
function compose (middleware) {
  // 判断是否为数组,不是则抛出异常
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
  // 判断 middleware 数组中的中间件是否为函数,不是函数抛出异常
  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) {
      // 同一个中间件多次调用 next 抛出异常
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      index = i
      // 获取第 i 个中间件
      let fn = middleware[i]
      /**
       * 中间件执行结束,检查是否有传入 next 回调函数
       * 此 next 并非中间件执行的 next 参数
       */
      if (i === middleware.length) fn = next
      /**
       * 所有的返回都是Promise对象
       * Promise对象可以保证中间件和返回请求对象之间的执行顺序
       */
      if (!fn) return Promise.resolve()
      try {
        // 执行第 i 个中间件,并传入第 i + 1 个中间件
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

通过解析可以发现,源码相对于我们的实现更加健全:

  • 更完善的异常处理

    • 在执行前统一对传入参数进行检查
    • 多次执行 next 函数抛出异常处理等
  • 最终返回结果 Promise 化,保证中间件和整个处理函数在 Koa 中的执行顺序,具体可参考下面 Koa 源码片段:
/**
 * application.js
 * fnMiddleware(ctx) 就是 compose 函数返回的函数,默认不传入 next 参数
 * Promise 保证中间件,handleResponse 执行顺序。
 */
fnMiddleware(ctx).then(handleResponse).catch(onerror)

3. 小结

从最开始的编写 Koa 中间件,到现在阅读 compose 函数源码,Koa 中间件机制并不复杂,了解之后,我们可以运用、编写更合适的中间件,构建自己的 Koa 应用。

  • 本文首发于公众号,更多内容欢迎关注我的公众号: 阿夸漫谈

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Build Your Own Web Site the Right Way Using HTML & CSS

Build Your Own Web Site the Right Way Using HTML & CSS

Ian Lloyd / SitePoint / 2006-05-02 / USD 29.95

Build Your Own Website The Right Way Using HTML & CSS teaches web development from scratch, without assuming any previous knowledge of HTML, CSS or web development techniques. This book introduces you......一起来看看 《Build Your Own Web Site the Right Way Using HTML & CSS》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

html转js在线工具
html转js在线工具

html转js在线工具