lert0 2019-11-19
1、首先这是koa2最简单的入门例子,我将通过这个入门例子来演示koa2的洋葱模型
const Koa = require('koa'); const app = new Koa(); app.use((ctx,next)=>{ console.log("第一个中间件执行"); next() ; }); // 第二个中间件 app.use((ctx,next)=>{ console.log("第二个中间件"); }) let r = app.listen(8080); console.log("server run on 8080");
在这里面,app首先是调用了两次use,然后就开始监听端口,
listen(...args) { debug('listen'); // 当客户端发送请求将会调用callback const server = http.createServer(this.callback()); return server.listen(...args); }
因此use是核心:
use(fn) { // ... 一些判断代码 this.middleware.push(fn); return this; }
从上面可以看出这里将外部use的函数通过内部的一个middleware变量记录下来,然后就没了。
OK,现在当客户端发送请求的时候,内部会创建上下文对象,然后处理请求:
callback() { const fn = compose(this.middleware); if (!this.listenerCount('error')) this.on('error', this.onerror); const handleRequest = (req, res) => { // 创建上下文 const ctx = this.createContext(req, res); // 处理请求 return this.handleRequest(ctx, fn); }; return handleRequest; }
处理请求
handleRequest(ctx, fnMiddleware) { const res = ctx.res; res.statusCode = 404; const onerror = err => ctx.onerror(err); const handleResponse = () => respond(ctx); onFinished(res, onerror); // 核心,调用中间件,从这里可以看出我们use(fn)中的fn是一个promise return fnMiddleware(ctx).then(handleResponse).catch(onerror); }
OK,到这里我们知道当浏览器发送一个请求的时候,koa的application对象会根据middleware调用compose来生成一个另一个函数fn,然后向fn中传入上下文ctx执行这个函数。我们都知道,最上面的代码执行顺序是先打印第一个中间件执行
,再打印第二个中间件执行
,那么这个compose这个函数就需要来保证这个机制。具体怎么实现看如下:
function compose (middleware) { if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!') for (const fn of middleware) { if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!') } /** * @param {Object} context * @return {Promise} * @api public */ return function (context, next) { // last called middleware # let index = -1 return dispatch(0) function dispatch (i) { if (i <= index) return Promise.reject(new Error('next() called multiple times')) index = i let fn = middleware[i] if (i === middleware.length) fn = next if (!fn) return Promise.resolve() try { return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); } catch (err) { return Promise.reject(err) } } } }
上面代码虽然不多,但是是koa2实现洋葱模型最重要的部分。整个过程如下:
koa2源码虽然少,但是原理巧妙,值得学习,也正是因为它的小,对于w我们看源码学习也能更轻松.