洋葱模型
官网例子:
const Koa = require('koa'); const app = new Koa(); // logger app.use(async (ctx, next) => { await next(); const rt = ctx.response.get('X-Response-Time'); console.log(`${ctx.method} ${ctx.url} - ${rt}`); }); // x-response-time app.use(async (ctx, next) => { const start = Date.now(); await next(); const ms = Date.now() - start; ctx.set('X-Response-Time', `${ms}ms`); }); // response app.use(async ctx => { ctx.body = 'Hello World'; }); app.listen(3000);
根据中间件的执行顺序,我们可以得到一下步骤:
1.logger中间件 执行了 await next()将执行权移交给了下一个中间件x-response-time
2.x-response-time中间件 记录了当前的时间,把执行权移交给了下一个中间件response
3.response中间件 返回了’Hello World’,把执行权还给了x-response-time
4.x-response-time 记录了当前时间 - 执行开始时间,并在respones header中记录下来,把执行权移交给了logger中间件
5.logger中间件在response中获取到X-Response-Time,并打印相关结果。
完整代码
const http = require('http'); // 组合中间件 function compose(middlewareList) { return function (ctx) { function dispatch(i) { const fn = middlewareList[i] try { return Promise.resolve( fn(ctx, dispatch.bind(null, i + 1)) ) } catch (err) { return Promise.reject(err) } } return dispatch(0) } } class koaMiddleWare { constructor() { this.middlewareList = [] } // 核心方法 use(fn) { this.middlewareList.push(fn) return this } // 处理中间件的 http 请求 handleRequest(ctx, middleWare) { // 这个 middleWare 就是 compose 函数返回的 fn // 执行 middleWare(ctx) 其实就是执行中间件函数,然后再用 Promise.resolve 封装并返回 return middleWare(ctx) } // 将 req res 组合成为 ctx createContext(req, res) { // 简单模拟 koa 的 ctx ,不管细节了 const ctx = { req, res } return ctx } callback() { const fn = compose(this.middlewareList) return (req, res) => { const ctx = this.createContext(req, res) return this.handleRequest(ctx, fn) } } listen(...args) { const server = http.createServer(this.callback()) return server.listen(...args); } } module.exports = koaMiddleWare