【javaScript】中间件机制(express、koa)

1,691 阅读3分钟

what

在 Node.js 中,中间件主要是指封装所有 Http 请求细节处理的方法。一次 Http 请求通常包含很多工作,如记录日志、ip 过滤、查询字符串、请求体解析、Cookie 处理、权限验证、参数验证、异常处理等,但对于 Web 应用而言,并不希望接触到这么多细节性的处理,因此引入中间件来简化和隔离这些基础设施与业务逻辑之间的细节,让开发者能够关注在业务的开发上,以达到提升开发效率的目的,它的工作模型下图所示。 img

中间件的实现方式一(express)

  • 如下定义三个简单的中间件:
const middleware1 = (req, res, next) => {
  console.log('middleware1 start')
  next()
}

const middleware2 = (req, res, next) => {
  console.log('middleware2 start')
  next()
}

const middleware3 = (req, res, next) => {
  console.log('middleware3 start')
  next()
}
  • 通过递归的形式,将后续中间件的执行方法传递给当前中间件,在当前中间件执行结束,通过调用 next() 方法执行后续中间件的调用。
// 中间件数组
const middlewares = [middleware1, middleware2, middleware3]
function run (req, res) {
  const next = () => {
    // 获取中间件数组中第一个中间件
    //shift:get&delete
    const middleware = middlewares.shift()
    if (middleware) {
      middleware(req, res, next)
    }
  }
  next()
}
run() // 模拟一次请求发起

执行以上代码,可以看到如下结果:

middleware1 start
middleware2 start
middleware3 start
  • 如果中间件中有异步操作,需要在异步操作的流程结束后再调用 next() 方法,否则中间件不能按顺序执行。改写 middleware2 中间件:
const middleware2 = (req, res, next) => {
  console.log('middleware2 start')
  new Promise(resolve => {
    setTimeout(() => resolve(), 1000)
  }).then(() => {
    next()
  })
}
//执行结果与之前一致,不过middleware3会在middleware2异步完成后执行

中间件的实现方式二(koa)

  • 有些中间件不止需要在业务处理前执行,还需要在业务处理后执行,比如统计时间的日志中间件。在方式一情况下,无法在 next() 为异步操作时再将当前中间件的其他代码作为回调执行。因此可以将next() 方法的后续操作(即将要调用的下一个中间件)封装成一个 Promise 对象(next()后,就执行,并且返回一个promise),中间件内部就可以使用 next.then()形式完成业务处理结束后的回调。改写 run() 方法如下:
function run (req, res) {
  const next = () => {
    const middleware = middlewares.shift()
    if (middleware) {
      // 将middleware(req, res, next)包装为Promise对象,返回promise对象
      return Promise.resolve(middleware(req, res, next))
    }
  }
  next()
}
  • 中间件的调用方式需改写为:
const middleware1 = (req, res, next) => {
  console.log('middleware1 start')
  // 所有的中间件都应返回一个Promise对象,即next()获得一个promise(使得可以调用then)
  // Promise.resolve()方法:直接封装中间件返回的结果成为Promise对象,供下层中间件异步控制
  return next().then(() => {
    console.log('middleware1 end')
  })
}

得益于 async 函数的自动异步流程控制,中间件也可以用如下方式来实现:

// async函数自动返回Promise对象
const middleware2 = async (req, res, next) => {
  console.log('middleware2 start')
  
  await new Promise(resolve => {//业务逻辑,和koa实现没有关系
    setTimeout(() => resolve(), 1000)
  })
  await next() 
  console.log('middleware2 end')
  //也可以写成:next.then().catch()之类的
}

const middleware3 = async (req, res, next) => {
  console.log('middleware3 start')
  await next()
  console.log('middleware3 end')
}

express koa的中间件机制对比

  • 比较于express优势:express无法在 next() 为异步操作时再将当前中间件的接下来的代码作为回调执行