在下面的测试代码中,检查req.query
以查看其值是否为name=cat。 如果不是这样next()
则会触发下一件中间件。 这是在next()
后不包含return
语句即可完成的,并且按预期工作。
app.get('/test', (req, res, next) => {
if (req.query.name != 'cat') {
next();
}
res.send('it was cat');
});
app.get('/test', (req, res) => {
res.send('it was not cat');
});
但是,当我在第二个中间件中将res.send
更改为res.sendFile
时,行为完全不同。
app.get('/test', (req, res) => {
res.sendFile(__dirname + '/public/index.html');
});
更改后res.send('it was cat');
在第一个中间件中每次都会触发,无论名称的值如何。 此外,第二个中间件永远不会触发。
这可以通过在第一个中间件中next()
后添加一个return
来轻松修复。 行为再次变得可预测。
app.get('/test', (req, res, next) => {
if (req.query.name != 'cat') {
next();
return;
}
res.send('it was cat');
});
app.get('/test', (req, res) => {
res.sendFile(__dirname + '/public/index.html');
});
为什么在有和没有return
语句的情况下会发生这种情况? 当我使用res.sendFile
时需要return
,但当我使用res.send
时不需要。 我确定我错过了一些明显的东西,但我不明白模式。
这是在
next()
后不包含return
语句的情况下完成的,并且它按预期工作。
不,它看起来按预期工作,但事实并非如此。例如,如果您检索不带查询参数的/test
,或者检索不等于name=cat
的查询参数,Express 将记录错误:
Error: Can't set headers after they are sent.
我无法重现您的第二个示例;对我来说,它总是返回"它是猫"(在没有return
的示例中(。
任何 Express 处理程序/中间件的共同规则是:它应该结束响应本身(通过调用res.end
、res.send
、res.render
、res.sendFile
等(或使用next
传递请求。在您的情况下,如果没有return
,您正在同时执行这两项操作。实际上,其结果将是不确定的。
当您的侦听器执行res.send('it was not cat')
请求的整个处理时,不会撞到蹦床。next()
调用在res.send('it was not cat')
完成之前永远不会完成。 因此,当到达send('it was the cat')
的代码时,响应已经发送。
但是,当侦听器执行res.sendFile()
响应的处理是异步的。 该文件必须读入内存,这是节点中的非阻塞 I/O。 该 I/O 的回调被放入事件队列中,然后继续处理。 因此,next()
调用返回,然后执行send('it was the cat')
。 所有这些都是在文件读入内存之前。 稍后,当读取文件时,代码会尽职尽责地尝试发送响应,但为时已晚。