functioncompose(middleware) { if (!Array.isArray(middleware)) thrownewTypeError("Middleware stack must be an array!"); for (const fn of middleware) { if (typeof fn !== "function") thrownewTypeError("Middleware must be composed of functions!"); }
/** * @param {Object}context * @return {Promise} * @api public * 这里会接受 context,而且还可以接受一个 next */ returnfunction (context, next) { // last called middleware # let index = -1; return dispatch(0); functiondispatch(i) { if (i <= index) returnPromise.reject(newError("next() called multiple times")); index = i; let fn = middleware[i]; if (i === middleware.length) fn = next; if (!fn) returnPromise.resolve(); try { returnPromise.resolve(fn(context, dispatch.bind(null, i + 1))); } catch (err) { returnPromise.reject(err); } } }; }
functiondispatch(i) { if (i <= index) // i = 0, index = -1 returnPromise.reject(newError("next() called multiple times")); index = i; let fn = middleware[i]; if (i === middleware.length) fn = next; if (!fn) returnPromise.resolve(); try { returnPromise.resolve(fn(context, dispatch.bind(null, i + 1))); } catch (err) { returnPromise.reject(err); } }
如果我们在第二个中间件中继续执行 next 呢。这里也就是 dispatch(2), 会进入 if (i === middleware.length) fn = next; 这里,这里的 next 是最开始 fnMiddleware(ctx) 传入的,显然是 undefined, 然后走 if (!fn) return Promise.resolve(); 这个逻辑,结束递归。
compose 的限制
从上面的例子在扩展下,如果我们执行到 dispatch(2) 再去执行一下 next, 会怎样呢?
别忘了,我们还有一个分支没有走到呢!
1 2
if (i <= index) returnPromise.reject(newError("next() called multiple times"));
在 dispatch(2) 后,再去执行一次 next, 相当于再执行一次 dispatch(2)。为什么这么说呢,因为在最后一个中间件里面,我们的 next 函数就是 fnMiddleware 里的第二个参数,即 undefined 了,这里走的逻辑是 if (!fn) return Promise.resolve();, 直接 resolve 掉了。并没有执行 dispatch.bind(null, i + 1) 这里。然而不同的是 compose 函数里面的闭包 index。index 的含义其实是上一次的 i。
当我们在最后一个中间件中执行两次 next 时,第二次 next 就满足了 i <= index 这个条件,抛出 "next() called multiple times" 错误。
因此,这也就限制了 next 的执行次数,其实可想而知,肯定不会让你执行超过 middleware 长度的 next 了啊
const res = ctx.res; let body = ctx.body; const code = ctx.status;
// ignore body if (statuses.empty[code]) { // strip headers ctx.body = null; return res.end(); }
if (ctx.method === "HEAD") { if (!res.headersSent && !ctx.response.has("Content-Length")) { const { length } = ctx.response; if (Number.isInteger(length)) ctx.length = length; } return res.end(); }
// status body if (body == null) { if (ctx.response._explicitNullBody) { ctx.response.remove("Content-Type"); ctx.response.remove("Transfer-Encoding"); ctx.length = 0; return res.end(); } if (ctx.req.httpVersionMajor >= 2) { body = String(code); } else { body = ctx.message || String(code); } if (!res.headersSent) { ctx.type = "text"; ctx.length = Buffer.byteLength(body); } return res.end(body); }
// responses if (Buffer.isBuffer(body)) return res.end(body); if (typeof body === "string") return res.end(body); if (body instanceof Stream) return body.pipe(res);
// body: json body = JSON.stringify(body); if (!res.headersSent) { ctx.length = Buffer.byteLength(body); } res.end(body); }
{ prototype: { get header: http.IncomingMessage.headers, set header: http.IncomingMessage.headers, get headers: http.IncomingMessage.headers, set headers: http.IncomingMessage.headers, get url: http.IncomingMessage.url, set url: http.IncomingMessage.url, get origin: `${this.protocol}://${this.host}`, get href: this.originalUrl | this.origin + this.originalUrl, get method: http.IncomingMessage.method, set method: http.IncomingMessage.method, get path: http.IncomingMessage.pathname, set path: http.IncomingMessage.url, get query: String, // 返回 queryString set query: String, // 设置 this.querystring get querystring: Function, // 返回 http.IncomingMessage.query || '' set querystring: Function, // 设置 this.url get search: `?${this.querystring}`, set search: this.querystring = str, get host: String, // 请求头的 host, 支持 x-Forwarded-Host get hostname: String, // host 不带端口 get URL: URL, get fresh: Function, // 判断客户端是否过期 get stale: !this.fresh, get idempotent: // 检查请求是否是幂等的 get socket: http.IncomingMessage.socket, get charset: String, // 获取 content-type 中的 charset get length: Content-Length, get protocol: String, // 返回协议字符串“http”或“https” get secure: Boolean, // this.protocol === "https" get ips: Array, // 含有 app.proxy = true 时 get ip: String, // this.socket.remoteAddress || this.ips[0] get subdomains: String[], // 返回子域[] get accept: Object, // Get accept object. set accept: Function, // Set this._accept accepts: Function, acceptsEncodings: Function, // Return accepted encodings or best fit based on `encodings` acceptsCharsets: Function, // Return accepted charsets or best fit based on `charsets` acceptsLanguages: Function, // Return accepted languages or best fit based on `langs` is: Function, // 检查传入请求是否包含“Content Type”标头字段,以及是否包含任何给定的mime“Type”。如果没有请求正文,则返回“null”。如果没有内容类型,则返回“false”。否则,它将返回第一个匹配的“类型”。 get type: String, // Content-type get: Function, // 获取请求头的某个字段 toJSON: Function, } }