Express 中的 next
const app = require("express")()
app.use((req, res, next) => {
console.log(1);
next();
console.log(6);
})
app.use((req, res, next) => {
console.log(2);
next();
console.log(5);
})
app.use((req, res, next) => {
console.log(3);
next();
console.log(4);
})
app.use((req, res, next) => {
res.send('Hello Express');
});
app.listen(3000);
// 输出 1 2 3 4 5 6
注册
Express 中使用 use 函数注册中间件, 并按照注册顺序调用
注册时最终会调用 lib/router/index.js use 函数
最终会 new 一个 Layer 并放入 stack 队列中
proto.use = function use(fn) {
...
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: false,
end: false
}, fn);
layer.route = undefined;
this.stack.push(layer);
...
}
调用
在收到请求时的处理在 lib/router/index.js handle
然后在 handle 函数中定义了 next 函数
然后执行 next 函数去到 lib/router/layer.js
的 handle_request 函数或者 handle_error 函数
// lib/router/index.js handle 极度简化了代码
proto.handle = function handle(req, res, out) {
...
idx = 0
stack = this.stack
next()
function next(err) {
...
if (idx >= stack.length) {
return;
}
...
layer = stack[idx++]
...
if (err) {
layer.handle_error(err, req, res, next);
} else {
layer.handle_request(req, res, next);
}
}
...
}
Layer.prototype.handle_error = function handle_error(error, req, res, next) {
var fn = this.handle;
if (fn.length !== 4) {
// not a standard error handler
return next(error);
}
try {
fn(error, req, res, next);
} catch (err) {
next(err);
}
};
Layer.prototype.handle_request = function handle(req, res, next) {
var fn = this.handle;
if (fn.length > 3) {
// not a standard request handler
return next();
}
try {
fn(req, res, next);
} catch (err) {
next(err);
}
};
从上面的 handle 可以看出, 通过 next 的嵌套调用实现中间件的调用
即调用 next 就会调用下一个中间件, 但相比 Koa 它并不支持 Promise
并且使用 try catch 捕捉了错误, 通过 next 进行传播, 所以错误中间件必须在最后注册
简化版本
const stack = [];
function use(fn) {
stack.push(new Layer(fn));
}
function handle() {
let idx = 0;
next()
function next(err) {
if (idx >= stack.length) {
return;
}
const layer = stack[idx++]
if (err) {
layer.handle_error(err, next)
} else {
layer.handle_request(next)
}
}
}
function Layer(fn) {
this.handle = fn
}
Layer.prototype.handle_error = function (err, next) {
const fn = this.handle
if (fn.length !== 2) return next(err) // 跳过不是错误处理的中间件
try {
fn(err, next)
} catch (err) {
next(err)
}
}
Layer.prototype.handle_request = function (next) {
const fn = this.handle
if (fn.length > 1) return next() // 跳过错误处理的中间件
try {
fn(next)
} catch (err) {
next(err)
}
}
use((next) => {
console.log(1);
next();
console.log(6);
})
use((next) => {
console.log(2);
next();
console.log(5);
})
use((next) => {
console.log(3);
next();
console.log(4);
})
use((next) => {
throw new Error("some err");
})
use((err, next) => {
console.log(err.message)
})
handle()
/**
* 输出
* 1
* 2
* 3
* some err
* 4
* 5
* 6
**/
结论
- 中间件代码中必须调用 next() 方法, 后续中间件才会执行
- 中断剩余中间件代码只要不调用 next() 方法即可
- 普通中间件参数不能大于3个, 错误处理中间件参数只能是4个, 否则将不会被调用
- 错误处理中间件必须要排在普通中间件的后面
- 中间件不支持 Promise
- 中间件调用 next() 方法之后, 会先执行后面的中间件最后再执行当前中间件的剩余代码, 即使是在中间件抛出错误时也一样