手把手实现koa2中间件的注册和调用

133 阅读1分钟

Koa2的几个特点:

  1. 中间件只有use方法进行中间件注册
  2. 中间件的返回值是promise实例
  3. 中间件的入参中有ctx上下文,ctx包含req,res,cookie等;入参第二个参数next实现链式调用
  4. 执行中间件时,执行的过程符合洋葱模型(先开始执行的中间件,执行的结果后打印出来,具体可看后面的中间件调用的例子)

下面是like-koa2.js的实现

function compose(middlewares) {
  return (ctx) => {
    // 传入ctx,返回promise;把middleware中的中间件都执行完
    function dispatch(i) {
      const fn = middlewares[i]
      try {
        return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)))
      } catch (error) {
        return Promise.reject(error)
      }
    }
    return dispatch(0)
  }
}


const http = require('http')

class KoaTest3 {
  constructor() {
    this.middleWares = []
  }

  use() {
    const argLists = [...arguments]
    this.middleWares.push(...argLists)
  }

  getCtx(req, res) {
    const ctx = {}
    ctx.req = req
    ctx.res = res
    return ctx
  }

  callback() {
    return (req, res) => {
      res.setHeader("Content-type", "application/json");
      // ctx是所有中间件共用
      const ctx = this.getCtx(req, res)
      // 依次执行每一个中间件
      // 中间件的入参是ctx 和 指向middleWares的index,返回值是Promise实例, 且依次往后调用
      const fn = compose(this.middleWares)
      return fn(ctx)
    }
  }

  listen() {
    const server = http.createServer(this.callback())
    server.listen(...arguments)
  }
}

module.exports = KoaTest3

在test.js中调用like-koa2.js

const Koa = require('./like-koa2.js')
const app = new Koa()

app.use(async (ctx, next) => {
  console.log(1);
  await next()
  console.log(2);
  const rt = ctx['X-Response-Time']
  console.log(`${ctx.req.method} ${ctx.req.url} - ** ${rt}`)
})

// x-response-time
app.use(async(ctx, next) => {
  const start = Date.now()
  console.log(3);
  await next()
  console.log(4);
  const ms = Date.now() - start
  ctx['X-Response-Time'] = `${ms} ms`
})

app.use(async ctx => {
  const url = ctx.req.url
  const method = ctx.req.method.toLowerCase()
  if(url === '/api/user/update' && method === 'post') {
    ctx.res.end(JSON.stringify({
      errno: 0,
      data: {name: 'zhangsan', id: 1}
    }))
  }else if(url === '/api/blog/list') {
    ctx.res.end(JSON.stringify({
      errno: 0,
      data: {title: 'blog title', id: 1, time: '2022-2-13 22:06:40'}
    }))
  } else {
    ctx.res.end(JSON.stringify({errno: 0, value: 'we11'}))
  }
})

app.listen(8989, () => {
  console.log('welcome, 8989!!')
})

执行node test.js,访问 http://localhost:8989/api/blog/list 控制台打印的结果:

1
3
4
2
GET /api/blog/list - ** 5 ms

接口返回的结果:

{"errno":0,"data":{"title":"blog title","id":1,"time":"2022-2-13 22:06:40"}}

以上,就是koa2的中间件注册和使用。难点是compose 方法的实现。